Создание URL-ссылок
Создавать ссылки в Nette просто, как указывать пальцем. Достаточно просто направить, и фреймворк уже сделает всю работу за вас. Мы покажем:
- как создавать ссылки в шаблонах и в других местах
- как отличить ссылку на текущую страницу
- что делать с недействительными ссылками
Благодаря двусторонней маршрутизации вам никогда не придется жестко прописывать URL-адреса вашего приложения в шаблонах или коде, которые могут позже измениться, или сложно их составлять. В ссылке достаточно указать презентер и действие, передать возможные параметры, и фреймворк сам сгенерирует URL. На самом деле, это очень похоже на вызов функции. Вам это понравится.
В шаблоне презентера
Чаще всего мы создаем ссылки в шаблонах, и отличным помощником
является атрибут n:href
:
<a n:href="Product:show">деталь</a>
Обратите внимание, что вместо HTML-атрибута href
мы использовали n:атрибут n:href
. Его значением
является не URL, как это было бы в случае атрибута href
, а имя
презентера и действия.
Нажатие на ссылку, упрощенно говоря, похоже на вызов метода
ProductPresenter::renderShow()
. И если у него в сигнатуре есть параметры, мы
можем вызвать его с аргументами:
<a n:href="Product:show $product->id, $product->slug">деталь продукта</a>
Можно передавать и именованные параметры. Следующая ссылка передает
параметр lang
со значением cs
:
<a n:href="Product:show $product->id, lang: cs">деталь продукта</a>
Если метод ProductPresenter::renderShow()
не имеет $lang
в своей
сигнатуре, он может получить значение параметра с помощью
$lang = $this->getParameter('lang')
или из свойства.
Если параметры хранятся в массиве, их можно развернуть с помощью
оператора ...
(в Latte 2.x оператором (expand)
):
{var $args = [$product->id, lang => cs]}
<a n:href="Product:show ...$args">деталь продукта</a>
В ссылках также автоматически передаются так называемые персистентные параметры.
Атрибут n:href
очень удобен для HTML-тегов <a>
. Если мы
хотим вывести ссылку в другом месте, например, в тексте, используем
{link}
:
Адрес: {link Home: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="Home:default">главная страница</a>
Если мы ссылаемся на действие текущего презентера, мы можем опустить его имя:
<a n:href="default">главная страница</a>
Если целью является действие default
, мы можем его опустить, но
двоеточие должно остаться:
<a n:href="Home:">главная страница</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">обновить</a>
Мы можем ссылаться на определенную часть страницы через так
называемый фрагмент после символа решетки #
:
<a n:href="Home:#main">ссылка на Home:default и фрагмент #main</a>
Абсолютные пути
Ссылки, генерируемые с помощью link()
или n:href
, всегда
являются абсолютными путями (т. е. начинаются с символа /
), но не
абсолютными URL с протоколом и доменом, как https://domain
.
Для генерации абсолютного URL добавьте в начало два слеша (например,
n:href="//Home:"
). Или можно переключить презентер, чтобы он
генерировал только абсолютные ссылки, установив
$this->absoluteUrls = true
.
Ссылка на текущую страницу
Цель this
создаст ссылку на текущую страницу:
<a n:href="this">обновить</a>
При этом передаются все параметры, указанные в сигнатуре метода
action<Action>()
или render<View>()
, если action<Action>()
не
определена. Так что если мы находимся на странице Product:show
и
id: 123
, ссылка на this
передаст и этот параметр.
Конечно, можно указать параметры напрямую:
<a n:href="this refresh: 1">обновить</a>
Функция isLinkCurrent()
проверяет, совпадает ли цель ссылки с текущей
страницей. Это можно использовать, например, в шаблоне для выделения
ссылок и т. п.
Параметры такие же, как у метода link()
, но дополнительно можно
вместо конкретного действия указать подстановочный знак *
,
который означает любое действие данного презентера.
{if !isLinkCurrent('Admin:login')}
<a n:href="Admin: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>
Ссылки на сигнал
Целью ссылки может быть не только презентер и действие, но и сигнал (вызывают метод
handle<Signal>()
). Тогда синтаксис следующий:
[//] [sub-component:]signal! [#fragment]
Сигнал отличает восклицательный знак:
<a n:href="click!">сигнал</a>
Можно создать и ссылку на сигнал подкомпонента (или под-подкомпонента):
<a n:href="componentName:click!">сигнал</a>
Ссылки в компоненте
Поскольку компоненты являются
отдельными повторно используемыми единицами, которые не должны иметь
никаких связей с окружающими презентерами, ссылки здесь работают
немного иначе. Атрибут Latte n:href
и тег {link}
, а также методы
компонента, такие как link()
и другие, считают цель ссылки всегда
именем сигнала. Поэтому не нужно даже указывать
восклицательный знак:
<a n:href="click">сигнал, а не действие</a>
Если бы мы хотели в шаблоне компонента ссылаться на презентеры, мы бы
использовали для этого тег {plink}
:
<a href={plink Home:default}>введение</a>
или в коде
$this->getPresenter()->link('Home:default')
Псевдонимы
Иногда может быть полезно присвоить паре Presenter:action легко
запоминающийся псевдоним. Например, главную страницу 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">администрирование</a>
Они также поддерживаются во всех методах, работающих со ссылками,
таких как redirect()
и подобных.
Недействительные ссылки
Может случиться, что мы создадим недействительную ссылку — либо
потому, что она ведет на несуществующий презентер, либо потому, что
передает больше параметров, чем принимает целевой метод в своей
сигнатуре, либо когда для целевого действия невозможно сгенерировать
URL. Как обращаться с недействительными ссылками, определяет
статическая переменная Presenter::$invalidLinkMode
. Она может принимать
комбинацию следующих значений (констант):
Presenter::InvalidLinkSilent
— тихий режим, в качестве URL возвращается символ #Presenter::InvalidLinkWarning
— выбрасывается предупреждение E_USER_WARNING, которое в режиме production будет залогировано, но не вызовет прерывания выполнения скриптаPresenter::InvalidLinkTextual
— визуальное предупреждение, выводит ошибку прямо в ссылкуPresenter::InvalidLinkException
— выбрасывается исключение InvalidLinkException
Настройка по умолчанию — InvalidLinkWarning
в режиме production и
InvalidLinkWarning | InvalidLinkTextual
в режиме разработки. InvalidLinkWarning
в
production-среде не вызывает прерывания скрипта, но предупреждение будет
залогировано. В среде разработки его перехватывает 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
.