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.
RewriteEngine on RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
oder (bevorzugt):
RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ index.php [NC,L]
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:
RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]
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" )
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', new Zend_Controller_Router_Route('user/:username', array('controller' => 'user', 'action' => 'info')) );
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', new Zend_Controller_Router_Route('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.
Anmerkung
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.
Umgekehrter Vergleich
Routen werden in umgekehrter Reihenfolge verglichen. Deswegen muß sichergestellt werden das die generellen Routen zuerst definiert werden.
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.
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.
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: $ctrl->setControllerDirectory( array( 'default' => '/path/to/default/controllers', 'news' => '/path/to/news/controllers', 'blog' => '/path/to/blog/controllers' ) ); Nur Modul: http://example/news module == news Ungültiges Modul, geht an den Controller Namen: http://example/foo controller == foo Modul + Controller: http://example/blog/archive module == blog controller == archive Modul + Controller + Aktion: http://example/blog/archive/list module == blog controller == archive action == list Modul + Controller + Aktion + Parameter: http://example/blog/archive/list/sort/alpha/date/desc module == blog controller == archive action == 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:
$compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request); $this->addRoute('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 $router->removeDefaultRoutes();
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):
$request->setBaseUrl('/~user/application-root/');
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:
$router->setGlobalParam('lang', 'en');
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:
$route = new Zend_Controller_Router_Route( 'author/:username', array( '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.
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:
$values = array( '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:
public function userinfoAction() { $request = $this->getRequest(); $username = $request->getParam('username'); $username = $this->_getParam('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:
$route = new Zend_Controller_Router_Route( ':module/:controller/:action/*', array('module' => 'default') ); $router->addRoute('default', $route);
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:
$route = new Zend_Controller_Router_Route( 'archive/:year', array('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:
$route = new Zend_Controller_Router_Route( 'archive/:year', array( '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.
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:
$route = new Zend_Controller_Router_Route( 'archive/:year', array( 'year' => 2006, 'controller' => 'archive', 'action' => 'show' ), array('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.
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.
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 $translator = new Zend_Translate( array( 'adapter' => 'array', 'content' => array(), 'locale' => 'en' ) ); $translator->addTranslation( array( 'content' => array( 'archive' => 'archiv', 'year' => 'jahr', 'month' => 'monat', 'index' => 'uebersicht' ), 'locale' => 'de' ) ); // Das aktuelle Gebietsschema für den Übersetzer setzen $translator->setLocale('en'); // Als Standard-Übersetzer für Routen setzen Zend_Controller_Router_Route::setDefaultTranslator($translator);
Dieses Beispiel zeigt die Verwendung von statischen Segmenten:
// Die Route erstellen $route = new Zend_Controller_Router_Route( '@archive', array( 'controller' => 'archive', 'action' => 'index' ) ); $router->addRoute('archive', $route); // Die URL im Standard-Gebietsschema zusammenbauen: archive $route->assemble(array()); // Die URL in Deutsch zusammenbauen: archiv $route->assemble(array());
Man kann dynamische Segmente verwenden um eine Modul-Route, so wie die übersetzte Version, zu erstellen:
// Die Route erstellen $route = new Zend_Controller_Router_Route( ':@controller/:@action/*', array( 'controller' => 'index', 'action' => 'index' ) ); $router->addRoute('archive', $route); // Die URL im Standard-Gebietsschema zusammenbauen: archive/index/foo/bar $route->assemble(array('controller' => 'archive', 'foo' => 'bar')); // Die URL in Deutsch zusammenbauen: archiv/uebersicht/foo/bar $route->assemble(array('controller' => 'archive', 'foo' => 'bar'));
Man kann auch statische und dynamische Segmente mischen:
// Die Route erstellen +$route = new Zend_Controller_Router_Route( '@archive/:@mode/:value', array( 'mode' => 'year' 'value' => 2005, 'controller' => 'archive', 'action' => 'show' ), array('mode' => '(month|year)' 'value' => '\d+') ); $router->addRoute('archive', $route); // Die URL im Standard-Gebietsschema zusammenbauen: archive/month/5 $route->assemble(array('mode' => 'month', 'value' => '5')); // Die URL in Deutsch zusammenbauen: archiv/monat/5 $route->assemble(array('mode' => 'month', 'value' => '5', '@locale' => 'de'));
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:
$route = new Zend_Controller_Router_Route_Static( 'login', array('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()
.
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', true);
Trotzdem ist das als Workaround anzusehen; es ist immer besser vernünftige Standards explizit zu definieren.
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:
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( '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:
$values = array( 1 => '2006', 'controller' => 'archive', 'action' => 'show' );
Anmerkung
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
.
Anmerkung
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.
Anmerkung
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:
public function showAction() { $request = $this->getRequest(); $year = $request->getParam(1); // $year = '2006'; }
Anmerkung
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:
$route = new Zend_Controller_Router_Route_Regex( 'archive(?:/(\d+))?', array( 1 => '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:
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( 'controller' => 'archive', 'action' => 'show' ), array( 1 => 'year' ) ); $router->addRoute('archive', $route);
Als Ergebnis werden die folgenden Werte in die Anfrage injiziiert:
$values = array( '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:
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( ... ), array(1 => 'year') ); // ODER $route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( ... ), array('year' => 1) );
Anmerkung
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:
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)/page/(\d+)', array( ... ), array('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:
$values = array( '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:
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( ... ), array('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:
$route = new Zend_Controller_Router_Route_Regex( 'blog/archive/(\d+)-(.+)\.html', array( 'controller' => 'blog', 'action' => 'view' ), array( 1 => '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
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:
$hostnameRoute = new Zend_Controller_Router_Route_Hostname( ':username.users.example.com', array( 'controller' => 'profile', 'action' => 'userinfo' ) ); $plainPathRoute = new Zend_Controller_Router_Route_Static(''); $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
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.
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 $hostnameRoute = new Zend_Controller_Router_Route_Hostname(...); $pathRoute = new Zend_Controller_Router_Route(...); // Erster Weg, mit Verkettung über die Chain Route $chainedRoute = new Zend_Controller_Router_Route_Chain(); $chainedRoute->chain($hostnameRoute) ->chain($pathRoute); // 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 $firstRoute = new Zend_Controller_Router_Route('foo'); $secondRoute = new Zend_Controller_Router_Route('bar'); // Sie mit einem anderen Trennzeichen miteinander verketten $chainedRoute = $firstRoute->chain($secondRoute, '-'); // Zusammenbauen der Route: "foo-bar" echo $chainedRoute->assemble();
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:
$config = new Zend_Config(array( 'chainName' => array( 'type' => 'Zend_Controller_Router_Route_Static', 'route' => 'foo', 'chains' => array( 'subRouteName' => array( 'type' => 'Zend_Controller_Router_Route_Static', 'route' => 'bar', 'defaults' => array( '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 echo $this->_router->assemble(array(), 'chainName_separator_subRouteName'); // Die Prüfung: Ausgegeben wird /foo/bar
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.
Tabelle 40. 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()
|
Um Zend_Rest_Route
für eine komplette Anwendung einzuschalten
muss diese ohne Konfigurationsparameter erstellt und als Standardroute dem
Frontcontroller hinzugefügt werden:
$front = Zend_Controller_Front::getInstance(); $restRoute = new Zend_Rest_Route($front); $front->getRouter()->addRoute('default', $restRoute);
Anmerkung
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:
$front = Zend_Controller_Front::getInstance(); $restRoute = new Zend_Rest_Route($front, array(), array('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.
$front = Zend_Controller_Front::getInstance(); $restRoute = new Zend_Rest_Route($front, array(), array( 'product' => array('ratings') )); $front->getRouter()->addRoute('rest', $restRoute);
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:
$config = new Zend_Config_Ini('path/to/routes.ini'); $router = new Zend_Controller_Router_Rewrite(); $router->addConfig($config, 'routes');
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.
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/*" routes.archive.defaults.controller = archive routes.archive.defaults.action = show routes.archive.defaults.year = 2000 routes.archive.reqs.year = "\d+" routes.news.type = "Zend_Controller_Router_Route_Static" routes.news.route = "news" routes.news.defaults.controller = "news" routes.news.defaults.action = "list" routes.archive.type = "Zend_Controller_Router_Route_Regex" routes.archive.route = "archive/(\d+)" routes.archive.defaults.controller = "archive" routes.archive.defaults.action = "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:
$config = new Zend_Config_Ini('/path/to/config.ini', 'production'); $router = new Zend_Controller_Router_Rewrite(); $router->addConfig($config, '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.
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.
interface Zend_Controller_Router_Interface { /** * @param Zend_Controller_Request_Abstract $request * @throws Zend_Controller_Router_Exception * @return Zend_Controller_Request_Abstract */ public function route(Zend_Controller_Request_Abstract $request); }
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.