Creación de enlaces URL

Crear enlaces en Nette es tan fácil como señalar con el dedo. Basta con apuntar y el framework hará todo el trabajo por usted. Veremos:

  • cómo crear enlaces en las plantillas y fuera de ellas
  • cómo distinguir un enlace a la página actual
  • qué hacer con los enlaces no válidos

Gracias al enrutamiento bidireccional nunca tendrá que escribir a fuego en las plantillas o en el código las URL de su aplicación, que quizá cambien más adelante o resulten complicadas de componer. En el enlace basta con indicar el presenter y la acción, pasar los parámetros que hagan falta, y el framework generará la URL por sí solo. En realidad es muy parecido a llamar a una función. Le va a gustar.

En la plantilla del presenter

Lo más habitual es crear los enlaces en las plantillas, y el atributo n:href es una gran ayuda:

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

Fíjese en que, en lugar del atributo HTML href, hemos usado el n:atributo n:href. Su valor no es una URL, como ocurriría con el atributo href, sino el nombre del presenter y la acción.

Pulsar un enlace es, dicho de forma sencilla, algo así como llamar al método ProductPresenter::renderShow(). Y si tiene parámetros en su firma, podemos llamarlo con argumentos:

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

También es posible pasar parámetros nombrados. El siguiente enlace pasa el parámetro lang con el valor en:

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

Si el método ProductPresenter::renderShow() no tiene $lang en su firma, puede obtener el valor del parámetro con $lang = $this->getParameter('lang') o desde una propiedad.

Si los parámetros están guardados en un array, se pueden expandir con el operador ...:

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

Los llamados parámetros persistentes también se pasan automáticamente en los enlaces.

El atributo n:href resulta muy práctico en las etiquetas HTML <a>. Si queremos imprimir el enlace en otro sitio, por ejemplo dentro de un texto, usamos {link}:

URL is: {link Home:default}

En el código

Para crear un enlace en el presenter se usa el método link():

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

Los parámetros también se pueden pasar como array, donde además se pueden indicar parámetros nombrados:

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

Los enlaces también se pueden crear sin presenter, con el LinkGenerator y su método link().

A veces necesita crear un enlace ahora pero generar la URL real más tarde. Para eso está el método lazyLink(), que devuelve un objeto Nette\Application\UI\Link. La ventaja es que puede pasar ese objeto de un sitio a otro, por ejemplo a una plantilla, y antes de que se renderice todavía puede ajustar sus parámetros con el método setParameter(). La URL propiamente dicha se compone solo cuando el objeto se convierte en cadena:

$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // la URL se genera solo aquí

Enlaces a un presenter

Si el destino del enlace es un presenter y una acción, la sintaxis es esta:

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

Este formato lo admiten todas las etiquetas de Latte y todos los métodos del presenter que trabajan con enlaces, es decir, n:href, {link}, {plink}, link(), lazyLink(), isLinkCurrent(), redirect(), redirectPermanent(), forward(), canonicalize() y también el LinkGenerator. Así que, aunque en los ejemplos se use n:href, ahí podría estar cualquiera de esas funciones.

La forma básica es, por tanto, Presenter:acción:

<a n:href="Home:default">home page</a>

Si enlazamos a una acción del presenter actual, podemos omitir su nombre:

<a n:href="default">home page</a>

Si la acción de destino es default, la podemos omitir, pero los dos puntos deben quedarse:

<a n:href="Home:">home page</a>

Los enlaces también pueden apuntar a otros módulos. Aquí se distingue entre enlaces relativos a un submódulo anidado y enlaces absolutos. El principio es análogo al de las rutas de disco, solo que en lugar de barras se usan dos puntos. Suponiendo que el presenter actual forme parte del módulo Front, escribiríamos:

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

Un caso especial es el enlace a sí mismo, donde indicamos this como destino.

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

Podemos enlazar a una parte concreta de la página mediante un fragmento tras el signo de almohadilla #:

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

El fragmento también se puede fijar dinámicamente como argumento con la clave #. Su valor se codifica automáticamente y tiene prioridad sobre el fragmento indicado en el destino:

$this->link('Home:default', ['#' => $fragment]);

Rutas absolutas

Los enlaces generados con link() o n:href son siempre rutas absolutas (es decir, empiezan por /), pero no URL absolutas con protocolo y dominio, como https://domain.

Para generar una URL absoluta, añada dos barras al principio (por ejemplo, n:href="//Home:"). Como alternativa, puede hacer que el presenter genere solo enlaces absolutos poniendo $this->absoluteUrls = true.

En la plantilla también se puede usar el filtro |absoluteUrl para convertir una ruta relativa en absoluta.

Enlace a la página actual

El destino this crea un enlace a la página actual:

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

Al mismo tiempo se transfieren todos los parámetros indicados en la firma del método action<Acción>() o render<Vista>() (si action<Acción>() no está definido). Así, si estamos en la página Product:show con id: 123, el enlace a this pasará también ese parámetro.

Por supuesto, es posible indicar los parámetros directamente:

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

La función isLinkCurrent() comprueba si el destino del enlace es idéntico a la página actual. Esto se puede usar, por ejemplo, en una plantilla para distinguir los enlaces, etc.

Los parámetros son los mismos que los del método link(), pero además es posible usar el comodín * en lugar de una acción concreta, lo que significa cualquier acción del presenter dado.

{if !isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">Login</a>
{/if}

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

Combinado con n:href en un mismo elemento, se puede usar una forma abreviada:

<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>

El comodín * solo se puede usar en lugar de la acción, no del presenter.

Para saber si estamos en un módulo concreto o en uno de sus submódulos, use el método isModuleCurrent(moduleName).

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

Cambio de la base de los enlaces

De forma predeterminada, los enlaces relativos se derivan del presenter actual. Esto se puede cambiar con {linkBase}:

{linkBase Admin:Dashboard}
<a n:href="Product:show">product detail</a>

El enlace llevará a Admin:Dashboard:Product:show. Solo se ven afectados los enlaces relativos: los absolutos, que empiezan por dos puntos, y los enlaces al presenter actual (this, show) quedan sin cambios.

{linkBase} vale para toda la plantilla y resulta especialmente útil en las plantillas de layout, donde garantiza enlaces coherentes con independencia del presenter que las use. La etiqueta debe colocarse al principio de la plantilla, o lanzará una CompileException.

Enlaces a una señal

El destino de un enlace no tiene por qué ser solo un presenter y una acción, sino también una señal (que llama al método handle<Señal>()). La sintaxis es entonces esta:

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

La señal se distingue, pues, por el signo de exclamación:

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

También puede crear un enlace a la señal de un subcomponente (o de un subsubcomponente):

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

Enlaces en un componente

Como los componentes son unidades independientes y reutilizables que no deberían tener ningún vínculo con los presenters que los rodean, aquí los enlaces funcionan de forma algo distinta. El atributo de Latte n:href y la etiqueta {link}, así como los métodos del componente como link() y demás, consideran siempre que el destino del enlace es el nombre de una señal. Por eso ni siquiera hace falta poner el signo de exclamación:

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

Si en la plantilla del componente quisiéramos enlazar a presenters, usaríamos la etiqueta {plink}:

<a href={plink Home:default}>home</a>

o en el código

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

Alias

A veces puede resultar útil asignar a una pareja Presenter:acción un alias fácil de recordar. Por ejemplo, llamar a la página de inicio Front:Home:default simplemente home, o a Admin:Dashboard:default llamarla admin.

Los alias se definen en la configuración, bajo la clave application › aliases:

application:
    aliases:
        home: Front:Home:default
        admin: Admin:Dashboard:default
        sign: Front:Sign:in

En los enlaces se escriben después con una arroba, por ejemplo:

<a n:href="@admin">administration</a>

También están admitidos en todos los métodos que trabajan con enlaces, como redirect() y similares.

Enlaces no válidos

Puede ocurrir que creemos un enlace no válido, ya sea porque lleva a un presenter inexistente, porque pasa más parámetros de los que acepta en su firma el método de destino, o porque no se puede generar ninguna URL para la acción de destino. Cómo tratar los enlaces no válidos se fija en el presenter con $this->invalidLinkMode. Puede tomar una combinación de estos valores (constantes):

  • Presenter::InvalidLinkSilent – modo silencioso, devuelve el carácter # como URL
  • Presenter::InvalidLinkWarning – se lanza una advertencia E_USER_WARNING, que se registrará en modo de producción, pero no interrumpirá la ejecución del script
  • Presenter::InvalidLinkTextual – advertencia visual, imprime el error directamente en el enlace
  • Presenter::InvalidLinkException – lanza InvalidLinkException

El ajuste predeterminado es InvalidLinkWarning en modo de producción e InvalidLinkWarning | InvalidLinkTextual en modo de desarrollo. InvalidLinkWarning en el entorno de producción no interrumpe el script, pero la advertencia queda registrada. En el entorno de desarrollo, Tracy la captura y muestra una pantalla azul. InvalidLinkTextual funciona devolviendo como URL un mensaje de error que empieza por los caracteres #error:. Para que esos enlaces salten a la vista, añada esto a su CSS:

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

Si no queremos que se produzcan advertencias en el entorno de desarrollo, podemos silenciarlas directamente en la configuración.

application:
	silentLinks: true

LinkGenerator

¿Cómo crear enlaces con una comodidad parecida a la del método link(), pero sin la presencia de un presenter? Para eso está Nette\Application\LinkGenerator.

LinkGenerator es un servicio que puede hacerse pasar por el constructor y con cuyo método link() puede crear después los enlaces.

Hay una diferencia respecto a los presenters. LinkGenerator crea todos los enlaces directamente como URL absolutas. Además, no existe un “presenter actual”, así que no es posible indicar como destino solo el nombre de la acción, link('default'), ni usar rutas relativas a los módulos.

Los enlaces no válidos lanzan siempre Nette\Application\UI\InvalidLinkException.

versión: 4.x