Создание 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.