Creating URL Links

Creating links in Nette is as easy as pointing a finger. Just point and the framework will do all the work for you. We will show:

  • how to create links in templates and elsewhere
  • how to distinguish a link to the current page
  • what about invalid links

Thanks to bidirectional routing, you'll never have to hardcode application's URLs in the templates or code, which may change later or be complicated to compose. Just specify the presenter and the action in the link, pass any parameters and the framework will generate the URL itself. In fact, it's very similar to calling a function. You will like it.

In the Presenter Template

Most often we create links in templates and a great helper is the attribute n:href:

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

Note, that instead of the HTML attribute href we've used n:attribute n:href. Its value isn't a URL, as you are used to with the href attribute, but name of the presenter and the action.

Clicking on a link is, simply said, something like calling a method ProductPresenter::renderShow(). And if it has parameters in its signature, we can call it with arguments:

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

Unlike PHP, it is possible to pass named parameters. The following link passes parameter lang with value en:

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

If method ProductPresenter::renderShow() does not have $lang in its signature, it can read the value of the parameter using $lang = $this->getParameter('lang').

If the parameters are stored in an array, they can be expanded with the (expand) operator (something like the ... operator in PHP, but works with associative arrays):

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

Attribute n:href is very handy for HTML tags <a>. If we want to print the link elsewhere, for example in the text, we use {link}:

URL is: {link Homepage:default}

In the Code

The method link() is used to create a link in the presenter:

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

Parameters can also be passed as an array where named parameters can also be specified:

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

Links can be created without a presenter too, using the LinkGenerator and its method link().

If the target of the link is presenter and action, it has this syntax:

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

The format is supported by all Latte tags and all presenter methods that work with links, ie n:href, {link}, {plink}, link(), lazyLink(), isLinkCurrent(), redirect(), redirectPermanent(), forward(), canonicalize() and also LinkGenerator. So even if n:href is used in the examples, there could be any of the functions.

The basic form is therefore Presenter:action:

<a n:href="Homepage:default">homepage</a>

If we link to the action of the current presenter, we can omit its name:

<a n:href="default">homepage</a>

If the action is default, we can omit it, but the colon must remain:

<a n:href="Homepage:">homepage</a>

Links may also point to other modules. Here, the links are distinguished into relative to the submodules, or absolute. The principle is analogous to disk paths, only instead of slashes there are colons. Let's assume that the actual presenter is part of module Front, then we will write:

<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>

A special case is linking to itself. Here we'll write this as the target.

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

We can link to a certain part of the HTML page via a so-called fragment after the # hash symbol:

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

The generated link is in the form of a relative URL path. When you want to generate an absolute link including the domain, add two slashes at the beginning (eg n:href="//Homepage:"). Or you can switch the presenter to generate only absolute references.

The target of the link may not only be the presenter and action, but also the signal (they call the method handle<Signal>()). The syntax is as follows:

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

The signal is therefore distinguishes by exclamation mark:

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

You can also create a link to the signal of the subcomponent (or sub-subcomponent):

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

Because components are separate reusable units that should have no relations to surrounding presenters, the links work a little differently. The Latte attribute n:href and tag {link} and component methods such as link() and others always consider the target as the signal name. Therefore it is not necessary to use an exclamation mark:

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

If we want to link to presenters in the component template, we use the tag {plink}:

<a href="{plink Homepage:default}">homepage</a>

or in the code

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

The presenter's method isLinkCurrent() determines if the target of the link is the same as the current page. This can be used, for example, in a template to differentiate links, etc.

The parameters are the same as for the link() method, but it is also possible to use the wildcard * instead of a specific action, which means any action of the presenter.

{if !$presenter->isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">Přihlaste se</a>
{/if}

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

An abbreviated form can be used in combination with n:href in single element:

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

Wildcard character * replaces presenter's action only, not presenter itself. To find out if we are in a certain module or its submodule we can use $presenter->isModuleCurrent(moduleName) method.

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

It may happen that we create an invalid link – either because it refers to a non-existing presenter, or because it passes more parameters that the target method receives in its signature, or when there can't be a generated URL for the targeted action. What to do with invalid links is determined by the static variable Presenter::$invalidLinkMode. It can have one of these values (constants):

  • Presenter::INVALID_LINK_SILENT – silent mode, returns symbol # as URL
  • Presenter::INVALID_LINK_WARNING – E_USER_WARNING will be produced
  • Presenter::INVALID_LINK_TEXTUAL – visual warning, the error text is displayed in the link
  • Presenter::INVALID_LINK_EXCEPTION – InvalidLinkException will be thrown

The default setup in production mode is INVALID_LINK_WARNING and in development mode is INVALID_LINK_WARNING | INVALID_LINK_TEXTUAL. INVALID_LINK_WARNING doesn't kill the script in the production environment, but the warning will be logged. In the development environment, Tracy will intercept the warning and display the error bluescreen. If the INVALID_LINK_TEXTUAL is set, presenter and components return error message as URL which stars with #error:. To make such links visible, we can add a CSS rule to our stylesheet:

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

If we don't want warnings to be produced in the development environment we can turn on silent invalid link mode in the configuration.

application:
	silentLinks: true

LinkGenerator

How to create links with the method link() comfort, but without the presence of a presenter? That's why here is Nette\Application\LinkGenerator.

LinkGenerator is a service that you can have passed through the constructor and then create links using its method link().

There is a difference compared to presenters. LinkGenerator creates all links as absolute URLs. Furthermore, there is no “current presenter”, so it is not possible to specify only the name of the action link('default') or the relative paths to the modules.

Invalid links always throw Nette\Application\UI\InvalidLinkException.