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 URLPresenter::InvalidLinkWarning– se lanza una advertencia E_USER_WARNING, que se registrará en modo de producción, pero no interrumpirá la ejecución del scriptPresenter::InvalidLinkTextual– advertencia visual, imprime el error directamente en el enlacePresenter::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.