4.3. Rozszerzanie
Poprzedni | Rozdział 4. Zend_Controller | Następny

## 4.3. Rozszerzanie

### 4.3.1. Wprowadzenie

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.

### 4.3.2. Konwencje

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.

#### 4.3.2.1. Przedrostki

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_".

#### 4.3.2.2. Struktura katalogów

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.

### 4.3.3. Interfejs Routera

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.

### 4.3.4. Interfejs dispatchera

`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.

Poprzedni | Początek rozdziału | Następny
4.2. Pierwsze kroki | Spis treści | 4.4. Wtyczki