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

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

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

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

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

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

<a n:href="Product:show">подробнее</a>

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

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

<a n:href="Product:show $product->id, $product->slug">подробнее</a>

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

<a n:href="Product:show $product->id, lang: en">подробнее</a>

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

Если параметры хранятся в массиве, их можно расширить с помощью оператора (expand) (что-то вроде оператора ... в PHP, но работает с ассоциативными массивами):

{var $args = [$product->id, lang => en]}
<a n:href="Product:show (expand) $args">подробнее</a>

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

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

URL: {link Homepage:default}

В коде

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

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

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

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

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

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

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

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

Поэтому основной формой является Presenter:action:

<a n:href="Homepage:default">главная страница</a>

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

<a n:href="default">главная страница</a>

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

<a n:href="Homepage:">главная страница</a>

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

<a n:href="Shop:Product:show">ссылка на Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">ссылка на Admin:Product:show</a>

Особым случаем является ссылка на себя. Здесь мы напишем this в качестве цели.

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

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

<a n:href="Homepage:#main">ссылка на Homepage:default и фрагмент #main</a>

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

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

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

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

<a n:href="this">обновить</a>

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

Конечно, можно указать параметры напрямую:

<a n:href="this refresh: 1">обновить</a>

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

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

{if !$presenter->isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">Войти</a>
{/if}

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

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

<a n:class="$presenter->isLinkCurrent() ? active" n:href="Product:detail">...</a>

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

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

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

Целью ссылки может быть не только презентер и действие, но и сигнал (они вызывают метод 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">сигнал, не действие</a>

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

<a href="{plink Homepage:default}">главная страница</a>

or in the code

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

Может случиться так, что мы создадим некорректную ссылку — либо потому, что она ссылается на несуществующий презентер, либо потому, что она передает больше параметров, чем целевой метод получает в своей сигнатуре, либо когда не может быть сгенерирован URL для целевого действия. Что делать с недействительными ссылками, определяется статической переменной Presenter::$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.