Nette Documentation Preview

syntax
Создание URL-ссылок
*******************

<div class=perex>

Создавать ссылки в Nette так же просто, как показать пальцем. Достаточно прицелиться, а всю работу сделает фреймворк. Мы покажем:

- как создавать ссылки в шаблонах и не только
- как отличить ссылку на текущую страницу
- что делать с некорректными ссылками

</div>


Благодаря [двунаправленной маршрутизации |routing] вам никогда не придётся жёстко прописывать URL вашего приложения в шаблонах или коде, ведь они могут позже измениться или оказаться сложными в сборке. В ссылке достаточно указать презентер и действие, передать параметры, а URL фреймворк породит сам. По сути это очень похоже на вызов функции. Вам понравится.


В шаблоне презентера
====================

Чаще всего мы создаём ссылки в шаблонах, и отличный помощник здесь - атрибут `n:href`:

```latte
<a n:href="Product:show">detail</a>
```

Обратите внимание, что вместо HTML-атрибута `href` мы использовали [n:атрибут |latte:syntax#n:атрибуты] `n:href`. Его значением служит не URL, как было бы у атрибута `href`, а имя презентера и действия.

Щелчок по ссылке, попросту говоря, похож на вызов метода `ProductPresenter::renderShow()`. А если у него есть параметры в сигнатуре, мы можем вызвать его с аргументами:

```latte
<a n:href="Product:show $product->id, $product->slug">product detail</a>
```

Можно передавать и именованные параметры. Следующая ссылка передаёт параметр `lang` со значением `en`:

```latte
<a n:href="Product:show $product->id, lang: en">product detail</a>
```

Если у метода `ProductPresenter::renderShow()` нет `$lang` в сигнатуре, он может получить значение параметра через `$lang = $this->getParameter('lang')` или из [свойства |presenters#Параметры запроса].

Если параметры хранятся в массиве, их можно развернуть оператором `...`:

```latte
{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">product detail</a>
```

Так называемые [постоянные параметры |presenters#Постоянные параметры] тоже передаются в ссылках автоматически.

Атрибут `n:href` очень удобен для HTML-тегов `<a>`. Если мы хотим вывести ссылку в другом месте, например в тексте, мы используем `{link}`:

```latte
URL is: {link Home:default}
```


В коде
======

Для создания ссылки в презентере служит метод `link()`:

```php
$url = $this->link('Product:show', $product->id);
```

Параметры можно передать и массивом, где можно указать и именованные параметры:

```php
$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);
```

Ссылки можно создавать и без презентера, с помощью [#LinkGenerator] и его метода `link()`.

Иногда нужно создать ссылку сейчас, а сам URL породить только позже. Для этого есть метод `lazyLink()`, возвращающий объект `Nette\Application\UI\Link`. Преимущество в том, что этот объект можно передать дальше, например в шаблон, и до его отрисовки ещё изменить его параметры методом `setParameter()`. Сам URL собирается только при преобразовании объекта в строку:

```php
$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // URL порождается только здесь
```


Ссылки на презентер
===================

Если целью ссылки служит презентер и действие, синтаксис такой:

```
[//] [[[[:]module:]presenter:]action | this] [#fragment]
```

Этот формат поддерживают все теги Latte и все методы презентера, работающие со ссылками, то есть `n:href`, `{link}`, `{plink}`, `link()`, `lazyLink()`, `isLinkCurrent()`, `redirect()`, `redirectPermanent()`, `forward()`, `canonicalize()`, а также [#LinkGenerator]. Так что, хотя в примерах используется `n:href`, на его месте могла бы быть любая из этих функций.

Основная форма, таким образом, - `Презентер:действие`:

```latte
<a n:href="Home:default">home page</a>
```

Если мы ссылаемся на действие текущего презентера, его имя можно опустить:

```latte
<a n:href="default">home page</a>
```

Если целевое действие - `default`, его можно опустить, но двоеточие должно остаться:

```latte
<a n:href="Home:">home page</a>
```

Ссылки могут указывать и на другие [модули |directory-structure#Презентеры и шаблоны]. Здесь ссылки различаются как относительные к вложенному подмодулю или абсолютные. Принцип аналогичен путям на диске, только вместо слешей используются двоеточия. Если считать, что текущий презентер входит в модуль `Front`, мы написали бы:

```latte
<a n:href="Shop:Product:show">link to Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">link to Admin:Product:show</a>
```

Особый случай - ссылка [на саму себя |#Ссылка на текущую страницу], где мы указываем целью `this`.

```latte
<a n:href="this">refresh</a>
```

Мы можем сослаться на определённую часть страницы через так называемый фрагмент после знака решётки `#`:

```latte
<a n:href="Home:#main">link to Home:default and fragment #main</a>
```

.{data-version:3.3.0}
Фрагмент можно задать и динамически, аргументом с ключом `#`. Его значение автоматически кодируется и имеет приоритет над фрагментом, указанным в цели:

```php
$this->link('Home:default', ['#' => $fragment]);
```


Абсолютные пути
===============

Ссылки, порождаемые через `link()` или `n:href`, всегда являются абсолютными путями (то есть начинаются с `/`), но не абсолютными URL с протоколом и доменом вроде `https://domain`.

Чтобы породить абсолютный URL, добавьте в начале два слеша (например, `n:href="//Home:"`). Как вариант, вы можете переключить презентер на порождение только абсолютных ссылок, задав `$this->absoluteUrls = true`.

В шаблоне для преобразования относительного пути в абсолютный можно использовать и фильтр `|absoluteUrl`.


Ссылка на текущую страницу
==========================

Цель `this` создаёт ссылку на текущую страницу:

```latte
<a n:href="this">refresh</a>
```

При этом передаются все параметры, указанные в сигнатуре метода `action<Action>()` или `render<View>()` (если `action<Action>()` не определён). Так что если мы находимся на странице `Product:show` с `id: 123`, ссылка на `this` передаст и этот параметр.

Разумеется, параметры можно указать и напрямую:

```latte
<a n:href="this refresh: 1">refresh</a>
```

Функция `isLinkCurrent()` проверяет, совпадает ли цель ссылки с текущей страницей. Это можно использовать, например, в шаблоне, чтобы выделять ссылки и подобное.

Параметры те же, что и у метода `link()`, но вместо конкретного действия можно использовать подстановочный знак `*`, означающий любое действие данного презентера.

```latte
{if !isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">Login</a>
{/if}

<li n:class="isLinkCurrent('Product:*') ? active">
	<a n:href="Product:">...</a>
</li>
```

В сочетании с `n:href` в одном элементе можно использовать сокращённую форму:

```latte
<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>
```

Подстановочный знак `*` можно использовать только вместо действия, но не вместо презентера.

Чтобы определить, находимся ли мы в определённом модуле или его подмодуле, используйте метод `isModuleCurrent(moduleName)`.

```latte
<li n:class="isModuleCurrent('Forum:Users') ? active">
	<a n:href="Product:">...</a>
</li>
```


Изменение базы ссылок .{data-version:3.2.7}
===========================================

По умолчанию относительные ссылки отсчитываются от текущего презентера. Это можно изменить с помощью `{linkBase}`:

```latte
{linkBase Admin:Dashboard}
<a n:href="Product:show">product detail</a>
```

Ссылка приведёт на `Admin:Dashboard:Product:show`. Затрагиваются только относительные ссылки: абсолютные, начинающиеся с двоеточия, и ссылки на текущий презентер (`this`, `show`) остаются без изменений.

`{linkBase}` действует на весь шаблон и особенно полезен в шаблонах макетов, где обеспечивает единообразные ссылки независимо от вызывающего презентера.
Тег должен стоять в начале шаблона, иначе он выбросит `CompileException`.


Ссылки на сигнал
================

Целью ссылки может быть не только презентер и действие, но и [сигнал |components#Сигнал] (он вызывает метод `handle<Signal>()`). Тогда синтаксис такой:

```
[//] [sub-component:]signal! [#fragment]
```

Сигнал, таким образом, отличается восклицательным знаком:

```latte
<a n:href="click!">signal</a>
```

Можно создать и ссылку на сигнал подкомпонента (или под-подкомпонента):

```latte
<a n:href="componentName:click!">signal</a>
```


Ссылки в компоненте
===================

Поскольку [компоненты|components] - самостоятельные переиспользуемые единицы, которые не должны быть связаны с окружающими презентерами, ссылки здесь работают немного иначе. Атрибут Latte `n:href` и тег `{link}`, а также методы компонента вроде `link()` и другие **всегда считают целью ссылки имя сигнала**. Поэтому восклицательный знак даже не нужен:

```latte
<a n:href="click">signal, not an action</a>
```

Если бы мы хотели сослаться в шаблоне компонента на презентеры, мы использовали бы тег `{plink}`:

```latte
<a href={plink Home:default}>home</a>
```

или в коде

```php
$this->getPresenter()->link('Home:default')
```


Псевдонимы .{data-version:3.2.3}
================================

Иногда бывает полезно назначить паре "презентер:действие" легко запоминающийся псевдоним. Например, назвать главную страницу `Front:Home:default` просто `home`, а `Admin:Dashboard:default` - `admin`.

Псевдонимы задаются в [конфигурации|configuration] под ключом `application › aliases`:

```neon
application:
    aliases:
        home: Front:Home:default
        admin: Admin:Dashboard:default
        sign: Front:Sign:in
```

В ссылках они затем записываются со знаком собаки, например:

```latte
<a n:href="@admin">administration</a>
```

Они поддерживаются и во всех методах, работающих со ссылками, таких как `redirect()` и подобные.


Некорректные ссылки
===================

Может случиться, что мы создадим некорректную ссылку: либо потому, что она ведёт на несуществующий презентер, либо потому, что передаёт больше параметров, чем принимает в сигнатуре целевой метод, либо когда URL для целевого действия породить нельзя. Как обходиться с некорректными ссылками, задаётся в презентере через `$this->invalidLinkMode`. Он может принимать сочетание этих значений (констант):

- `Presenter::InvalidLinkSilent` - молчаливый режим, возвращает в качестве URL символ #
- `Presenter::InvalidLinkWarning` - выдаётся предупреждение E_USER_WARNING, которое в производственном режиме попадёт в лог, но не прервёт выполнение скрипта
- `Presenter::InvalidLinkTextual` - наглядное предупреждение, выводит ошибку прямо в ссылку
- `Presenter::InvalidLinkException` - выбрасывает InvalidLinkException

По умолчанию задано `InvalidLinkWarning` в производственном режиме и `InvalidLinkWarning | InvalidLinkTextual` в режиме разработки. `InvalidLinkWarning` в производственной среде не прерывает скрипт, но предупреждение попадает в лог. В среде разработки его перехватывает [Tracy |tracy:] и показывает синий экран. `InvalidLinkTextual` работает так, что возвращает как URL сообщение об ошибке, начинающееся с символов `#error:`. Чтобы такие ссылки бросались в глаза с первого взгляда, добавьте в свой CSS:

```css
a[href^="#error:"] {
	background: red;
	color: white;
}
```

Если мы не хотим, чтобы в среде разработки возникали предупреждения, мы можем подавить их прямо в [конфигурации|configuration].

```neon
application:
	silentLinks: true
```


LinkGenerator
=============

Как создавать ссылки с таким же удобством, как методом `link()`, но без презентера? Для этого и существует [api:Nette\Application\LinkGenerator].

LinkGenerator - сервис, который вы можете получить через конструктор и затем создавать ссылки его методом `link()`.

По сравнению с презентерами есть отличие. LinkGenerator создаёт все ссылки сразу как абсолютные URL. Кроме того, "текущего презентера" нет, поэтому нельзя указать целью только имя действия `link('default')` или использовать относительные пути к модулям.

Некорректные ссылки всегда выбрасывают `Nette\Application\UI\InvalidLinkException`.

Создание URL-ссылок

Создавать ссылки в Nette так же просто, как показать пальцем. Достаточно прицелиться, а всю работу сделает фреймворк. Мы покажем:

  • как создавать ссылки в шаблонах и не только
  • как отличить ссылку на текущую страницу
  • что делать с некорректными ссылками

Благодаря двунаправленной маршрутизации вам никогда не придётся жёстко прописывать URL вашего приложения в шаблонах или коде, ведь они могут позже измениться или оказаться сложными в сборке. В ссылке достаточно указать презентер и действие, передать параметры, а URL фреймворк породит сам. По сути это очень похоже на вызов функции. Вам понравится.

В шаблоне презентера

Чаще всего мы создаём ссылки в шаблонах, и отличный помощник здесь – атрибут n:href:

<a n:href="Product:show">detail</a>

Обратите внимание, что вместо HTML-атрибута href мы использовали n:атрибут n:href. Его значением служит не URL, как было бы у атрибута href, а имя презентера и действия.

Щелчок по ссылке, попросту говоря, похож на вызов метода ProductPresenter::renderShow(). А если у него есть параметры в сигнатуре, мы можем вызвать его с аргументами:

<a n:href="Product:show $product->id, $product->slug">product detail</a>

Можно передавать и именованные параметры. Следующая ссылка передаёт параметр lang со значением en:

<a n:href="Product:show $product->id, lang: en">product detail</a>

Если у метода ProductPresenter::renderShow() нет $lang в сигнатуре, он может получить значение параметра через $lang = $this->getParameter('lang') или из свойства.

Если параметры хранятся в массиве, их можно развернуть оператором ...:

{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">product detail</a>

Так называемые постоянные параметры тоже передаются в ссылках автоматически.

Атрибут n:href очень удобен для HTML-тегов <a>. Если мы хотим вывести ссылку в другом месте, например в тексте, мы используем {link}:

URL is: {link Home:default}

В коде

Для создания ссылки в презентере служит метод link():

$url = $this->link('Product:show', $product->id);

Параметры можно передать и массивом, где можно указать и именованные параметры:

$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);

Ссылки можно создавать и без презентера, с помощью LinkGenerator и его метода link().

Иногда нужно создать ссылку сейчас, а сам URL породить только позже. Для этого есть метод lazyLink(), возвращающий объект Nette\Application\UI\Link. Преимущество в том, что этот объект можно передать дальше, например в шаблон, и до его отрисовки ещё изменить его параметры методом setParameter(). Сам URL собирается только при преобразовании объекта в строку:

$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // URL порождается только здесь

Ссылки на презентер

Если целью ссылки служит презентер и действие, синтаксис такой:

[//] [[[[:]module:]presenter:]action | this] [#fragment]

Этот формат поддерживают все теги Latte и все методы презентера, работающие со ссылками, то есть n:href, {link}, {plink}, link(), lazyLink(), isLinkCurrent(), redirect(), redirectPermanent(), forward(), canonicalize(), а также LinkGenerator. Так что, хотя в примерах используется n:href, на его месте могла бы быть любая из этих функций.

Основная форма, таким образом, – Презентер:действие:

<a n:href="Home:default">home page</a>

Если мы ссылаемся на действие текущего презентера, его имя можно опустить:

<a n:href="default">home page</a>

Если целевое действие – default, его можно опустить, но двоеточие должно остаться:

<a n:href="Home:">home page</a>

Ссылки могут указывать и на другие модули. Здесь ссылки различаются как относительные к вложенному подмодулю или абсолютные. Принцип аналогичен путям на диске, только вместо слешей используются двоеточия. Если считать, что текущий презентер входит в модуль Front, мы написали бы:

<a n:href="Shop:Product:show">link to Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">link to Admin:Product:show</a>

Особый случай – ссылка на саму себя, где мы указываем целью this.

<a n:href="this">refresh</a>

Мы можем сослаться на определённую часть страницы через так называемый фрагмент после знака решётки #:

<a n:href="Home:#main">link to Home:default and fragment #main</a>

Фрагмент можно задать и динамически, аргументом с ключом #. Его значение автоматически кодируется и имеет приоритет над фрагментом, указанным в цели:

$this->link('Home:default', ['#' => $fragment]);

Абсолютные пути

Ссылки, порождаемые через link() или n:href, всегда являются абсолютными путями (то есть начинаются с /), но не абсолютными URL с протоколом и доменом вроде https://domain.

Чтобы породить абсолютный URL, добавьте в начале два слеша (например, n:href="//Home:"). Как вариант, вы можете переключить презентер на порождение только абсолютных ссылок, задав $this->absoluteUrls = true.

В шаблоне для преобразования относительного пути в абсолютный можно использовать и фильтр |absoluteUrl.

Ссылка на текущую страницу

Цель this создаёт ссылку на текущую страницу:

<a n:href="this">refresh</a>

При этом передаются все параметры, указанные в сигнатуре метода action<Action>() или render<View>() (если action<Action>() не определён). Так что если мы находимся на странице Product:show с id: 123, ссылка на this передаст и этот параметр.

Разумеется, параметры можно указать и напрямую:

<a n:href="this refresh: 1">refresh</a>

Функция isLinkCurrent() проверяет, совпадает ли цель ссылки с текущей страницей. Это можно использовать, например, в шаблоне, чтобы выделять ссылки и подобное.

Параметры те же, что и у метода link(), но вместо конкретного действия можно использовать подстановочный знак *, означающий любое действие данного презентера.

{if !isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">Login</a>
{/if}

<li n:class="isLinkCurrent('Product:*') ? active">
	<a n:href="Product:">...</a>
</li>

В сочетании с n:href в одном элементе можно использовать сокращённую форму:

<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>

Подстановочный знак * можно использовать только вместо действия, но не вместо презентера.

Чтобы определить, находимся ли мы в определённом модуле или его подмодуле, используйте метод isModuleCurrent(moduleName).

<li n:class="isModuleCurrent('Forum:Users') ? active">
	<a n:href="Product:">...</a>
</li>

Изменение базы ссылок

По умолчанию относительные ссылки отсчитываются от текущего презентера. Это можно изменить с помощью {linkBase}:

{linkBase Admin:Dashboard}
<a n:href="Product:show">product detail</a>

Ссылка приведёт на Admin:Dashboard:Product:show. Затрагиваются только относительные ссылки: абсолютные, начинающиеся с двоеточия, и ссылки на текущий презентер (this, show) остаются без изменений.

{linkBase} действует на весь шаблон и особенно полезен в шаблонах макетов, где обеспечивает единообразные ссылки независимо от вызывающего презентера. Тег должен стоять в начале шаблона, иначе он выбросит CompileException.

Ссылки на сигнал

Целью ссылки может быть не только презентер и действие, но и сигнал (он вызывает метод handle<Signal>()). Тогда синтаксис такой:

[//] [sub-component:]signal! [#fragment]

Сигнал, таким образом, отличается восклицательным знаком:

<a n:href="click!">signal</a>

Можно создать и ссылку на сигнал подкомпонента (или под-подкомпонента):

<a n:href="componentName:click!">signal</a>

Ссылки в компоненте

Поскольку компоненты – самостоятельные переиспользуемые единицы, которые не должны быть связаны с окружающими презентерами, ссылки здесь работают немного иначе. Атрибут Latte n:href и тег {link}, а также методы компонента вроде link() и другие всегда считают целью ссылки имя сигнала. Поэтому восклицательный знак даже не нужен:

<a n:href="click">signal, not an action</a>

Если бы мы хотели сослаться в шаблоне компонента на презентеры, мы использовали бы тег {plink}:

<a href={plink Home:default}>home</a>

или в коде

$this->getPresenter()->link('Home:default')

Псевдонимы

Иногда бывает полезно назначить паре „презентер:действие“ легко запоминающийся псевдоним. Например, назвать главную страницу Front:Home:default просто home, а Admin:Dashboard:default – admin.

Псевдонимы задаются в конфигурации под ключом application › aliases:

application:
    aliases:
        home: Front:Home:default
        admin: Admin:Dashboard:default
        sign: Front:Sign:in

В ссылках они затем записываются со знаком собаки, например:

<a n:href="@admin">administration</a>

Они поддерживаются и во всех методах, работающих со ссылками, таких как redirect() и подобные.

Некорректные ссылки

Может случиться, что мы создадим некорректную ссылку: либо потому, что она ведёт на несуществующий презентер, либо потому, что передаёт больше параметров, чем принимает в сигнатуре целевой метод, либо когда URL для целевого действия породить нельзя. Как обходиться с некорректными ссылками, задаётся в презентере через $this->invalidLinkMode. Он может принимать сочетание этих значений (констант):

  • Presenter::InvalidLinkSilent – молчаливый режим, возвращает в качестве URL символ #
  • Presenter::InvalidLinkWarning – выдаётся предупреждение E_USER_WARNING, которое в производственном режиме попадёт в лог, но не прервёт выполнение скрипта
  • Presenter::InvalidLinkTextual – наглядное предупреждение, выводит ошибку прямо в ссылку
  • Presenter::InvalidLinkException – выбрасывает InvalidLinkException

По умолчанию задано InvalidLinkWarning в производственном режиме и InvalidLinkWarning | InvalidLinkTextual в режиме разработки. InvalidLinkWarning в производственной среде не прерывает скрипт, но предупреждение попадает в лог. В среде разработки его перехватывает Tracy и показывает синий экран. InvalidLinkTextual работает так, что возвращает как URL сообщение об ошибке, начинающееся с символов #error:. Чтобы такие ссылки бросались в глаза с первого взгляда, добавьте в свой CSS:

a[href^="#error:"] {
	background: red;
	color: white;
}

Если мы не хотим, чтобы в среде разработки возникали предупреждения, мы можем подавить их прямо в конфигурации.

application:
	silentLinks: true

LinkGenerator

Как создавать ссылки с таким же удобством, как методом link(), но без презентера? Для этого и существует Nette\Application\LinkGenerator.

LinkGenerator – сервис, который вы можете получить через конструктор и затем создавать ссылки его методом link().

По сравнению с презентерами есть отличие. LinkGenerator создаёт все ссылки сразу как абсолютные URL. Кроме того, „текущего презентера“ нет, поэтому нельзя указать целью только имя действия link('default') или использовать относительные пути к модулям.

Некорректные ссылки всегда выбрасывают Nette\Application\UI\InvalidLinkException.