Erstellen von URL-Links
Links in Nette zu erstellen ist so einfach wie mit dem Finger zu zeigen. Sie müssen nur zielen, und das Framework erledigt die ganze Arbeit für Sie. Wir zeigen:
- wie man Links in Templates und anderswo erstellt
- wie man einen Link auf die aktuelle Seite erkennt
- was man mit ungültigen Links macht
Dank des bidirektionalen Routings müssen Sie URLs Ihrer Anwendung nie fest in Templates oder Code schreiben – URLs, die sich später ändern könnten oder kompliziert zusammenzusetzen wären. Im Link geben Sie einfach den Presenter und die Aktion an, übergeben eventuelle Parameter, und das Framework erzeugt die URL selbst. Es ist eigentlich sehr ähnlich wie ein Funktionsaufruf. Das wird Ihnen gefallen.
Im Template des Presenters
Am häufigsten erstellen wir Links in Templates, und ein großartiger Helfer ist dabei das Attribut n:href:
<a n:href="Product:show">Detail</a>
Beachten Sie, dass wir statt des HTML-Attributs href das n:Attribut n:href verwendet haben. Sein Wert ist keine
URL, wie es beim Attribut href der Fall wäre, sondern der Name des Presenters und der Aktion.
Auf einen Link zu klicken ist, vereinfacht gesagt, so etwas wie der Aufruf der Methode
ProductPresenter::renderShow(). Und wenn diese in ihrer Signatur Parameter hat, können wir sie mit Argumenten
aufrufen:
<a n:href="Product:show $product->id, $product->slug">Produktdetail</a>
Es lassen sich auch benannte Parameter übergeben. Der folgende Link übergibt den Parameter lang mit dem Wert
en:
<a n:href="Product:show $product->id, lang: en">Produktdetail</a>
Hat die Methode ProductPresenter::renderShow() in ihrer Signatur kein $lang, kann sie den Wert des
Parameters mit $lang = $this->getParameter('lang') oder aus einer Property holen.
Sind die Parameter in einem Array gespeichert, lassen sie sich mit dem Operator ... entpacken:
{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">Produktdetail</a>
In Links werden automatisch auch die sogenannten persistenten Parameter übergeben.
Das Attribut n:href ist für HTML-Tags <a> sehr praktisch. Wollen wir den Link anderswo
ausgeben, zum Beispiel im Text, verwenden wir {link}:
Die URL lautet: {link Home:default}
Im Code
Zum Erstellen eines Links im Presenter dient die Methode link():
$url = $this->link('Product:show', $product->id);
Die Parameter lassen sich auch als Array übergeben, in dem sich ebenfalls benannte Parameter angeben lassen:
$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);
Links lassen sich auch ohne Presenter erstellen, und zwar mit dem LinkGenerator und seiner
Methode link().
Manchmal brauchen Sie einen Link jetzt, wollen die eigentliche URL aber erst später erzeugen. Dafür gibt es die Methode
lazyLink(), die ein Objekt Nette\Application\UI\Link zurückgibt. Der Vorteil ist, dass Sie dieses
Objekt weiterreichen können, zum Beispiel in ein Template, und seine Parameter vor dem Rendern noch mit der Methode
setParameter() anpassen können. Die URL selbst wird erst zusammengesetzt, wenn das Objekt in einen String
umgewandelt wird:
$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // erst hier wird die URL erzeugt
Links auf Presenter
Ist das Ziel des Links ein Presenter und eine Aktion, hat er diese Syntax:
[//] [[[[:]module:]presenter:]action | this] [#fragment]
Dieses Format unterstützen alle Latte-Tags und alle Methoden des Presenters, die mit Links arbeiten, also n:href,
{link}, {plink}, link(), lazyLink(), isLinkCurrent(),
redirect(), redirectPermanent(), forward(), canonicalize() und auch der LinkGenerator. Auch wenn in den Beispielen n:href verwendet wird, könnte dort also
jede dieser Funktionen stehen.
Die Grundform ist demnach Presenter:action:
<a n:href="Home:default">Startseite</a>
Verlinken wir auf eine Aktion des aktuellen Presenters, können wir dessen Namen weglassen:
<a n:href="default">Startseite</a>
Ist die Zielaktion default, können wir sie weglassen, der Doppelpunkt muss aber bleiben:
<a n:href="Home:">Startseite</a>
Links können auch auf andere Module zeigen. Dabei unterscheidet
man Links, die relativ zu einem verschachtelten Untermodul sind, und absolute Links. Das Prinzip ist analog zu Pfaden auf der
Festplatte, nur werden statt Schrägstrichen Doppelpunkte verwendet. Nehmen wir an, der aktuelle Presenter gehört zum Modul
Front, dann schreiben wir:
<a n:href="Shop:Product:show">Link auf Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">Link auf Admin:Product:show</a>
Ein Sonderfall ist ein Link auf sich selbst, bei dem wir als Ziel
this angeben.
<a n:href="this">aktualisieren</a>
Auf einen bestimmten Teil der Seite können wir über ein sogenanntes Fragment nach dem Rautezeichen #
verlinken:
<a n:href="Home:#main">Link auf Home:default und das Fragment #main</a>
Das Fragment lässt sich auch dynamisch als Argument mit dem Schlüssel # setzen. Sein Wert
wird automatisch kodiert und hat Vorrang vor dem im Ziel angegebenen Fragment:
$this->link('Home:default', ['#' => $fragment]);
Absolute Pfade
Mit link() oder n:href erzeugte Links sind immer absolute Pfade (sie beginnen also mit
/), aber keine absoluten URLs mit Protokoll und Domain wie https://domain.
Um eine absolute URL zu erzeugen, ergänzen Sie am Anfang zwei Schrägstriche (z. B. n:href="//Home:"). Alternativ
können Sie den Presenter mit $this->absoluteUrls = true so umschalten, dass er nur absolute Links erzeugt.
Im Template lässt sich außerdem der Filter |absoluteUrl verwenden, der einen relativen Pfad in einen absoluten
umwandelt.
Link auf die aktuelle Seite
Das Ziel this erzeugt einen Link auf die aktuelle Seite:
<a n:href="this">aktualisieren</a>
Dabei werden zugleich alle Parameter übernommen, die in der Signatur der Methode action<Aktion>() oder
render<View>() angegeben sind (sofern action<Aktion>() nicht definiert ist). Sind wir also
auf der Seite Product:show mit id: 123, übergibt der Link auf this diesen Parameter
ebenfalls.
Natürlich lassen sich Parameter auch direkt angeben:
<a n:href="this refresh: 1">aktualisieren</a>
Die Funktion isLinkCurrent() prüft, ob das Ziel des Links mit der aktuellen Seite identisch ist. Das lässt sich
zum Beispiel im Template nutzen, um Links optisch zu unterscheiden usw.
Die Parameter sind dieselben wie bei der Methode link(), zusätzlich lässt sich anstelle einer konkreten Aktion
die Wildcard * verwenden, was jede beliebige Aktion des angegebenen Presenters bedeutet.
{if !isLinkCurrent('Admin:login')}
<a n:href="Admin:login">Anmelden</a>
{/if}
<li n:class="isLinkCurrent('Product:*') ? active">
<a n:href="Product:">...</a>
</li>
In Kombination mit n:href im selben Element lässt sich eine Kurzform verwenden:
<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>
Die Wildcard * lässt sich nur anstelle der Aktion verwenden, nicht anstelle des Presenters.
Um festzustellen, ob wir uns in einem bestimmten Modul oder dessen Untermodul befinden, dient die Methode
isModuleCurrent(moduleName).
<li n:class="isModuleCurrent('Forum:Users') ? active">
<a n:href="Product:">...</a>
</li>
Link-Basis ändern
Standardmäßig werden relative Links vom aktuellen Presenter abgeleitet. Das lässt sich mit {linkBase}
ändern:
{linkBase Admin:Dashboard}
<a n:href="Product:show">Produktdetail</a>
Der Link führt dann auf Admin:Dashboard:Product:show. Betroffen sind nur relative Links – absolute Links, die
mit einem Doppelpunkt beginnen, und Links auf den aktuellen Presenter (this, show) bleiben
unverändert.
{linkBase} gilt für das gesamte Template und ist besonders in Layout-Templates nützlich, wo es unabhängig vom
aufrufenden Presenter für einheitliche Links sorgt. Der Tag muss am Anfang des Templates stehen, sonst wirft er eine
CompileException.
Links auf Signale
Das Ziel eines Links muss nicht nur ein Presenter und eine Aktion sein, sondern kann auch ein Signal sein (es ruft die Methode
handle<Signal>() auf). Dann sieht die Syntax so aus:
[//] [sub-component:]signal! [#fragment]
Das Signal wird also durch ein Ausrufezeichen unterschieden:
<a n:href="click!">Signal</a>
Sie können auch einen Link auf ein Signal einer Unterkomponente (oder Unter-Unterkomponente) erstellen:
<a n:href="componentName:click!">Signal</a>
Links in einer Komponente
Da Komponenten eigenständige, wiederverwendbare Einheiten sind,
die keinerlei Bindung an umgebende Presenter haben sollten, funktionieren Links hier etwas anders. Das Latte-Attribut
n:href und der Tag {link} sowie Methoden der Komponente wie link() und weitere
betrachten das Ziel des Links immer als Namen eines Signals. Deshalb muss nicht einmal ein Ausrufezeichen angegeben
werden:
<a n:href="click">Signal, keine Aktion</a>
Wollen wir im Template der Komponente auf Presenter verlinken, verwenden wir den Tag {plink}:
<a href={plink Home:default}>Startseite</a>
oder im Code
$this->getPresenter()->link('Home:default')
Aliase
Manchmal ist es praktisch, einem Paar Presenter:action einen leicht merkbaren Alias zuzuweisen. Zum Beispiel die Startseite
Front:Home:default einfach home zu nennen oder Admin:Dashboard:default als
admin.
Aliase werden in der Konfiguration unter dem Schlüssel
application › aliases definiert:
application:
aliases:
home: Front:Home:default
admin: Admin:Dashboard:default
sign: Front:Sign:in
In Links schreibt man sie dann mit einem At-Zeichen, zum Beispiel:
<a n:href="@admin">Administration</a>
Unterstützt werden sie auch in allen Methoden, die mit Links arbeiten, etwa redirect() und ähnlichen.
Ungültige Links
Es kann passieren, dass wir einen ungültigen Link erzeugen – entweder weil er auf einen nicht existierenden Presenter
führt, oder weil er mehr Parameter übergibt, als die Zielmethode in ihrer Signatur entgegennimmt, oder wenn für die Zielaktion
keine URL erzeugt werden kann. Wie mit ungültigen Links umgegangen wird, legt man im Presenter über
$this->invalidLinkMode fest. Es kann eine Kombination dieser Werte (Konstanten) annehmen:
Presenter::InvalidLinkSilent– stiller Modus, gibt als URL das Zeichen # zurückPresenter::InvalidLinkWarning– es wird eine Warnung E_USER_WARNING ausgelöst, die im Produktionsmodus protokolliert wird, die Ausführung des Skripts aber nicht unterbrichtPresenter::InvalidLinkTextual– visuelle Warnung, gibt den Fehler direkt in den Link ausPresenter::InvalidLinkException– wirft eine InvalidLinkException
Die Standardeinstellung ist InvalidLinkWarning im Produktionsmodus und
InvalidLinkWarning | InvalidLinkTextual im Entwicklungsmodus. InvalidLinkWarning bewirkt in der
Produktionsumgebung keinen Abbruch des Skripts, die Warnung wird jedoch protokolliert. In der Entwicklungsumgebung fängt Tracy sie ab und zeigt einen Bluescreen. InvalidLinkTextual funktioniert so,
dass es als URL eine Fehlermeldung zurückgibt, die mit den Zeichen #error: beginnt. Damit solche Links auf den
ersten Blick auffallen, ergänzen Sie Ihr CSS um:
a[href^="#error:"] {
background: red;
color: white;
}
Wollen wir, dass in der Entwicklungsumgebung keine Warnungen entstehen, können wir sie direkt in der Konfiguration unterdrücken.
application:
silentLinks: true
LinkGenerator
Wie erstellt man Links mit ähnlichem Komfort wie mit der Methode link(), aber ohne die Anwesenheit eines
Presenters? Dafür ist Nette\Application\LinkGenerator da.
Der LinkGenerator ist ein Service, den Sie sich über den Konstruktor übergeben lassen können und mit dessen Methode
link() Sie dann Links erstellen.
Gegenüber Presentern gibt es einen Unterschied. Der LinkGenerator erzeugt alle Links direkt als absolute URLs. Außerdem gibt
es keinen “aktuellen Presenter”, daher lässt sich als Ziel weder nur der Name der Aktion link('default') angeben
noch lassen sich relative Pfade zu Modulen verwenden.
Ungültige Links werfen immer eine Nette\Application\UI\InvalidLinkException.