Creazione di link URL
*********************
<div class=perex>
Creare link in Nette è semplice come puntare il dito. Basta mirare e il framework farà tutto il lavoro per voi. Vi mostreremo:
- come creare link nei template e altrove
- come riconoscere un link alla pagina corrente
- cosa fare con i link non validi
</div>
Grazie al [routing bidirezionale |routing] non dovrete mai scrivere a mano nei template o nel codice gli URL della vostra applicazione, che potrebbero cambiare in seguito o essere complicati da comporre. Nel link basta indicare il presenter e l'azione, passare eventuali parametri, e il framework genererà l'URL da sé. In realtà è molto simile a chiamare una funzione. Vi piacerà.
Nel template del presenter
==========================
Il più delle volte creiamo i link nei template, e l'attributo `n:href` è un ottimo aiuto:
```latte
<a n:href="Product:show">dettaglio</a>
```
Notate che al posto dell'attributo HTML `href` abbiamo usato l'[n:attributo |latte:syntax#n:attributi] `n:href`. Il suo valore non è un URL, come sarebbe per l'attributo `href`, ma il nome del presenter e dell'azione.
Cliccare su un link è, detta semplicemente, un po' come chiamare il metodo `ProductPresenter::renderShow()`. E se questo ha dei parametri nella propria firma, possiamo chiamarlo con degli argomenti:
```latte
<a n:href="Product:show, $product->id, $product->slug">dettaglio del prodotto</a>
```
È possibile passare anche parametri nominali. Il link seguente passa il parametro `lang` con il valore `en`:
```latte
<a n:href="Product:show, $product->id, lang: en">dettaglio del prodotto</a>
```
Se il metodo `ProductPresenter::renderShow()` non ha `$lang` nella propria firma, può ottenere il valore del parametro con `$lang = $this->getParameter('lang')` oppure da una [proprietà |presenters#Parametri della richiesta].
Se i parametri sono salvati in un array, si possono espandere con l'operatore `...`:
```latte
{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">dettaglio del prodotto</a>
```
Nei link vengono passati automaticamente anche i cosiddetti [parametri persistenti |presenters#Parametri persistenti].
L'attributo `n:href` è molto comodo per i tag HTML `<a>`. Se vogliamo stampare il link altrove, per esempio nel testo, usiamo `{link}`:
```latte
L'URL è: {link Home:default}
```
Nel codice
==========
Per creare un link nel presenter si usa il metodo `link()`:
```php
$url = $this->link('Product:show', $product->id);
```
I parametri si possono passare anche come array, in cui si possono indicare anche parametri nominali:
```php
$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);
```
I link si possono creare anche senza un presenter, con il [#LinkGenerator] e il suo metodo `link()`.
A volte vi serve creare un link subito, ma generare l'URL vero e proprio solo più tardi. A questo serve il metodo `lazyLink()`, che restituisce un oggetto `Nette\Application\UI\Link`. Il vantaggio è che potete passare questo oggetto, per esempio a un template, e prima che venga disegnato potete ancora modificarne i parametri con il metodo `setParameter()`. L'URL viene composto solo quando l'oggetto viene convertito in stringa:
```php
$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // l'URL viene generato solo qui
```
Link a un presenter
===================
Se la destinazione del link è un presenter con un'azione, la sintassi è questa:
```
[//] [[[[:]module:]presenter:]action | this] [#fragment]
```
Questo formato è supportato da tutti i tag di Latte e da tutti i metodi del presenter che lavorano con i link, cioè `n:href`, `{link}`, `{plink}`, `link()`, `lazyLink()`, `isLinkCurrent()`, `redirect()`, `redirectPermanent()`, `forward()`, `canonicalize()` e anche dal [#LinkGenerator]. Quindi, anche se negli esempi si usa `n:href`, al suo posto potrebbe esserci una qualsiasi di queste funzioni.
La forma di base è dunque `Presenter:azione`:
```latte
<a n:href="Home:default">home page</a>
```
Se colleghiamo a un'azione del presenter corrente, possiamo ometterne il nome:
```latte
<a n:href="default">home page</a>
```
Se l'azione di destinazione è `default`, possiamo ometterla, ma i due punti devono restare:
```latte
<a n:href="Home:">home page</a>
```
I link possono puntare anche ad altri [moduli |directory-structure#Presenter e template]. Qui si distingue tra link relativi a un sottomodulo annidato e link assoluti. Il principio è analogo a quello dei percorsi su disco, solo che al posto delle barre si usano i due punti. Supponendo che il presenter corrente faccia parte del modulo `Front`, scriveremmo:
```latte
<a n:href="Shop:Product:show">link a Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">link a Admin:Product:show</a>
```
Un caso particolare è il link [a sé stessi |#Link alla pagina corrente], dove indichiamo come destinazione `this`.
```latte
<a n:href="this">aggiorna</a>
```
Possiamo collegare a una parte precisa della pagina tramite il cosiddetto frammento dopo il cancelletto `#`:
```latte
<a n:href="Home:#main">link a Home:default e al frammento #main</a>
```
.{data-version:3.3.0}
Il frammento si può impostare anche dinamicamente, come argomento con la chiave `#`. Il suo valore viene codificato automaticamente e ha la precedenza sul frammento indicato nella destinazione:
```php
$this->link('Home:default', ['#' => $fragment]);
```
Percorsi assoluti
=================
I link generati con `link()` o `n:href` sono sempre percorsi assoluti (cioè iniziano con `/`), ma non URL assoluti con protocollo e dominio, come `https://domain`.
Per generare un URL assoluto aggiungete due barre all'inizio (per esempio `n:href="//Home:"`). In alternativa potete far generare al presenter solo link assoluti impostando `$this->absoluteUrls = true`.
Nel template si può usare anche il filtro `|absoluteUrl` per convertire un percorso relativo in uno assoluto.
Link alla pagina corrente
=========================
La destinazione `this` crea un link alla pagina corrente:
```latte
<a n:href="this">aggiorna</a>
```
Allo stesso tempo vengono trasferiti tutti i parametri indicati nella firma del metodo `action<Azione>()` o `render<Vista>()` (se `action<Azione>()` non è definito). Se quindi ci troviamo sulla pagina `Product:show` con `id: 123`, anche il link a `this` passerà questo parametro.
Naturalmente è possibile indicare i parametri direttamente:
```latte
<a n:href="this, refresh: 1">aggiorna</a>
```
La funzione `isLinkCurrent()` controlla se la destinazione del link coincide con la pagina corrente. Si può usare per esempio in un template per distinguere i link e simili.
I parametri sono gli stessi del metodo `link()`, ma al posto di un'azione specifica si può usare anche il carattere jolly `*`, che indica una qualsiasi azione del presenter indicato.
```latte
{if !isLinkCurrent('Admin:login')}
<a n:href="Admin:login">Accedi</a>
{/if}
<li n:class="isLinkCurrent('Product:*') ? active">
<a n:href="Product:">...</a>
</li>
```
In combinazione con `n:href` su un unico elemento si può usare una forma abbreviata:
```latte
<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>
```
Il carattere jolly `*` si può usare solo al posto dell'azione, non del presenter.
Per stabilire se ci troviamo in un determinato modulo o in un suo sottomodulo, usate il metodo `isModuleCurrent(moduleName)`.
```latte
<li n:class="isModuleCurrent('Forum:Users') ? active">
<a n:href="Product:">...</a>
</li>
```
Cambiare la base dei link .{data-version:3.2.7}
===============================================
Per impostazione predefinita i link relativi sono derivati dal presenter corrente. Lo si può cambiare con `{linkBase}`:
```latte
{linkBase Admin:Dashboard}
<a n:href="Product:show">dettaglio del prodotto</a>
```
Il link porterà a `Admin:Dashboard:Product:show`. Ne sono interessati solo i link relativi: i link assoluti che iniziano con i due punti e i link al presenter corrente (`this`, `show`) restano invariati.
`{linkBase}` vale per l'intero template ed è particolarmente utile nei template di layout, dove garantisce link coerenti indipendentemente dal presenter chiamante.
Il tag va collocato all'inizio del template, altrimenti solleva una `CompileException`.
Link a un segnale
=================
La destinazione di un link non deve essere per forza un presenter con un'azione: può essere anche un [segnale |components#Segnale] (che chiama il metodo `handle<Segnale>()`). La sintassi è allora questa:
```
[//] [sotto-componente:]segnale! [#fragment]
```
Il segnale si distingue quindi per il punto esclamativo:
```latte
<a n:href="click!">segnale</a>
```
Potete creare anche un link a un segnale di un sottocomponente (o di un sotto-sottocomponente):
```latte
<a n:href="componentName:click!">segnale</a>
```
Link in un componente
=====================
Poiché i [componenti|components] sono unità riutilizzabili autonome, che non dovrebbero avere alcun legame con i presenter circostanti, qui i link funzionano in modo un po' diverso. L'attributo Latte `n:href` e il tag `{link}`, così come i metodi del componente come `link()` e altri, **considerano sempre la destinazione del link come il nome di un segnale**. Non è quindi nemmeno necessario indicare il punto esclamativo:
```latte
<a n:href="click">segnale, non un'azione</a>
```
Se volessimo collegare ai presenter nel template di un componente, useremmo il tag `{plink}`:
```latte
<a href={plink Home:default}>home</a>
```
oppure nel codice
```php
$this->getPresenter()->link('Home:default')
```
Alias .{data-version:3.2.3}
===========================
A volte può essere utile assegnare a una coppia Presenter:azione un alias facile da ricordare. Per esempio chiamare la home page `Front:Home:default` semplicemente `home`, oppure `Admin:Dashboard:default` come `admin`.
Gli alias si definiscono nella [configurazione|configuration], sotto la chiave `application › aliases`:
```neon
application:
aliases:
home: Front:Home:default
admin: Admin:Dashboard:default
sign: Front:Sign:in
```
Nei link si scrivono poi con la chiocciola, per esempio:
```latte
<a n:href="@admin">amministrazione</a>
```
Sono supportati anche in tutti i metodi che lavorano con i link, come `redirect()` e simili.
Link non validi
===============
Può capitare di creare un link non valido: perché porta a un presenter inesistente, perché passa più parametri di quanti ne accetti il metodo di destinazione nella propria firma, oppure perché per l'azione di destinazione non è possibile generare un URL. Come gestire i link non validi si imposta nel presenter con `$this->invalidLinkMode`. Può assumere una combinazione di questi valori (costanti):
- `Presenter::InvalidLinkSilent` - modalità silenziosa, restituisce come URL il carattere #
- `Presenter::InvalidLinkWarning` - viene emesso un avviso E_USER_WARNING, che in modalità di produzione verrà registrato nel log ma non interromperà l'esecuzione dello script
- `Presenter::InvalidLinkTextual` - avviso visivo, stampa l'errore direttamente nel link
- `Presenter::InvalidLinkException` - solleva InvalidLinkException
L'impostazione predefinita è `InvalidLinkWarning` in modalità di produzione e `InvalidLinkWarning | InvalidLinkTextual` in modalità di sviluppo. In ambiente di produzione `InvalidLinkWarning` non provoca l'interruzione dello script, ma l'avviso verrà registrato nel log. In ambiente di sviluppo lo intercetta [Tracy |tracy:] e mostra una schermata blu. `InvalidLinkTextual` funziona restituendo come URL un messaggio di errore che inizia con i caratteri `#error:`. Perché link del genere si notino a colpo d'occhio, aggiungete al vostro CSS:
```css
a[href^="#error:"] {
background: red;
color: white;
}
```
Se non vogliamo che in ambiente di sviluppo vengano emessi avvisi, possiamo silenziarli direttamente nella [configurazione|configuration].
```neon
application:
silentLinks: true
```
LinkGenerator
=============
Come creare link con la stessa comodità del metodo `link()`, ma senza la presenza di un presenter? A questo serve [api:Nette\Application\LinkGenerator].
LinkGenerator è un servizio che potete farvi passare tramite il costruttore e con cui potete poi creare link usandone il metodo `link()`.
C'è una differenza rispetto ai presenter. LinkGenerator crea tutti i link direttamente come URL assoluti. Inoltre non esiste un "presenter corrente", quindi non è possibile indicare come destinazione solo il nome dell'azione, `link('default')`, né usare percorsi relativi ai moduli.
I link non validi sollevano sempre `Nette\Application\UI\InvalidLinkException`.
Creazione di link URL
Creare link in Nette è semplice come puntare il dito. Basta mirare e il framework farà tutto il lavoro per voi. Vi
mostreremo:
- come creare link nei template e altrove
- come riconoscere un link alla pagina corrente
- cosa fare con i link non validi
Grazie al routing bidirezionale non dovrete mai scrivere a mano nei
template o nel codice gli URL della vostra applicazione, che potrebbero cambiare in seguito o essere complicati da comporre. Nel
link basta indicare il presenter e l'azione, passare eventuali parametri, e il framework genererà l'URL da sé. In realtà è
molto simile a chiamare una funzione. Vi piacerà.
Nel template del presenter
Il più delle volte creiamo i link nei template, e l'attributo n:href è un ottimo aiuto:
<a n:href="Product:show">dettaglio</a>
Notate che al posto dell'attributo HTML href abbiamo usato l'n:attributo n:href. Il suo valore non è un URL, come
sarebbe per l'attributo href, ma il nome del presenter e dell'azione.
Cliccare su un link è, detta semplicemente, un po' come chiamare il metodo ProductPresenter::renderShow(). E se
questo ha dei parametri nella propria firma, possiamo chiamarlo con degli argomenti:
<a n:href="Product:show, $product->id, $product->slug">dettaglio del prodotto</a>
È possibile passare anche parametri nominali. Il link seguente passa il parametro lang con il valore
en:
<a n:href="Product:show, $product->id, lang: en">dettaglio del prodotto</a>
Se il metodo ProductPresenter::renderShow() non ha $lang nella propria firma, può ottenere il valore
del parametro con $lang = $this->getParameter('lang') oppure da una proprietà.
Se i parametri sono salvati in un array, si possono espandere con l'operatore ...:
{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">dettaglio del prodotto</a>
Nei link vengono passati automaticamente anche i cosiddetti parametri persistenti.
L'attributo n:href è molto comodo per i tag HTML <a>. Se vogliamo stampare il link altrove,
per esempio nel testo, usiamo {link}:
L'URL è: {link Home:default}
Nel codice
Per creare un link nel presenter si usa il metodo link():
$url = $this->link('Product:show', $product->id);
I parametri si possono passare anche come array, in cui si possono indicare anche parametri nominali:
$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);
I link si possono creare anche senza un presenter, con il LinkGenerator e il suo metodo
link().
A volte vi serve creare un link subito, ma generare l'URL vero e proprio solo più tardi. A questo serve il metodo
lazyLink(), che restituisce un oggetto Nette\Application\UI\Link. Il vantaggio è che potete passare
questo oggetto, per esempio a un template, e prima che venga disegnato potete ancora modificarne i parametri con il metodo
setParameter(). L'URL viene composto solo quando l'oggetto viene convertito in stringa:
$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // l'URL viene generato solo qui
Link a un presenter
Se la destinazione del link è un presenter con un'azione, la sintassi è questa:
[//] [[[[:]module:]presenter:]action | this] [#fragment]
Questo formato è supportato da tutti i tag di Latte e da tutti i metodi del presenter che lavorano con i link, cioè
n:href, {link}, {plink}, link(), lazyLink(),
isLinkCurrent(), redirect(), redirectPermanent(), forward(),
canonicalize() e anche dal LinkGenerator. Quindi, anche se negli esempi si usa
n:href, al suo posto potrebbe esserci una qualsiasi di queste funzioni.
La forma di base è dunque Presenter:azione:
<a n:href="Home:default">home page</a>
Se colleghiamo a un'azione del presenter corrente, possiamo ometterne il nome:
<a n:href="default">home page</a>
Se l'azione di destinazione è default, possiamo ometterla, ma i due punti devono restare:
<a n:href="Home:">home page</a>
I link possono puntare anche ad altri moduli. Qui si distingue tra link
relativi a un sottomodulo annidato e link assoluti. Il principio è analogo a quello dei percorsi su disco, solo che al posto
delle barre si usano i due punti. Supponendo che il presenter corrente faccia parte del modulo Front,
scriveremmo:
<a n:href="Shop:Product:show">link a Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">link a Admin:Product:show</a>
Un caso particolare è il link a sé stessi, dove indichiamo come destinazione
this.
<a n:href="this">aggiorna</a>
Possiamo collegare a una parte precisa della pagina tramite il cosiddetto frammento dopo il cancelletto #:
<a n:href="Home:#main">link a Home:default e al frammento #main</a>
Il frammento si può impostare anche dinamicamente, come argomento con la chiave #. Il suo
valore viene codificato automaticamente e ha la precedenza sul frammento indicato nella destinazione:
$this->link('Home:default', ['#' => $fragment]);
Percorsi assoluti
I link generati con link() o n:href sono sempre percorsi assoluti (cioè iniziano con
/), ma non URL assoluti con protocollo e dominio, come https://domain.
Per generare un URL assoluto aggiungete due barre all'inizio (per esempio n:href="//Home:"). In alternativa potete
far generare al presenter solo link assoluti impostando $this->absoluteUrls = true.
Nel template si può usare anche il filtro |absoluteUrl per convertire un percorso relativo in uno assoluto.
Link alla pagina corrente
La destinazione this crea un link alla pagina corrente:
<a n:href="this">aggiorna</a>
Allo stesso tempo vengono trasferiti tutti i parametri indicati nella firma del metodo action<Azione>() o
render<Vista>() (se action<Azione>() non è definito). Se quindi ci troviamo sulla pagina
Product:show con id: 123, anche il link a this passerà questo parametro.
Naturalmente è possibile indicare i parametri direttamente:
<a n:href="this, refresh: 1">aggiorna</a>
La funzione isLinkCurrent() controlla se la destinazione del link coincide con la pagina corrente. Si può usare
per esempio in un template per distinguere i link e simili.
I parametri sono gli stessi del metodo link(), ma al posto di un'azione specifica si può usare anche il
carattere jolly *, che indica una qualsiasi azione del presenter indicato.
{if !isLinkCurrent('Admin:login')}
<a n:href="Admin:login">Accedi</a>
{/if}
<li n:class="isLinkCurrent('Product:*') ? active">
<a n:href="Product:">...</a>
</li>
In combinazione con n:href su un unico elemento si può usare una forma abbreviata:
<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>
Il carattere jolly * si può usare solo al posto dell'azione, non del presenter.
Per stabilire se ci troviamo in un determinato modulo o in un suo sottomodulo, usate il metodo
isModuleCurrent(moduleName).
<li n:class="isModuleCurrent('Forum:Users') ? active">
<a n:href="Product:">...</a>
</li>
Cambiare la base dei link
Per impostazione predefinita i link relativi sono derivati dal presenter corrente. Lo si può cambiare con
{linkBase}:
{linkBase Admin:Dashboard}
<a n:href="Product:show">dettaglio del prodotto</a>
Il link porterà a Admin:Dashboard:Product:show. Ne sono interessati solo i link relativi: i link assoluti che
iniziano con i due punti e i link al presenter corrente (this, show) restano invariati.
{linkBase} vale per l'intero template ed è particolarmente utile nei template di layout, dove garantisce link
coerenti indipendentemente dal presenter chiamante. Il tag va collocato all'inizio del template, altrimenti solleva una
CompileException.
Link a un segnale
La destinazione di un link non deve essere per forza un presenter con un'azione: può essere anche un segnale (che chiama il metodo
handle<Segnale>()). La sintassi è allora questa:
[//] [sotto-componente:]segnale! [#fragment]
Il segnale si distingue quindi per il punto esclamativo:
<a n:href="click!">segnale</a>
Potete creare anche un link a un segnale di un sottocomponente (o di un sotto-sottocomponente):
<a n:href="componentName:click!">segnale</a>
Link in un componente
Poiché i componenti sono unità riutilizzabili autonome, che
non dovrebbero avere alcun legame con i presenter circostanti, qui i link funzionano in modo un po' diverso. L'attributo Latte
n:href e il tag {link}, così come i metodi del componente come link() e altri,
considerano sempre la destinazione del link come il nome di un segnale. Non è quindi nemmeno necessario indicare il punto
esclamativo:
<a n:href="click">segnale, non un'azione</a>
Se volessimo collegare ai presenter nel template di un componente, useremmo il tag {plink}:
<a href={plink Home:default}>home</a>
oppure nel codice
$this->getPresenter()->link('Home:default')
Alias
A volte può essere utile assegnare a una coppia Presenter:azione un alias facile da ricordare. Per esempio chiamare la home
page Front:Home:default semplicemente home, oppure Admin:Dashboard:default come
admin.
Gli alias si definiscono nella configurazione, sotto la chiave
application › aliases:
application:
aliases:
home: Front:Home:default
admin: Admin:Dashboard:default
sign: Front:Sign:in
Nei link si scrivono poi con la chiocciola, per esempio:
<a n:href="@admin">amministrazione</a>
Sono supportati anche in tutti i metodi che lavorano con i link, come redirect() e simili.
Link non validi
Può capitare di creare un link non valido: perché porta a un presenter inesistente, perché passa più parametri di quanti ne
accetti il metodo di destinazione nella propria firma, oppure perché per l'azione di destinazione non è possibile generare un
URL. Come gestire i link non validi si imposta nel presenter con $this->invalidLinkMode. Può assumere una
combinazione di questi valori (costanti):
Presenter::InvalidLinkSilent – modalità silenziosa, restituisce come URL il carattere #
Presenter::InvalidLinkWarning – viene emesso un avviso E_USER_WARNING, che in modalità di produzione verrà
registrato nel log ma non interromperà l'esecuzione dello script
Presenter::InvalidLinkTextual – avviso visivo, stampa l'errore direttamente nel link
Presenter::InvalidLinkException – solleva InvalidLinkException
L'impostazione predefinita è InvalidLinkWarning in modalità di produzione e
InvalidLinkWarning | InvalidLinkTextual in modalità di sviluppo. In ambiente di produzione
InvalidLinkWarning non provoca l'interruzione dello script, ma l'avviso verrà registrato nel log. In ambiente di
sviluppo lo intercetta Tracy e mostra una schermata blu. InvalidLinkTextual
funziona restituendo come URL un messaggio di errore che inizia con i caratteri #error:. Perché link del genere si
notino a colpo d'occhio, aggiungete al vostro CSS:
a[href^="#error:"] {
background: red;
color: white;
}
Se non vogliamo che in ambiente di sviluppo vengano emessi avvisi, possiamo silenziarli direttamente nella configurazione.
application:
silentLinks: true
LinkGenerator
Come creare link con la stessa comodità del metodo link(), ma senza la presenza di un presenter? A questo serve
Nette\Application\LinkGenerator.
LinkGenerator è un servizio che potete farvi passare tramite il costruttore e con cui potete poi creare link usandone il
metodo link().
C'è una differenza rispetto ai presenter. LinkGenerator crea tutti i link direttamente come URL assoluti. Inoltre non esiste
un “presenter corrente”, quindi non è possibile indicare come destinazione solo il nome dell'azione,
link('default'), né usare percorsi relativi ai moduli.
I link non validi sollevano sempre Nette\Application\UI\InvalidLinkException.