Klasa Zend_Controller została zbudowana w sposób bardzo elastyczny.
Można ją rozwijać rozszerzając klasy istniejące lub pisząc nowe klasy implementujące
interfejsy Zend_Controller_Router_Interface oraz Zend_Controller_Dispatcher_Interface.
Powodami dla których warto implementować nowy router lub dispatcher mogą być:
Istniejący w Zend Framework system routingu URI nie jest kompatybilny. Np. gdy chcemy go zintegrować z istniejącą witryną która używa swoich własnych konwencji routingu, które nie są kompatybilne z mechanizmem routingu dostarczanym przez Zend Framework.
Potrzebujesz zaimplementować routing dla czegoś zupełnie innego. Klasa Zend_Controller_Router
działa jedynie z adresami URI. Jest prawdopodobne że chciałbyś użyć wzorca MVC
do opracowania innego typu aplikacji, np. aplikacji konsolowej. W przypadku
aplikacji konsolowej własny router mógłby obrabiać argumenty linii poleceń w celu
określenia nazw kontrolerów, nazw akcji oraz opcjonalnych parametrów.
Mechanizm dostarczany przez Zend_Controller_Dispatcher nie jest kompatybilny.
Domyślna konfiguracja przyjmuje taką konwencję, że kontrolery są klasami, a akcje metodami
tych klas. Bądź co bądź, jest wiele innych sposobów wykonania tego. Przykładem może być takie
rozwiązanie, w którym kontrolery są katalogami a akcje plikami w tych katalogach.
Chciałbyś dostarczyć dodatkowe możliwości które będą odziedziczone
przez wszystkie kontrolery. Na przykład Zend_Controller_Action
nie jest domyślnie zintegrowany z Zend_View. Jednak mógłbyś
rozszerzyć swój własny kontroler aby to robił i zapewnienie takiej
funkcjonalności nie wymagałoby modyfikowania dostarczonych klas
Zend_Controller_Router oraz Zend_Controller_Dispatcher.
Proszę być ostrożnym podczas nadpisywania znaczących części systemu, sczególnie
wtedy gdy jest to dispatcher. Jedną z zalet klasy Zend_Controller
jest to że wprowadza ona ogólne konwencje budowy aplikacji. Jeżeli odejdziemy
zbyt daleko od tych konwencji, możemy stracić część tych zalet. Jednak
jest wiele różnych zapotrzebowań i jedno rozwiązanie nie jest w stanie spełnić
ich wszystkich więc dowolność jest zapewniona gdy jest potrzebna.
Kiedy rozszerzasz którekolwiek klasy Zend_Controller powinieneś użyć takich samych konwencji w nazywaniu i przechowywaniu plików. Takie postępowanie spowoduje to, że inny programista który jest zaznajomiony z Zend Framework będzie w stanie łatwo zrozumieć Twój projekt.
Klasy ładowane przez Zend Framework są nazywane wg tej samej konwencji, każda z nich jest poprzedzona przedrostkiem "Zend_". Zalecamy abyś nazywał wszystkie swoje klasy w analogiczny sposób, np. jeśli Twoja firma nazywa się Widget Inc., to prefiksem mogłoby być "Widget_".
Klasa Zend_Controller jest przechowywana w taki sposób:
/library
/Zend
/Controller
Action.php
Dispatcher.php
Router.php
Kiedy rozszerzasz klasy Zend_Controller, zalecane jest aby
nowa klasa była przechowywana w identyczny sposób z uwzględnieniem własnego
prefiksu. To spowoduje że będą one łatwe do znalezienia i zrozumienia
dla kogoś kto przegląda kod Twojego projektu.
Na przykład struktura projektu firmy Widget Inc., który implementuje jedynie własny router mogłaby wyglądać w ten sposób:
/library
/Zend
/Widget
/Controller
Router.php
README.txt
Pamiętaj, że w tym przykładzie Widget/Controller/ ma taką samą strukturę
jak Zend/Controller/ kiedy tylko jest to możliwe. W tym przypadku definiuje
on klasę Widget_Controller_Router, która może być klasa rozszerzającą lub
zastępującą klasę Zend_Controller_Router implementującą
Zend_Controller_Router_Interface.
Zwróć także uwagę na to, że w powyższym przykładzie plik README.txt został
umieszczony w katalogu Widget/Controller/. Zend zaleca abyś dokumentował
swoje projekty dostarczając klientom osobne testy oraz dokumentację. Jakkolwiek, zalecamy
Ci abyś także tworzył prosty plik README.txt w katalogu swojej klasy
aby wyjaśnić zmiany oraz zasady jej działania.
Interfejs Zend_Controller_Router_Interface definiuje jedynie jedną metodę:
<?php /** * @param Zend_Controller_Dispatcher_Interface * @throws Zend_Controller_Router_Exception * @return Zend_Controller_Dispatcher_Token|boolean */ public function route(Zend_Controller_Dispatcher_Interface $dispatcher); ?>
Proces routingu ma miejsce tylko raz: wtedy gdy system po raz pierwszy otrzymuje żądanie.
Celem routera jest wygenerowanie obiektu Zend_Controller_Dispatch_Token który
określa kontroler oraz akcję z tego kontrolera. To jest przekazywane do dispatchera. Jeśli
nie jest możliwe określenie mapowanie trasy do tokena to zwracana jest wartość logiczna
FALSE.
Niektóre routery mogą przetwarzać dynamiczne elementy i przed zwróceniem
wygenerowanego tokena potrzebują możliwości sprawdzenia czy jest możliwe
jego wykonanie. Z tego powodu, metoda route() routera otrzymuje
uchwyt obiektu dispatchera jako jedyny argument. Dispatcher posiada metodę
isDispatchable() do sprawdzania możliwości jego wykonania.
Zend_Controller_Front wpierw wywołuje router w celu otrzymania tokena, który
będzie przekazany do dispatchera. Dispatcher uruchomi akcję (tworząc instancję kontrolera
i wywołując jego akcję) a następnie zwróci logiczną wartość FALSE lub kolejny token.
Zend_Controller_Front w pętli wywołuje dispatcher dopóki zwracany
jest kolejny token. Nazywamy to pętlą uruchomieniową (dispatch loop). Pozwala ona
na sekwencyjne uruchamianie akcji aż wszystkie zostaną wykonane.
Interfejs Zend_Controller_Dispatcher_Interface dostarcza definicje dwóch metod:
<?php /** * @param Zend_Controller_Dispatcher_Token $route * @return boolean */ public function isDispatchable(Zend_Controller_Dispatcher_Token $route); ?>
Metoda isDispatchable() sprawdza czy jest możliwe uruchomienie akcji
z tokena. Jeśli jest to możliwe, zwraca ona wartość TRUE. W przeciwnym
wypadku zwraca wartość FALSE. Decyzja o tym czy jest możliwe
uruchomienie akcji została pozostawiona klasie implementującej interfejs.
W domyślnej implementacji klasy Zend_Controller_Dispatcher oznacza to
sprawdzenie, czy plik kontrolera istnieje, czy klasa istnieje w tym pliku
oraz czy wewnątrz klasy istnieje żądana akcja.
<?php /** * @param Zend_Controller_Dispatcher_Token $route * @return Zend_Controller_Dispatcher_Token|boolean */ public function dispatch(Zend_Controller_Dispatcher_Token $route); ?>
dispatch() jest metodą, która wykonuje całą pracę. Ta metoda
musi uruchomić akcję kontrolera. Musi zwrócić kolejny token lub wartość
logiczną FALSE aby określić czy są jeszcze jakieś akcje do uruchomienia.