Rozdział 7. Zend_Controller
Poprzedni |  | Następny

## Rozdział 7. Zend_Controller

## 7.1. Wstęp

### 7.1.1. Wprowadzenie

`Zend_Controller` dostarcza system umożliwiający budowę witryny opartej na wzorcu MVC (Model-View-Controller - Model-Widok-Kontroler).

System `Zend_Controller` został zaprojektowany aby być lekkim, modułowym i rozszerzalnym. Projekt jest minimalistyczny w celu zapewnienia elastyczności i wolności użytkownikom, jednak zapewniając wystarczającą strukturę powoduje, że systemy zbudowane w oparciu o `Zend_Controller` będą napisane w podobnych konwencjach.

System `Zend_Controller` został zaimplementowany jako kilka komponentów. Nie jest wymagane posiadanie dokładnej wiedzy na temat działania wszystkich tych komponentów, jednak znajomość tego procesu może być pomocna.

- `Zend_Controller_Front` instrumentuje całą przestrzeń systemu `Zend_Controller`. Jest interpretacją wzorca projektowego FrontController. Obiekt `Zend_Controller_Front` przetwarza wszystkie żądania otrzymywane przez serwer i jest ostatecznie odpowiedzialny za przekazywanie żądań do odpowiednich kontrolerów (`Zend_Controller_Action`).

- `Zend_Controller_Request_Abstract` reprezentuje środowisko żądania i zapewnia metody do ustawienia i odebrania nazw kontrolera i akcji oraz parametrów żądania. Dodatkowo przechowuje informacje o tym, czy akcja z tego żądania została uruchomiona przez `Zend_Controller_Dispatcher` czy nie. Rozszerzenia do abstrakcyjnego obiektu żądania mogą być użyte aby obudować całe środowisko żądania, pozwalając routerom na wyciągnięcie informacji ze środowiska żądania w celu ustawienia nazwy kontrolera i akcji.

  Domyślnie używany jest obiekt, `Zend_Controller_Request_Http`, ktory zapewnia dostęp do całości środowiska żądania HTTP.

- Interfejs `Zend_Controller_Router_Interface` jest używany do definiowania tras. Routing jest procesem badania środowiska żądania w celu określenia który kontroler i która akcja tego kontrolera powinny odebrać żądanie. Nazwa tego kontrolera, akcji oraz opcjonalnych parametrów są ustawione w obiekcie żądania a następnie przetwarzane przez `Zend_Controller_Dispatcher_Standard`. Routing przeprowadzany jest tylko raz: wtedy kiedy żądanie jest po raz pierwszy odebrane, a przed uruchomieniem pierwszego kontrolera.

  Domyślnym routerem jest `Zend_Controller_Router_Rewrite`.

  Domyśłny router, `Zend_Controller_Router_Rewrite`, pobiera adres URI, który jest określony w obiekcie `Zend_Controller_Request_Http` i wyciąga z niego informacje o kontrolerze, akcji oraz parametrach w oparciu o informacje z adresu url. Na przykład, adres URL `http://localhost/foo/bar/key/value` może być zdekodowany jako kontroler `foo`, akcja `bar`, i parametr `key` o wartości `value`.

  `Zend_Controller_Router_Rewrite` może być także użyty do dopasowania bezwzlędnych ścieżek; zobacz Sekcja 7.4.3, „Zend_Controller_Router_Rewrite” aby dowiedzieć się więcej.

- `Zend_Controller_Dispatcher_Interface` jest używany do definiowania klas uruchamiających. Uruchamianie jest procesem pobierania kontrolera i akcji z obiektu żądania, określania na ich podstawie pliku kontrolera, klasy oraz metody akcji w klasie kontrolera. Jeśli kontroler lub akcja nie istnieje, obsługuje on określanie domyślnych kontrolerów i akcji do uruchomienia.

  Aktualny proces uruchamiania składa się z tworzenia instancji klasy kontrolera i wywoływania metody akcji tej klasy. W przeciwieństwie do routingu, który występuje raz, uruchamianie występuje w pętli. Jeśli status uruchomienia w obiekcie żądania zostanie w którymkolwiek momencie zresetowany, pętla zostanie powtórzona, wywołując akcję która jest obecnie ustawiona w obiekcie żądania. Za pierwszym razem pętla kończy się z ustawioną flagą statusu uruchomienia obiektu żądania (jako wartość logiczna true), co kończy uruchamianie.

  Domyślną klasą uruchamiającą jest `Zend_Controller_Dispatcher_Standard`. Definiuje ona kontroler jako CamelCasedClasses zakończone słowem Controller, oraz metody akcji jako camelCasedMethods zakończone słowem Action: `SomeFooController::barAction`. W tym przypadku, odpowiadałoby to kontrolerowi o nazwie `somefoo` oraz akcji o nazwie `bar`.

- `Zend_Controller_Action` jest podstawowym komponentem kontrolera. Każdy kontroler jest pojedynczą klasą, która rozszerza klasę `Zend_Controller_Action`, a ta klasa posiada metody które są akcjami.

- `Zend_Controller_Response_Abstract` definiuje podstawową klasę odpowiedzi używaną do zbierania i zwracania odpowiedzi z kontrolerów akcji. Zbiera ona zarówno nagłówki jak i zawartość treści, oraz z tego względu, że implementuje metodę `__toString()`, może być bezpośrednio wyświetlona w celu wysłania wszystkich nagłówków i całej zawartości na raz.

  Domyślną klasą odpowiedzi jest `Zend_Controller_Response_Http`, która jest odpowiednia do użycia w środowisku HTTP.

Przestrzeń systemu `Zend_Controller` jest stosunkowo prosta. Żądanie jest odbierane przez obiekt `Zend_Controller_Front`, który wywołuje `Zend_Controller_Router_Rewrite` w celu określenia kontrolera (i akcji w tym kontrolerze) do uruchomienia. `Zend_Controller_Router_Rewrite` rozkłada adres URI na części w celu określenia nazwy kontrolera i akcji w żądaniu. Wtedy `Zend_Controller_Front` rozpoczyna pętlę uruchamiania akcji z kontrolerów. Uruchamia `Zend_Controller_Dispatcher_Standard`, przekazując mu żądanie w celu uruchomienia kontrolera i akcji, określonych w żądaniu (lub domyślnych). Gdy kontroler kończy działanie, kontrola wraca do obiektu `Zend_Controller_Front`. Jeśli kontroler resetując status wykonania żądania wskazał kolejny kontroler, który powinien być uruchomiony pętla kontynuuje działanie i kolejny kontroler zostaje uruchomiony. W przeciwnym wypadku proces się kończy.

### 7.1.2. Obiekt żądania

Obiekt żądania jest prostym obiektem przekazywanym pomiędzy obiektem `Zend_Controller_Front`, routerem, dispatcherem i klasami kontrolerów. Przechowuje on nazwę kontrolera, akcji oraz parametrów, które mają być przekazane do akcji, tak samo jak resztę środowiska żądania, które mogą być w środowisku HTTP, CLI lub PHP-GTK.

- Nazwa kontrolera jest dostępna za pomocą metod `getControllerName()` oraz `setControllerName()`.

- Nazwa akcji kontrolera która ma być wywołana jest dostępna za pomocą metod `getActionName()` oraz `setActionName()`.

- Parametry, które mają być przekazane do tej akcji są asocjacyjną tablicą par klucz/wartość, ktore są dostępne za pomocą metod `getParams()` oraz `setParams()`, lub pojedynczo za pomocą metod `getParam()` oraz `setParam()`.

Zależnie od typu żądania, może być dostępnych więcej metod. Domyślny obiekt żądania, `Zend_Controller_Request_Http`, ma na przykład metody do pobierania zażądanego adresu URI, informacji o ścieżce, parametrów $_GET oraz $_POST itp.

Obiekt żądania jest przekazywany do kontrolera frontowego, lub jeśli nie istnieje zostaje utworzona jego instancja na początku procesu uruchomiania, zanim przeprowadzany jest routing. Jest on przekazywany przez każdy obiekt w łańcuchu uruchamiania.

Dodatkowo obiekt żądania jest użyteczny przy testowaniu. Programista może utworzyć środowisko żądania, włączając w to kontroler, akcję, parametry, URI itp. i przekazać obiekt żądania do kontrolera frontowego aby testować działanie aplikacji. Gdy do tego dołożymy obiekt odpowiedzi, precyzyjne testowanie jednostkowe aplikacji MVC staje się możliwe.

### 7.1.3. Proces Routingu

Zanim zbudujesz swój pierwszy kontroler, powinieneś zrozumieć jak działa proces routingu który jest zaimplementowany jako `Zend_Controller_Router_Rewrite`. Pamiętaj, że przestrzeń działania jest podzielona na routing, który ma miejsce tylko raz oraz na proces dispatchingu, który odbywa się w pętli.

`Zend_Controller_Front` wywołuje `Zend_Controller_Router_Rewrite` (lub inny zarejestrowany router) aby zmapować adres URI do kontrolera oraz do akcji wewnątrz kontrolera. `Zend_Controller_Router_Rewrite` pobiera adres URI z obiektu żądania i przekazuje go do obiektów tras w jego łańcuchu; domyślnie używa on `Zend_Controller_Router_Route_Module` do dopasowania adresów URL. Obiekt trasy następnie rozkłada URL na części aby określić nazwę kontrolera, akcji i inne parametry z adresu URL a następnie ustawia je w obiekcie żądania.

`Zend_Controller_Router_Route_Module` używa bardzo prostego mapowania w celu określenia nazwy kontrolera oraz akcji wewnątrz kontrolera:

    http://framework.zend.com/controller/action/

Pamiętaj, że powyżej pierwsza część adresu jest nazwą kontrolera, a druga część jest zawsze nazwą akcji.

Opcjonalnie w adresie URI mogą być zdefiniowane parametry które zostaną przekazane do kontrolera. Przyjmują one postać par klucz/wartość:

    http://framework.zend.com/controller/action/key1/value1/

Jeśli w adresie URI brakuje informacji o kontrolerze lub akcji `Zend_Controller_Dispatcher_Standard` spróbuje pobrać wartości z parametrów obiektu żądania i jeśli nie zostaną w nim znalezione, użyje domyślnych wartości. Zarówno dla kontrolera jak i dla akcji domyślną wartością jest "`index`". Pokazuje to ten przykład:

    http://framework.zend.com/roadmap/future/
    Kontroler: roadmap
    Akcja    : future

    http://framework.zend.com/roadmap/
    Kontroler: roadmap
    Akcja    : index

    http://framework.zend.com/
    Kontroler: index
    Akcja    : index

| Elastyczność
Jeśli chcesz bardziej elastyczne możliwości, możesz sprawdzić Sekcja 7.4.3, „Zend_Controller_Router_Rewrite”.

Nazwa kontrolera, nazwa akcji w kontrolerze i parametry opcjonalne są ustawione w obiekcie żądania. Kiedy kontroler frontowy `Zend_Controller_Front` wchodzi w pętlę uruchomieniową, obiekt żądania będzie przekazany do obiektu `Zend_Controller_Dispatcher_Standard`.

### 7.1.4. Proces uruchamiania

Uruchamianie jest procesem pobierania obiektu żądania, `Zend_Controller_Request_Abstract`, wyciągania z niego nazwy kontrolera, nazwy akcji oraz opcjonalnych parametrów zawartych w nim, następnie tworzenia instancji kontrolera i uruchamiania akcji tego kontrolera. Jeśli kontroler lub akcja nie zostaną znalezione, zostaną użyte wartości domyślne. `Zend_Controller_Dispatcher_Standard` ustawi wartość `index` zarówno dla kontrolera jak i akcji, ale pozwala programiście zmienić domyślne wartości używając metod `setDefaultController()` oraz `setDefaultAction()`.

Uruchamianie działa w pętli w kontrolerze frontowym. Zanim zacznie się uruchamianie, kontroler frontowy sprawdza żądanie aby określić nazwę kontrolera, akcji oraz opcjonalne parametry. Wtedy wchodzi w pętlę urchomieniową, uruchamiając żądanie.

Na początku każdej iteracji, w obiekcie żądania ustawiana jest flaga określająca czy akcja została uruchomiona. Jeśli akcja lub metoda pre/postDispatch plugina zresetuje tę flagę, pętla uruchamiania będzie kontynuowana i spróbuje uruchomić żądanie ponownie. Zmieniając nazwę kontrolera oraz/lub akcji w żądaniu i resetując flagę, programista może zdefiniować łańcuch żądań do uruchomienia.

Metodą kontrolera akcji, która kontroluje takie uruchamianie jest `_forward()`; wywołaj tę metodę w metodach pre/postDispatch() lub w metodach akcji podając nazwę kontrolera, akcji i opcjonalnie dodatkowe parametry które chcesz wysłać do nowej akcji:

    public function myAction()
    {
        // coś przetwarzaj...
        // przenieś do innej akcji, FooController::barAction(), w obecnym
        // module:
        $this->_forward('bar', 'foo', null, array('baz' => 'bogus'));
    }

### 7.1.5. Obiekt odpowiedzi

Obiekt odpowiedzi jest logiczną parą dla obiektu żądania. Jego zadaniem jest zebranie zawartości oraz/lub nagłówków aby mogły być one zwrócone wszystkie na raz. Dodatkowo, kontroler frontowy przekaże wszystkie złapane wyjątki do obiektu odpowiedzi, pozwalając programiście wdzięcznie obsłużyć wyjątki. Ta funkcjonalność może być nadpisana przez ustawienie `Zend_Controller_Front::throwExceptions(true)`:

    $front->throwExceptions(true);

Aby wysłać obiekt odpowiedzi, włączając w to nagłówki, użyj metody `sendOutput()`.

    $response->sendOutput();

Programiści powinni użyć obiektu odpowiedzi w ich kontrolerach akcji. Zamiast bezpośrednio wyświetlać dane wyjściowe i wysyłać nagłowki powinni przekazywać je do obiektu odpowiedzi:

    // Wewnątrz akcji kontrolera:
    // Ustaw nagłówek
    $this->getResponse()
        ->setHeader('Content-Type', 'text/html')
        ->appendBody($content);

Dzięki temu wszystkie nagłówki są wysyłane na raz, zaraz przed wyświetleniem zawartości.

To czy wyjątek wystąpił w aplikacji możesz sprawdzić za pomocą metody `isException()`, a odebrać wyjątek możesz używając metody `getException()`. Dodatkowo możesz stworzyć własny obiekt odpowiedzi, który może przekierowywać do stron błędów, zapisywać informacje o wyjątku, ładnie formatować informacje o wyjątku (dla potrzeb programistycznych) itp.

Możesz odebrać obiekt odpowiedzi po wywołaniu metody dispatch(), lub powodując aby kontroler frontowy zwrócił obiekt odpowiedzi zamiast renderowania danych wyjściowych.

    // odbierz obiekt po uruchomieniu:
    $front->dispatch();
    $response = $front->getResponse();
    if ($response->isException()) {
        // zaloguj, wyślij mailem itp.
    }

    // lub spowoduj aby metoda dispatch() zwróciła obiekt odpowiedzi
    $front->returnResponse(true);
    $response = $front->dispatch();

    // coś przetwarzaj...

    // ostatecznie wyświetl odpowiedź
    $response->sendOutput();

Domyślnie komunikaty wyjątków nie są wyświetlane. Te zachowanie może być zmienione przez wywołanie metody `renderException()`, lub włączenie w kontrolerze frontowym opcji throwExceptions(), tak jak pokazano niżej:

    $response->renderExceptions(true);
    $front->dispatch($request, $response);

    // lub:
    $front->returnResponse(true);
    $response = $front->dispatch();
    $response->renderExceptions();
    $response->sendOutput();

    // lub:
    $front->throwExceptions(true);
    $front->dispatch();

Poprzedni |  | Następny
6.4. Configuring Zend_Console_Getopt | Spis treści | 7.2. Pierwsze kroki