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

версия: 4.0