Der Standard Router
Einführung
Zend_Controller_Router_Rewrite ist der Standard Router des
Frameworks. Routing ist der Prozess der Übernahme und Zerteilung einer
URI (dem Teil der URI der nach der Basis
URL kommt), um zu ermitteln, welches Modul, welcher Controller und
welche Aktion des Controllers die Anfrage erhalten soll. Die Definition des Moduls, des
Controllers, der Aktion sowie weiterer Parameter wird in einem Objekt mit Namen
Zend_Controller_Dispatcher_Token gekapselt, das dann vom
Zend_Controller_Dispatcher_Standard verarbeitet wird. Das Routing
geschieht nur einmal: wenn zu Beginn die Anfrage erhalten wird und bevor der erste
Controller aufgerufen wird.
Zend_Controller_Router_Rewrite wurde entwickelt, um mit reinen
PHP Strukturen eine mod_rewrite ähnliche Funktionalität zu erlauben.
Es richtet sich sehr frei nach dem Ruby on Rails Routing und benötigt kein tieferes
Wissen über URL Weiterleitung des Webservers. Es wurde entwickelt, um
mit einer einzigen mod_rewrite Regel zu arbeiten.
oder (bevorzugt):
Der Rewrite Router kann auch mit dem IIS Webserver verwendet werden
(Versionen <= 7.0), wenn » Isapi_Rewrite als Isapi
Erweiterung installiert wurde und folgende Umschreibungsregel verwendet wird:
Note: IIS Isapi_Rewrite
Bei Verwendung von IIS, wird
$_SERVER['REQUEST_URI'] entweder nicht vorhanden oder auf einen
leeren String gesetzt sein. In diesem Fall wird
Zend_Controller_Request_Http versuchen, den durch die
Isapi_Rewrite Erweiterung gesetzten Wert
$_SERVER['HTTP_X_REWRITE_URL'] zu verwenden.
IIS 7.0 führt ein natives URL Rewriting Modul
ein, und kann wie folgt konfiguriert werden:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Imported Rule 1" stopProcessing="true">
<match url="^.*$" />
<conditions logicalGrouping="MatchAny">
<add input="{REQUEST_FILENAME}"
matchType="IsFile" pattern=""
ignoreCase="false" />
<add input="{REQUEST_FILENAME}"
matchType="IsDirectory"
pattern="" ignoreCase="false" />
</conditions>
<action type="None" />
</rule>
<rule name="Imported Rule 2" stopProcessing="true">
<match url="^.*$" />
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Bei der Verwendung von Lighttpd, ist folgende Umschreibungsregel gültig:
url.rewrite-once = (
".*\?(.*)$" => "/index.php?$1",
".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
"" => "/index.php"
)
Einen Router verwenden
Um den Rewrite Router richtig zu verwenden, muß er instanziiert, einige
benutzerdefinierte Routen hinzufügt und in den Controller einbunden werden. Der folgende
Code veranschaulicht die Vorgehensweise:
// Einen Router erstellen
$router = $ctrl->getRouter(); // gibt standardmäßig einen Rewrite Router zurück
$router->addRoute(
'user''user/:username''controller' => 'user',
'action' => 'info'))
);
Grundsätzliche Rewrite Router Operationen
Das Herz des RewriteRouters ist die Definition von Benutzerdefinierten Routen. Routen
werden durch aufruf der addRoute Methode des RewriteRouters hinzugefügt und übergeben
eine neue Instanz einer Klasse die
Zend_Controller_Router_Route_Interface implementiert. Z.B.:
$router->addRoute('user''user/:username'));
Der Rewrite Router kommt mit sechs Basistypen von Routen (eine von denen ist speziell):
is special):
Routen können unzählige Male verwendet werden um eine Kette oder benutzerdefinierte
Routing Schemas von Anwendungen zu erstellen. Es kann jede beliebige Anzahl von Routen
in jeder beliebigen Konfiguration verwendet werden, mit Ausnahme der Modul Route, welche
nur einmal verwendet werden sollte, und möglicherweise die am meisten standardmäßige
Route ist (z.B., als ein Standard). Jede Route wird später detailiert beschrieben.
Der erste Parameter für addRoute ist der Name der Route. Er wird als Handle verwendet um
die Route außerhalb des Routers zu erhalten (z.B. für den Zweck der
URL Erzeugung). Der zweite Parameter ist die Route selbst.
Note:
Die gewöhnlichste Verwendung des Namens der Route ist gegeben durch die Zwecke des
Zend_View Url Helfers:
<a href=
"<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>
Was zu folgender href führt: user/martel.
Routen ist ein einfacher Prozess des Durchlaufens aller vorhandenen Routen und
Vergleichens deren Definitionen mit der aktuellen Anfrage URI. Wenn
ein positiver Vergleich gefunden wird, werden variable Werte von der Instanz des Routers
zurückgegeben, und werden für die spätere Verwendung im Dispatcher in das
Zend_Controller_Request Objekt iniziiert, sowie in von Benutzern
erstellten Controllern. Bei einem negativen Ergebnis des Vergleiches, wird die nächste
Route in der Kette geprüft.
Wenn man herausfinden will welche Route gepasst hat, kann man die
getCurrentRouteName() Methode verwenden, die den Identifikator
zurückgibt der verwendet wurde als die Route im Router registriert wurde. Wenn man das
aktuelle Route Objekt benötigt, kann getCurrentRoute()
verwendet werden.
Note: Umgekehrter Vergleich
Routen werden in umgekehrter Reihenfolge verglichen. Deswegen muß sichergestellt
werden das die generellen Routen zuerst definiert werden.
Note: Zurückgegebene Werte
Werte die vom Routen zurückgegeben werden kommen von URL
Parametern oder Benutzerdefinierten Router Standards. Diese Variablen sind später
durch die Zend_Controller_Request::getParam() oder
Zend_Controller_Action::_getParam()Methoden verwendbar.
Es gibt drei spezielle Variablen welche in den Routen verwendet werden können -
'module', 'controller' und 'action'. Diese speziellen Variablen werden durch
Zend_Controller_Dispatcher verwendet um einen Controller und die
Aktion zu funden zu der verwiesen wird.
Note: Spezielle Variablen
Die Namen dieser speziellen Variablen kann unterschiedlich sein wenn entschieden
wird die Standards in Zend_Controller_Request_Http mithilfe
der setControllerKey() und
setActionKey() Methode zu Ändern.
Standard Routen
Zend_Controller_Router_Rewrite kommt mit einer Standard Route
vorkonfiguriert, welche URIs im Sinn von
controller/action entspricht. Zusätzlich kann ein Modul Name als
erstes Pfad Element definiert werden, welches URIs in der Form von
module/controller/action erlaubt. Letztendlich wird es auch allen
zusätzlichen Parametern entsprechen die der URI standardmäßig
hinzugefügt wurden - controller/action/var1/value1/var2/value2.
Einige Beispiele wie solche Routen verglichen werden:
// Folgende Annahme:
'default' => '/path/to/default/controllers',
'news' => '/path/to/news/controllers',
'blog' => '/path/to/blog/controllers'
)
);
Nur Modul:
http://example/news
//example/foo
controller == foo
Modul + Controller:
http://example/blog/archive
//example/blog/archive/list
//example/blog/archive/list/sort/alpha/date/desc
Die Standardroute ist einfach ein
Zend_Controller_Router_Route_Module Objekt welches unter dem
Namen (Index) 'default' im RewriteRouter gespeichert ist. Es wird mehr oder weniger wie
folgt erstellt:
span style="color: #ff0000;">'default', $compat);
Wenn diese spezielle Standard Route im eigenen Routing Schema nicht gewünscht ist, kann
Sie durch Erstellung einer eigenen 'default' Route überschrieben werden (z.B. durch
Speichern unter dem Namen 'default') oder dem kompletten Entfernen durch verwenden von
removeDefaultRoutes():
// Löschen aller Standard Routen
Basis URL und Unterverzeichnisse
Der Rewrite Router kann in Unterverzeichnissen verwendet werden (z.B.
http://domain.com/user/application-root/) und in diesem Fall
sollte die Basis URL der Anwendung
(/user/application-root) automatisch durch
Zend_Controller_Request_Http erkannt und auch verwendet werden.
Sollte die Basis URL nicht richtig erkannt werden kann diese mit
eigenen Basispfad überschrieben werden durch Verwendung von
Zend_Controller_Request_Http und Auruf der
setBaseUrl() Methode (siehe Basis URL und
Unterverzeichnisse):
span style="color: #ff0000;">'/~user/application-root/');
Globale Parameter
Man kann in einem Router globale Parameter setzen die der Route automatisch zur
Verfügung stehen wenn Sie durch setGlobalParam() eingefügt
werden. Wenn ein globaler Parameter gesetzt ist, aber auch direkt an die Assemble
Methode gegeben wird, überschreibt der Benutzer-Parameter den Globalen-Parameter.
Globale Parameter können auf folgendem Weg gesetzt werden:
span style="color: #ff0000;">'lang', 'en');
Router Typen
Zend_Controller_Router_Route
Zend_Controller_Router_Route ist die standardmäßige Framework Route.
Sie kombiniert einfache Verwendung mit einer flexiblen Routendefinition. Jede Route besteht
primär aus URL Übereinstimmungen (von statischen und dynamischen Teilen
(Variablen)) und kann mit Standardwerten initialisiert werden wie auch mit variablen
Notwendigkeiten.
Angenommen unsere fiktive Anwendung benötigt eine informelle Seite über den Seitenauthor. Es
soll möglich sein mit dem Browser auf http://domain.com/author/martel
zu verweisen um die Informationen über diesen "martel" Typ zu sehen. Und die Route für so
eine Funktionalität würde so aussehen:
span style="color: #ff0000;">'author/:username''controller' => 'profile',
'action' => 'userinfo'
)
);
$router->addRoute('user', $route);
Der ersten Parameter im Konstruktor von
Zend_Controller_Router_Route ist eine Routendefinition die einer
URL entspricht. Routendefinitionen bestehen aus statischen und
dynamischen Teilen die durch einen Schrägstrich ('/') seperiert sind. Statische Teile sind
nur einfacher Text: author. Dynamische Teile, Variablen genannt, werden
durch einen vorangestellten Doppelpunkt, zum variablen Namen, markiert:
:username.
Note: Zeichen verwenden
Die aktuelle Implementation erlaubt die Verwendung von allen Zeichen (außer einem
Schrägstrich) als variablen Identifikator, aber es wird dringend empfohlen das nur
Zeichen verwendet werden die auch für PHP Veriablen Identifikatoren
gültig sind. Zukünftige Implementationen können dieses Verhlaten ändern, was zu
versteckten Bugs im eigenen Code führen würde.
Diese Beispielroute wird verglichen wenn der Browser auf
http://domain.com/author/martel zeigt. In diesem Fall werden alle seine
Variablen dem Zend_Controller_Request Objekt injiziiert und es kann
im ProfileController darauf zugegriffen werden. Variablen die von
diesem Beispiel zurückgegeben werden können als Array mit den folgenden Schlüssel- und
Wertepaaren repräsentiert werden:
span style="color: #ff0000;">'username' => 'martel',
'controller' => 'profile',
'action' => 'userinfo'
);
Später sollte Zend_Controller_Dispatcher_Standard die
userinfoAction() Methode der eigenen
ProfileController Klasse aufrufen (im Standardmodul) basierend auf
diesen Werten. Dort ist es möglich alle Variablen durch die
Zend_Controller_Action::_getParam() oder
Zend_Controller_Request::getParam() Methoden zuzugreifen:
span style="color: #ff0000;">'username''username');
}
Eine Routendefinition kann ein weiteres spezielles Zeichen enthalten - eine Wildcard -
dargestellt durch ein '*' Symbol. Es wird verwendet um Parameter genauso wie im standard
Modulrouter zu erhalten (var => Wertepaare definiert in der URI). Die
folgende Route imitiert mehr oder weniger das Verhalten des Modulrouters:
span style="color: #ff0000;">':module/:controller/:action/*''module' => 'default')
);
$router->addRoute('default', $route);
Variable Standards
Jede Variable im Router kann einen Standardwert haben und das ist für was der zweite
Parameter des Konstruktors von Zend_Controller_Router_Route
verwendet wird. Dieser Parameter ist ein Array mit Schlüsseln die Variablennamen
repräsentieren und mit Werten als gewünschte Standards:
span style="color: #ff0000;">'archive/:year''year' => 2006)
);
$router->addRoute('archive', $route);
Die obige Route entspricht URLs wie
http://domain.com/archive/2005 und
http://example.com/archive. Im späteren Fall wird die Variable year
einen initialen Standardwert von 2006 haben.
Dieses Beispiel resultiert darin das eine year Variable in das Anfrage Objekt injiziiert
wird. Da keine Routinginformation vorhanden ist (es sind keine Controller und
Aktionsparameter definiert), wird die Anwendung zum Standardcontroller und der
Aktionsmethode (welche beide in
Zend_Controller_Dispatcher_Abstract definiert sind)
weitergeleitet. Um es verwendbarer zu machen muß ein gültiger Controller und eine
gültige aktion als Standard für die Route angegeben werden:
span style="color: #ff0000;">'archive/:year''year' => 2006,
'controller' => 'archive',
'action' => 'show'
)
);
$router->addRoute('archive', $route);
Diese Route führt dazu das an die Methode showAction() der
Klasse ArchiveController weitergeleitet wird.
Variable Anforderungen
Man kann einen dritten Parameter dem Zend_Controller_Router_Route
Konstruktor hinzufügen wo variable Anforderungen gesetzt werden können. Diese werden als
Teil eines regulären Ausdrucks definiert:
span style="color: #ff0000;">'archive/:year''year' => 2006,
'controller' => 'archive',
'action' => 'show''year' => '\d+')
);
$router->addRoute('archive', $route);
Mit einer Route die wie oben definiert ist, wird das Routing nur dann stattfinden wenn
die year Variable nummerische Daten enthält, z.B.
http://domain.com/archive/2345. Eine URL wie
http://example.com/archive/test wird nicht zugeordnet und die
Kontrolle wird stattdessen an die nächste Route in der Kette übertragen.
Übersetzte Segmente
Die Standardroute unterstützt übersetzte Segmente. Um dieses Feature zu verwenden muß
zumindest ein Übersetzer (eine Instanz von Zend_Translate) auf
einem der folgenden Wege definiert werden:
-
In die Registry mit dem Schlüssel Zend_Translate geben.
-
Über die statische Methode
Zend_Controller_Router_Route::setDefaultTranslator()
setzen.
-
Als vierten Parameter im Constructor übergeben.
Standardmäßig wird das Gebietsschema verwendet das in der Instanz von
Zend_Translate verwendet wird. Um es zu überschreiben, kann es
(als Instanz von Zend_Locale oder einem Gebietsschema-String) auf
einem der folgenden Wege gesetzt werden:
-
In die Registry mit dem Schlüssel Zend_Locale geben.
-
Über die statische Methode
Zend_Controller_Router_Route::setDefaultLocale()
setzen.
-
Als fünften Parameter im Constructor übergeben.
-
Als @locale Parameter der assemble Methode übergeben.
Übersetzte Segmente werden in zwei Teile getrennt. Ein fixes Segment dem ein einzelnes
@-Zeichen vorangestellt wird, der anhand des aktuellen
Gebietsschemas übersetzt wird und auf der Position des Parameters eingefügt wird.
Dynamischen Segmenten wird ein :@ vorangestellt. Beim Zusammenbauen,
wird der gegebene Parameter übersetzt und an der Position des Parameters eingefügt. Bei
der Überprüfung, wird der übersetzte Parameter von der URL wieder in
die Nachrichten ID umgewandelt.
Note: Nachrichten IDs und eigene Sprachdateien
Normalerweise werden Nachrichten IDs die man in einer seiner Routen verwenden will,
bereits in einem View Skript oder irgendwo anders verwendet. Um die komplette
Kontrolle über sichere URLs zu haben, sollte man eine eigene
Sprachdatei für die Nachrichten haben die in einer Route verwendet werden.
Nachfolgend ist die einfachste Verwendung gezeigt um eine Standardroute für übersetzte
Segmente zu Verwenden:
// Den Übersetzer vorbereiten
'adapter' => 'array',
'content''locale' => 'en''content''archive' => 'archiv',
'year' => 'jahr',
'month' => 'monat',
'index' => 'uebersicht'
),
'locale' => 'de'
)
);
// Das aktuelle Gebietsschema für den Übersetzer setzen
'en');
// Als Standard-Übersetzer für Routen setzen
Dieses Beispiel zeigt die Verwendung von statischen Segmenten:
// Die Route erstellen
'@archive''controller' => 'archive',
'action' => 'index'
)
);
$router->addRoute('archive', $route);
// Die URL im Standard-Gebietsschema zusammenbauen: archive
// Die URL in Deutsch zusammenbauen: archiv
Man kann dynamische Segmente verwenden um eine Modul-Route, so wie die übersetzte
Version, zu erstellen:
// Die Route erstellen
':@controller/:@action/*''controller' => 'index',
'action' => 'index'
)
);
$router->addRoute('archive', $route);
// Die URL im Standard-Gebietsschema zusammenbauen: archive/index/foo/bar
'controller' => 'archive', 'foo' => 'bar'));
// Die URL in Deutsch zusammenbauen: archiv/uebersicht/foo/bar
'controller' => 'archive', 'foo' => 'bar'));
Man kann auch statische und dynamische Segmente mischen:
// Die Route erstellen
'@archive/:@mode/:value''mode' => 'year'
'value' => 2005,
'controller' => 'archive',
'action' => 'show''mode' => '(month|year)'
'value' => '\d+')
);
$router->addRoute('archive', $route);
// Die URL im Standard-Gebietsschema zusammenbauen: archive/month/5
'mode' => 'month', 'value' => '5'));
// Die URL in Deutsch zusammenbauen: archiv/monat/5
'mode' => 'month', 'value' => '5', '@locale' => 'de'));
Zend_Controller_Router_Route_Static
Die oben angeführten Beispiele verwenden alle dynamische Routen -- Routen die einem Pattern
entsprechen. Trotzdem wird manchmal eine spezielle Route in Stein gegossen, und das Starten
der Regular Expression Maschine wäre ein Overkill. Die Lösung zu dieser Situation ist die
Verwendung von statischen Routen:
span style="color: #ff0000;">'login''controller' => 'auth', 'action' => 'login')
);
$router->addRoute('login', $route);
Die obige Route passt zu einer URL von
http://domain.com/login, und leitet weiter zu
AuthController::loginAction().
Note: Warnung: Statische Routen müssen vernüftige Standards enthalten
Da eine statische Route keinen Teil der URL an das Requestobjekt als
Parameter übergibt, muss man alle Parameter die für das Bearbeiten
eines Requests notwendig sind als Standards an die Route übergeben. Das unterdrücken der
Standardwerte von "controller" oder "action" kann zu unerwarteten Ergebnissen führen,
und wird dazu führen das der Request nicht bearbeitet werden kann.
Als Daumenregel sollte man immer jeden der folgenden Standardwerte anbieten:
controller
action
module (if not default)
Optional kann auch der "useDefaultControllerAlways" Parameter an den Frontcontroller
während des Bootstrappings übergeben werden:
$front->setParam('useDefaultControllerAlways'
Trotzdem ist das als Workaround anzusehen; es ist immer besser vernünftige Standards
explizit zu definieren.
Zend_Controller_Router_Route_Regex
Zusätzlich zu den standard statischen Routetypen, ist ein Regular Expression Routetyp
vorhanden. Diese Route bietet mehr Power und Flexibilität als die anderen, aber auf leichten
Kosten von Komplexität. Wärend der selben Zeit, sollte Sie schneller als die Standardroute
sein.
Wie die Standardroute, muß diese Route mit einer Routendefinition und einigen Standardwerten
initialisiert werden. Lasst uns eine Archivroute als Beispiel erstellen, ähnlich der zuletzt
definierten, nur das dieses Mal die Regex Route verwendet wird:
span style="color: #ff0000;">'archive/(\d+)''controller' => 'archive',
'action' => 'show'
)
);
$router->addRoute('archive', $route);
Jedes definierte Regex Subpattern wird in das Anfrageobjekt injiziiert. Mit dem obigen
Beispiel, nachdem http://domain.com/archive/2006 erfolgreich geprüft
wurde, kann das resultierende Wertearray so aussehen:
span style="color: #ff0000;">'2006',
'controller' => 'archive',
'action' => 'show'
);
Note:
Führende und folgende Schrägstriche werden von der URL im Router vor
dem Vergleich entfernt. Als Ergebnis, wird ein Vergleich der URL
http://domain.com/foo/bar/, ein Regex von
foo/bar inkludieren, aber nicht /foo/bar.
Note:
Zeilenbeginn und Endanker (normalerweise '^' und '$') werden automatisch allen
Ausdrücken vor- und nachgesetzt. Deswegen sollten Sie nicht in den Regular Expressions
verwendet werden, und der komplette String sollte entsprechen.
Note:
Diese Routeklasse verwendet das '#' Zeichen als Begrenzer. Das
bedeutet das ein Hashzeichen ('#') kommentiert werden muß aber keine Schrägstriche ('/')
in der Routendefinition. Da das '#' Zeichen (Anker genannt) selben an einen Webserver
übergeben wird, wird man dieses Zeichen selten in der eigenen regex verwenden.
Die Inhalte von definierten Subpattern können auf dem üblichen Weg bekommen werden:
span style="color: #808080; font-style: italic;">// $year = '2006';
}
Note:
Beachte das der Schlüssel ein Integer ist (1) anstatt ein String ('1').
Diese Route wird jetzt noch nicht exakt gleich wie Ihr Gegenspieler, die Standardroute,
arbeiten da der Standard für 'year' noch nicht gesetzt ist. Und was jetzt noch nicht
offensichtlich ist, ist das wir ein Problem mit endenden Schrägstrichen haben, selbst wenn
wir einen Standard für das Jahr definieren und das Subpattern optional machen. Die Lösung
ist, den ganzen year Teil optional zu nachen zusammen mit dem Schrägstrich, aber nur den
nummerischen Teil zu holen:
span style="color: #ff0000;">'archive(?:/(\d+))?''2006',
'controller' => 'archive',
'action' => 'show'
)
);
$router->addRoute('archive', $route);
Jetzt betrachten wir das Problem das möglicherweise schon selbst gefunden wurde. Die
Verwendung von Integer basierten Schlüsseln für Parameter ist keine einfach zu handhabende
Lösung und kann, während einer langen Laufzeit, potentiell problematisch sein. Hier kommt der
dritte Parameter ins Spiel. Dieser Parameter ist ein assoziatives Array das einer Karte von
Regex Subpatterns zu Parametern benannten Schlüsseln entspricht. Betrachten wir ein
einfacheres Beispiel:
span style="color: #ff0000;">'archive/(\d+)''controller' => 'archive',
'action' => 'show''year'
)
);
$router->addRoute('archive', $route);
Als Ergebnis werden die folgenden Werte in die Anfrage injiziiert:
span style="color: #ff0000;">'year' => '2006',
'controller' => 'archive',
'action' => 'show'
);
Die Karte kann in jede Richtung definiert werden damit Sie in jeder Umgebung funktioniert.
Schlüssel können Variablennamen oder Indezes von Subpattern enthalten:
span style="color: #ff0000;">'archive/(\d+)''year')
);
// ODER
'archive/(\d+)''year' => 1)
);
Note:
Schlüssel von Subpattern müssen durch Integer repräsentiert werden.
Es gilt zu beachten das der nummerische Index in den Anfragewerten jetzt weg ist und eine
benannte Variable statt Ihm angezeigt wird. Natürlich können nummerische und benannte
Variablen gemischt werden wenn das gewünscht ist:
span style="color: #ff0000;">'archive/(\d+)/page/(\d+)''year' => 1)
);
Das führt zu gemischten Werten die in der Anfrage vorhanden sind. Als Beispiel, wird die
URL http://domain.com/archive/2006/page/10 zu
folgenden Werte führen:
span style="color: #ff0000;">'year' => '2006',
2 => 10,
'controller' => 'archive',
'action' => 'show'
);
Da Regex Patterns nicht einfach rückgängig zu machen sind, muß eine umgekehrte
URL vorbereitet werden wenn ein URL Helfer verwendet
werden soll oder sogar eine Herstellungsmethode dieser Klasse. Dieser umgekehrte Pfad wird
durch einen String dargestellt der durch sprintf() durchsucht
werden kann und als vierter Parameter definiert wird:
span style="color: #ff0000;">'archive/(\d+)''year' => 1),
'archive/%s'
);
Da all das bereits etwas ist das durch die Bedeutung eines standardmäßigen Route Objektes
möglich ist kommt natürlich die Frage aus worin der Vorteil einer regex Route besteht?
Primär erlaubt Sie jeden Typ von URL zu beschreiben ohne irgendwelche
Einschränkungen. Angenommen man hat einen Blog und will eine URL wie die
folgende erstellen:
http://domain.com/blog/archive/01-Using_the_Regex_Router.html, und muß
das jetzt Pfad Element 01-Using_the_Regex_Router.html bearbeiten, in
eine Artikel ID und eine Artikel Titel oder Beschreibung; das ist nicht möglich mit der
Standardroute. Mit der Regex Route ist etwas wie die folgende Lösung möglich:
span style="color: #ff0000;">'blog/archive/(\d+)-(.+)\.html''controller' => 'blog',
'action' => 'view''id',
2 => 'description'
),
'blog/archive/%d-%s.html'
);
$router->addRoute('blogArchive', $route);
Wie man sieht, fügt das ein enormes Potential von Flexibilität zur Stnadardroute hinzu.
Zend_Controller_Router_Route_Hostname
Zend_Controller_Router_Route_Hostname ist die Hostname Route des
Frameworks. Sie arbeitet ähnlich wie die Standardrouten, aber Sie arbeitet an und mit dem
Hostnamen der aufgerufenen URL statt mit dem Pfad.
Verwenden wir also ein Beispiel von der Standardroute und schauen wir uns an wie Sie auf
einem Hostnamen basierenden Weg aussehen würde. Statt das der Benutzer über einen Pfad
aufgerufen wird, wollen wir das der Benutzer
http://martel.users.example.com aufrufen kann, um die Informationen
über den Benutzer "martel" zu sehen:
span style="color: #ff0000;">':username.users.example.com''controller' => 'profile',
'action' => 'userinfo''');
$router->addRoute('user', $hostnameRoute->chain($plainPathRoute);
Der erste Parameter in Konstruktor von
Zend_Controller_Router_Route_Hostname ist eine Routerdefinition die
zu einem Hostnamen passt. Routerdefinitionen bestehen aus statischen und dynamischen Teilen
die durch ein Punkt ('.') Zeichen getrennt sind. Dynamische Teile, genannt Variablen, werden
durch einen, dem Variablennamen vorangestellten Doppelpunkt, gekennzeichnet:
:username. Statische Teile sind nur einfacher Text:
user.
Hostnamerouten können verwendet werden wie sie sind, sollten es aber nie. Der Grund dahinter
ist, das Hostnamerouten alleine jedem Pfad entsprechen würden. Was man also tun muß, ist
eine Pfadroute an die Hostnameroute zu ketten. Das wird, wie im Beispiel, getan indem
$hostnameRoute->chain($pathRoute); aufgerufen wird. Indem das getan
wird, wird $hostnameRoute nicht geändert, aber eine neue Route
(Zend_Controller_Router_Route_Chain), welche dann dem Router
übergeben werden kann, wird zurückgegeben.
Zend_Controller_Router_Route_Chain
Zend_Controller_Router_Route_Chain ist eine Route die es erlaubt
mehrere Routen miteinander zu verketten. Das erlaubt es Hostname-Routen und Pfad-Routen zu
verketten, oder zum Beispiel mehrere Pfad-Routen. Verkettung kann entweder
program-technisch oder mit einer Konfigurationsdatei durchgeführt werden.
Note: Priorität der Parameter
Wenn Routen wie die Hostnameroute und die Pfadroute zusammengekettet werden, haben die
Parameter der Hostnameroute eine höhere Priorität als die Parameter der Pfadroute.
Deshalb wird, wenn man im Hostnamen und in der Pfadroute einen Controller definiert,
der Controller der Hostnameroute ausgewählt.
Wenn Programmtechnisch verkettet wird, gibt es zwei Wege das zu tun. Der erste besteht
darin eine neue Instanz von Zend_Controller_Router_Route_Chain zu
erstellen und dann die chain() Methode mehrere Male mit allen
Routen aufzurufen die zusammen verkettet werden sollen. Der andere Weg besteht darin die
erste Route zu nehmen, z.B. eine Hostname Route, und die chain()
Methode mit der Route auf Ihr aufzurufen, die angehängt werden soll. Das verändert die
Hostname Route nicht, gibt aber eine neue Instanz von
Zend_Controller_Router_Route_Chain zurück, die dann beide Routen
verkettet hat:
// Erstellung zweier Routen
// Erster Weg, mit Verkettung über die Chain Route
// Zweiter Weg, direkt verketten
$chainedRoute = $hostnameRoute->chain($pathRoute);
Wenn Routen miteinander verkettet werden, ist Ihr Trennzeichen ein
Schrägstrich. Es kann Fälle geben in denen man ein anderes Trennzeichen verwenden will:
// Zwei Routen erstellen
'foo''bar');
// Sie mit einem anderen Trennzeichen miteinander verketten
$chainedRoute = $firstRoute->chain($secondRoute, '-');
// Zusammenbauen der Route: "foo-bar"
Verkettete Routen über Zend_Config
Um Routen in einer Config Datei miteinander zu verketten gibt es zusätzliche Parameter
für die Konfiguration von Ihnen. Der einfachere Weg ist die Verwendung des
chains Parameters. Dieser ist einfach eine Liste von Routen, die
mit der Eltern-Route verkettet werden. Weder die Eltern-, noch die Kind-Routen werden
dem Router direkt hinzugefügt sondern nur die resultierende verkettete Route. Der Name
der verketteten Route im Router ist standardmäßig der Name der Eltern-Route und der Name
der Kind-Route verbunden mit einem Bindestrich (-). Eine einfache Konfiguration würde
in XML wie folgt aussehen:
<routes>
<www type="Zend_Controller_Router_Route_Hostname">
<route>www.example.com</route>
<chains>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
<chains>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index"
action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index"
action="index" />
</imprint>
</chains>
</language>
</chains>
</www>
<users type="Zend_Controller_Router_Route_Hostname">
<route>users.example.com</route>
<chains>
<profile type="Zend_Controller_Router_Route">
<route>:username</route>
<defaults module="users" controller="profile" action="index" />
</profile>
</chains>
</users>
<misc type="Zend_Controller_Router_Route_Static">
<route>misc</route>
</misc>
</routes>
Das führt zu den drei Routen www-language-index,
www-language-imprint und users-language-profile
die nur basierend auf dem Hostnamen und der Route misc passen, was
wiederum mit jedem Hostnamen passt.
Der alternative Weg der Erstellung einer verketteten Route ist der über den
chain Parameter, was wiederum nur mit dem Chain-Route Typ direkt
verwendet werden kann, und auch im Root Level funktioniert:
<routes>
<www type="Zend_Controller_Router_Route_Chain">
<route>www.example.com</route>
</www>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
</language>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index" action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index" action="index" />
</imprint>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www, language, index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www, language, imprint</chain>
</www-imprint>
</routes>
Man kann auch den chain Parameter als Array übergeben statt die
Routen mit einem Komma zu seperieren:
<routes>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>imprint</chain>
</www-imprint>
</routes>
Wenn man Chain-Routen mit Zend_Config konfiguriert und will dass
das Trennzeichen ein anderes als ein Unterstrich ist, dann muss man dises Trennzeichen
separat spezifizieren:
span style="color: #ff0000;">'chainName''type' => 'Zend_Controller_Router_Route_Static',
'route' => 'foo',
'chains''subRouteName''type' => 'Zend_Controller_Router_Route_Static',
'route' => 'bar',
'defaults''module' => 'module',
'controller' => 'controller',
'action' => 'action'
)
)
)
)
));
// Das Trennzeichen vor dem hinzufügen der Config setzen
$router->setChainNameSeparator('_separator_')
// Config hinzufügen
$router->addConfig($config);
// Der Name unserer Route ist jetzt: chainName_separator_subRouteName
'chainName_separator_subRouteName');
// Die Prüfung: Ausgegeben wird /foo/bar
Zend_Rest_Route
Die Komponente Zend_Rest enthält eine RESTvolle Route für
Zend_Controller_Router_Rewrite. Diese Route bietet ein
standardisiertes Routing Schema das Routinganfragen durch Übersetzung der
HTTP Methode und der URI zu einem Modul,
Controller und einer Action. Die unten stehende Tabelle bietet eine Übersicht darüber
wie Anfragemethoden und URI's geroutet werden.
Verhalten von Zend_Rest_Route
Methode |
URI |
Module_Controller::action |
GET |
/product/ratings/ |
Product_RatingsController::indexAction() |
GET |
/product/ratings/:id |
Product_RatingsController::getAction() |
POST |
/product/ratings |
Product_RatingsController::postAction() |
PUT |
/product/ratings/:id |
Product_RatingsController::putAction() |
DELETE |
/product/ratings/:id |
Product_RatingsController::deleteAction()
|
POST |
/product/ratings/:id?_method=PUT |
Product_RatingsController::putAction() |
POST |
/product/ratings/:id?_method=DELETE |
Product_RatingsController::deleteAction()
|
Verwendung von Zend_Rest_Route
Um Zend_Rest_Route für eine komplette Anwendung einzuschalten
muss diese ohne Konfigurationsparameter erstellt und als Standardroute dem
Frontcontroller hinzugefügt werden:
span style="color: #ff0000;">'default', $restRoute);
Note:
Wenn Zend_Rest_Route keinem gültigen Modul, Controller oder
keiner Action entspricht gibt diese FALSE zurück und der Router
versucht eine Entsprechung zu finden indem die nächste Route im Router verwendet
wird.
Um Zend_Rest_Route für spezielle Module einzuschalten muss diese
mit einem Array von Modulnamen als 3tes Argument des Constructors erstellt werden:
span style="color: #ff0000;">'product'));
$front->getRouter()->addRoute('rest', $restRoute);
Um Zend_Rest_Route für spezielle Controller einzuschalten muss
ein Array von Controllernamen als Wert für jedes Modul (Arrayelement) hinzugefügt
werden.
span style="color: #ff0000;">'product''ratings')
));
$front->getRouter()->addRoute('rest', $restRoute);
Zend_Rest_Route mit Zend_Config_Ini
Um Zend_Rest_Route von einer INI
Konfigurationsdatei aus zu verwenden muss man den "route" Typ Parameter verwenden und
die Konfigurationsoptionen setzen:
routes.rest.type = Zend_Rest_Route
routes.rest.defaults.controller = object
routes.rest.mod = project,user
Die 'type' Option benennt den RESTvollen Routing Konfigurationstyp. Die 'defaults'
Option wird verwendet um gemeinsame Standardmodule zu spezifizieren, und oder Aktionen
für die Route. Alle anderen Optionen in der Konfigurationsgruppe werden als RESTvolle
Modulnamen behandelt, und deren Werte sind RESTvolle Kontrollernamen. Die beispielhafte
Konfiguration definiert Mod_ProjectController und
Mod_UserController als RESTvolle Controller.
Dann ist die addConfig() Methode des Rewrite Router Objekts zu
verwenden:
span style="color: #ff0000;">'path/to/routes.ini''routes');
Zend_Rest_Controller
Um bei der Entwicklung von Controllern zu Hilfe zu sein die mit
Zend_Rest_Route verwendet werden, müssen die Controller von
Zend_Rest_Controller erweitert werden.
Zend_Rest_Controller definiert die 5 am meisten benötigten
Operationen für RESTvolle Ressourcen in der Form von abstrakten Actionmethoden.
-
indexAction() -
Sollte einen Index von Ressourcen empfangen und diese mit der View
verknüpfen.
-
getAction() -
Sollte eine einzelne Ressource empfangen die von einer URI
identifiziert wird und diese mit der Vew verknüpfen.
-
postAction() -
Sollte eine einzelne neue Ressource akzeptieren und dessen Status persistent
machen.
-
putAction() -
Sollte eine einzelne Ressource akzeptieren die von einer URI
identifiziert wird und dessen Status persistent machen.
-
deleteAction() -
Sollte eine einzelne Ressource löschen die von einer URI
identifiziert wird.
Zend_Config mit dem RewriteRouter verwenden
Manchmal ist es praktischer, eine Konfigurationsdatei mit neuen Routen zu
aktualisieren, als den Code zu ändern. Dies ist mit Hilfe der
addConfig() Methode möglich. Im Wesentlichen kann man eine
Zend_Config kompatible Konfiguration erstellen, in seinem Code
einlesen und an den RewriteRouter übergeben:
Als Beispiel wird die folgende INI Datei angenommen:
[production]
routes.archive.route = "archive/:year/*""\d+""Zend_Controller_Router_Route_Static""news""news""list"
routes.archive.type = "Zend_Controller_Router_Route_Regex"
routes.archive.route = "archive/(\d+)""archive""show"
routes.archive.map.1 = "year"
; OR: routes.archive.map.year = 1
Die oben angeführte INI Datei kann dann wie folgt in ein
Zend_Config Objekt eingelesen werden:
span style="color: #ff0000;">'/path/to/config.ini', 'production''routes');
Im oberen Beispiel teilen wir dem Router mit, den 'routes' Bereich der
INI Datei für seine Routen zu verwenden. Jeder Schlüssel auf erster
Ebene in diesem Bereich wird verwendet, um den Namen der Routen zu definieren; das obige
Beispiel definiert die Routen 'archive' und 'news'. Jede Route erfordert dann mindestens
einen 'route' Eintrag und einen oder mehrere 'defaults' Einträge; optional können eine
oder mehrere 'reqs' (kurz für 'required', d.h. erforderlich) Einträge angegeben werden.
Alles in allem entspricht dies den drei Argumenten, die an ein
Zend_Controller_Router_Route_Interface Objekt übergeben werden.
Ein Optionsschlüssel 'type' kann verwendet werden, um den Typ der Routenklasse für
diese Route anzugeben; standardmäßig wird
Zend_Controller_Router_Route verwendet. Im obigen Beispiel wird
die 'news' Route definiert, um
Zend_Controller_Router_Route_Static zu verwenden.
Erben vom Router
Der Standard Rewrite Router sollte die meisten Funktionalitäten die benötigt werden zur
Verfügung stellen; meistens wird es nur notwendig sein einen neuen Router Typen zu
erstellen um neue oder modifizierte Funktionalitäten für die verfügbaren Routen zu
bieten.
So gesehen, wird man in einigen Fällen ein anderes Routing Paradigma verwenden wollen.
Das Interface Zend_Controller_Router_Interface bietet die
minimalen Information die benötigt werden um einen Router er erstellen und besteht aus
einer einzigen Methode.
span style="color: #808080; font-style: italic;">/**
* @param Zend_Controller_Request_Abstract $request
* @throws Zend_Controller_Router_Exception
* @return Zend_Controller_Request_Abstract
*/
Das Routing findet nur einmal statt, wenn die Anfrage das erste Mal vom System erhalten
wird. Der Zweck des Routers ist es, Controller, Aktion und optionale Parameter auf Basis
der Anfrageumgebung zu ermitteln und im Request zu setzen. Das Request Objekt wird dann
an den Dispatcher übergeben. Wenn es nicht möglich ist, eine Route auf einen Dispatch
Token abzubilden, soll der Router nichts mit dem Request Objekt machen.
|
|