Kapitel 7. Zend_Controller
Zurück |  | Weiter

## Kapitel 7. Zend_Controller

## 7.1. Überblick

### 7.1.1. Einführung

`Zend_Controller` stellt das Fundament für den Aufbau einer Website auf Basis des Model-View-Controller (MVC) Entwurfsmusters bereit.

Das `Zend_Controller` System wurde leichtgewichtig, modular und erweiterbar aufgebaut. Das Design ist einfach, um den Benutzern viel Flexibilität und reiheiten zu ermöglichen. Dennoch bietet es ausreichend Struktur, damit Systeme, die auf den `Zend_Controller` aufbauen, gemeinsame Konventionen befolgen und einen ähnlichen Code Aufbau verwenden.

Der `Zend_Controller` Ablauf wurde mit Hilfe verschiedener Komponenten implementiert. Während es für die Benutzung des Systems nicht notwendig ist, den kompletten Unterbau all dieser Komponenten zu verstehen, ist es hilfreich, über den Ablauf ausreichend Kenntnisse zu haben.

- `Zend_Controller_Front` steuert den gesamten Ablauf des `Zend_Controller` Systems. Es ist eine Interpretation des FrontController Entwurfsmusters. `Zend_Controller_Front` verarbeitet alle Anfragen, die der Server erhält, und ist letztendlich dafür verantwortlich, die Anfragen an die ActionController (`Zend_Controller_Action`) zu deligieren.

- `Zend_Controller_Request_Abstract` repräsentiert die Umgebung der Anfrage und stellt Methoden für das Setzen und Abfragen der Namen für Controller und Aktion sowie jeder Anfrageparameter bereit. Zusätzlich behält es die Übersicht darüber, ob die enthaltene Aktion von `Zend_Controller_Dispatcher` verarbeitet wurde oder nicht. Erweiterungen dieses abstrakten Request Objektes können verwendet werden, um die gesamte Anfrageumgebung zu kapseln und Routern zu erlauben, Informationen aus der Anfrageumgebung abzufragen, um die Namen für Controller und Aktion zu setzen..

  Standardmäßig wird `Zend_Controller_Request_Http` verwendet, welches den Zugriff auf die komplette HTTP Anfrageumgebung ermöglicht.

- `Zend_Controller_Router_Interface` wird verwendet, um Router zu definieren. Routing ist der Prozess, bei dem die Anfrageumgebung untersucht wird, um zu ermitteln, welcher Controller und welche Aktion dieses Controllers diese Anfrage verarbeiten soll. Dieser Controller, diese Aktion und optionale Parameter werden dann im Request Object gesetzt, das vom `Zend_Controller_Dispatcher` verarbeitet wird. Das Routing wird nur einmal ausgeführt: wenn die Anfrage erhalten wird und bevor der erste Controller aufgerufen wird.

  Der Standardrouter `Zend_Controller_Router` nimmt den URI Endpunkt entgegen, der in `Zend_Controller_Request_Http` angegeben ist, und zerlegt ihn in einen Controller, eine Aktion und die Parameter basierend auf den Pfadinformationen der URL. Zum Beispiel würde die URL `http://localhost/foo/bar/key/value` übersetzt, um den `foo` Controller und die `bar` Aktion zu verwenden und einen Parameter `key` mit dem Wert `value` anzugeben.

- `Zend_Controller_Dispatcher_Interface` wird verwendet, um Dispatcher zu definieren. Dispatching ist der Prozess, den Controller und die Aktion aus dem Request Objekt abzufragen und auf eine Controller Datei/ Klasse und eine Aktionsmethode in dieser Controller Klasse zu abzubilden. Wenn der Controller oder die Aktion nicht existieren, ermittelt es den zu verarbeitenden Standard Controller und Aktion.

  Der aktuelle Dispatcher Prozess besteht aus dem Instanzieren der Controller Klasse und dem Aufrufen der Aktionsmethode in dieser Klasse. Anders als das Routing, welches immer nur einmal vorkommt, erfolgt das Dispatching in einer Schleife. Wenn der Verarbeitungsstatus des Request Objektes irgendwann zurück gesetzt wird, wird die Schleife wiederholt und die Aktion aufgerufen, die zu diesem Zeitpunkt im Request Objekt gesetzt ist. Das erste Mal, wenn ein Schleifendurchlauf mit gesetztem Verarbeitungsstatus (boolsches true) im Request Objekt beendet wird, wird der Prozess beendet.

  Der Standarddispatcher ist `Zend_Controller_Dispatcher`. Er definiert Controller als CamelCasedClasses, die auf das Wort Controller enden, und Aktionsmethoden als camelCasedMethods, die auf das Wort Action enden: `SomeFooController::barAction`. In diesem Fall wird auf den Controller über `somefoo` und auf die Aktion über `bar` zugegriffen.

- `Zend_Controller_Action` ist die elementare Controller Komponente. Jeder Controller ist eine einzelne Klasse, welche die `Zend_Controller_Action` Klasse erweitert und Methoden für die Aktionen enthält.

- `Zend_Controller_Response_Abstract` definiert eine grundlegende Response Klasse, um Antworten der Aktion aus den Controllern zu sammeln und zurück zu geben. Es sammelt Header und Inhalte und kann, weil es `__toString()` implementiert, direkt ausgegeben werden, um alle Header und Inhalte zusammen zu senden.

  Die Standard Response Klasse ist `Zend_Controller_Response_Http`, welches in einer HTTP Umgebung verwendet werden kann.

Der Ablauf vom `Zend_Controller` ist relativ einfach. Eine Anfrage wird vom `Zend_Controller_Front` empfangen, der wiederum `Zend_Controller_Router` aufruft, um zu ermitteln, welcher Controller (und welche Aktion in dem Controller) ausgeführt werden soll. `Zend_Controller_Router` zerteilt die URI um den Namen des Controllers und der Aktion für den Request zu setzen. `Zend_Controller_Front` durchläuft dann eine Dispatcher Schleife. Er ruft `Zend_Controller_Dispatcher` auf und übergibt den Request, um den Controller und die Aktion auszuführen, die im Request spezifiziert wurden (oder verwendet die Standardwerte). Wenn der Controller fertig ist, wird die Kontrolle wieder an `Zend_Controller_Front` übergeben. Wenn der Controller durch das Zurücksetzen des Dispatch Status des Requests angezeigt hat, dass ein weiterer Controller ausgeführt werden soll, wird der Durchlauf fortgesetzt und ein weiterer Dispatcher Prozess wird durchlaufen. Andernfalls endet der Prozess.

### 7.1.2. Request Objekt

Das Request Objekt ist eine einfaches Wertobjekt, das zwischen `Zend_Controller_Front` und den Router, Dispatcher und Controller Klassen übergeben wird. Es enthält sowohl die Definition des Controllers, der Aktion und der Parameter, die an die Aktion übergeben werden sollen, als auch den Rest der Anfrageumgebung, seit es HTTP, CLI oder PHP-GTK.

- Auf den Controller Namen kann über `getControllerName()` und `setControllerName()` zugegriffen werden.

- Auf den Namen der Aktion, die in diesem Controller aufgerufen wird, kann über accessed by `getActionName()` und `setActionName()` zugegriffen werden.

- Parameter, die an diese Aktion übergeben werden, bestehen aus einem assoziativen Array mit Schlüssel/Wert Paaren, auf die komplett per `getParams()` und `setParams()` oder einzeln per `getParam()` und `setParam()` zugegriffen werden kann.

Abhängig vom Typ der Anfrage können auch weitere Methoden verfügbar sein. Das verwendete Standard Request Object `Zend_Controller_Request_Http` stellt z.B. Methoden zum Abfragen der Request URI, Pfadinformationen, $_GET und $_POST Parameter usw. bereit.

Das Request Objekt wird an den Front Controller übergeben oder, wenn keines bereit gestellt wurde, am Anfang des Dispatcher Prozesses instanziert, bevor das Routing beginnt. Es wird an jedes Objekt in der Dispatcherkette übergeben.

Zusätzlich ist das Request Object besonders beim Testen sehr nützlich. Der Entwickler kann die Anfrageumgebung von Hand erstellen, inklusive Controller, Aktion, Parameter, URI usw. und das Request Objekt an den Front Controller übrgeben, um den Ablauf der Applikation zu testen. Zusammen mit dem Response Objekt sind durchdachte und genaue Unit Tests für eine MVC Applikation möglich.

### 7.1.3. Routing Prozess

Bevor dein erster Controller erstellt werden kann, musst du die Funkktionsweise des Routing Prozesses verstehen, wie er in `Zend_Controller_Router` implementiert worden ist. Denke daran, dass der Ablauf unterteilt ist in das Routing, das nur einmal ausgeführt wird, und das Dispatching, welches danach in einer Schleife durchlaufen wird.

`Zend_Controller_Front` ruft `Zend_Controller_Router` (oder ein anderer registrierter Router) auf, um die URI auf einen Controller (und eine Aktion in diesem Controller) abzubilden. `Zend_Controller_Router` empfängt die URI fom Request Objekt und zerteilt sie, um den Controller, die Aktion und andere, im Pfad übergebene URL Parameter zu ermitteln, und legt diese im Request Objekt fest.

`Zend_Controller_Router` verwendet eine sehr einfache Zuordnung, um den Namen des Controllers und den Namen der Aktion innerhalb dieses Controllers zu ermitteln.

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

Beachte im obigen Beispiel, dass das erste Segment immer den Namen des Controllers und das zweite Segment immer den Name der Aktion enthält.

Optional können in der URI auch Parameter definiert werden, die an den Controller übergeben werden. Diese haben die Form eines Schlüssel/Wert Paares:

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

Wenn weder der Controller noch die Aktion im URI Pfad vorhanden sind, versucht `Zend_Controller_Dispatcher` die Werte aus des Parametern des Request Objektes zu bekommen und, falls nicht erfolgreich, die Standardwerte zu verwenden. In beiden Fällen sind die Standardwerte "`index`". Dieses Beispiel illustriert dies:

    http://framework.zend.com/roadmap/future/
    Controller: roadmap
    Action    : future

    http://framework.zend.com/roadmap/
    Controller: roadmap
    Action    : index

    http://framework.zend.com/
    Controller: index
    Action    : index

| Flexibilität
Wenn man flexiblere Möglichkeiten benötigt, sollte man den Abschnitt 7.4.3, „Zend_Controller_RewriteRouter“ ausprobieren.

Der Name des Controllers und der Aktion innerhalb dieses Controllers sowie weitere optionale Parameter werden im Request Objekt gesetzt. Wenn `Zend_Controller_Front` die Dispatcher Schleife startet wird das Request Objekt an `Zend_Controller_Dispatcher` übergeben.

### 7.1.4. Dispatcher Prozess

Dispatching ist der Prozess, das Request Objekt `Zend_Controller_Request_Abstract` zu übernehmen, die dort enthaltenen Controller und Aktion Namen sowie die optionalen Parameter zu extrahieren und dann den Controller zu instanzieren und die Aktion dieses Controllers aufzurufen. Wenn kein Controller oder keine Aktion gefunden wurde, werden dafür Standardwerte verwendet. `Zend_Controller_Dispatcher` legt `index` für beide als Standardwert fest, aber erlaubt dem Entwickler auch, diese durch Verwendung der `setDefaultController()` und `setDefaultAction()` Methoden zu verändern.

Dispatching läuft innerhalb einer Schleife im Front Controller ab. Vor dem Dispatching fragt der Front Controller den Request ab, um benutzerspezifizierte Werte für Controller, Aktion und optionale Parameter zu finden. Dann startet er die Dispatch Schleife, um die Anfrage zu verarbeiten.

Zu Beginn jeden Durchlaufes setzt er im Request Objekt einen Schalter, der angibt, dass die Aktion verarbeitet worden ist. Wenn eine Aktion oder ein pre/postDispatch Plugin diesen Schalter zurücksetzt, wird die Dispatch Schleife fortgesetzt und versucht, die Anfrage erneut zu verarbeiten. Durch Ändern des Controllers und / oder der Aktion im Request Objekt und Zuürcksetzen des Verarbeitungsstatus, kann der Entwickler eine zu durchlaufende Anfragekette definieren.

Die Controller Methode, die solch eine Verarbeitung kontrolliert lautet `_forward()`; rufe diese Methode von einer beliebigen pre/postDispatch() oder Aktionsmethode auf und übergebe Controller, Aktion und beliebige optionale Parameter, die du zur neuen Aktion übersenden möchtest.

    public function myAction()
    {
        // do some processing...
        // forward to another action, FooController::barAction():
        $this->_forward('foo', 'bar', array('baz' => 'bogus'));
    }

### 7.1.5. Response Objekt

Das Response Objekt ist das logische Gegenstück zum Request Objekt. Sein Zweck ist es, Inhalte und / oder Header zu vereinigen, um sie in einem Rutsch zu versenden. Zusätzlich übergibt der Front Controller alle aufgefangenen Ausnahmen an das Response Objekt, um dem Entwickler das Verarbeiten von Ausnahmen zu ermöglichen. Dies Funktionalität kann durch Setzen von `Zend_Controller_Front::throwExceptions(true)` überschrieben werden.

    $front->throwExceptions(true);

Da das Response Objekt `__toString()` implementiert, kann es einfach ausgegeben werden. Dies ermöglicht die folgende Nutzung:

    echo $controller->getResponse();

    // or
    $response = $controller->getResponse();
    echo $response;

Entwickler sollten das Response Objekt in ihren Aktionscontrollern verwenden. Statt die Ausgabe direkt zu machen und Header zu versenden, sollten diese an des Response Objekt übergeben werden:

    // Innerhalb einer Controller Aktion:
    // Setze einen Header
    $this->getResponse()
        ->setHeader('Content-Type', 'text/html')
        ->appendBody($content);

Dadurch werden alle Header in einem Rutsch versendet, genau vor der Anzeige des Inhalts.

Sollte in der Applikation eine Ausnahme auftreten, überprüft man den `isException()` Schalter des Response Objektes und erhält die Ausnahme durch Verwendung von `getException()`. Zusätzlich kann man ein eigenes Response Objekt erstellen, dass zu einer Fehlerseite umleitet, die Nachricht der Ausnahme loggt, die Nachricht der Ausnahme schön formatiert ausgibt (für Entwicklungsumgebungen), usw.

Man kann das Response Objekt im Anschluß an die dispatch() Methode des Front Controllers erhalten oder den Front Controller auffordern, dass Response Objekt zurückzugeben statt den Inhalt auszugeben.

    // Erhalten nach dem Dispatch:
    $front->dispatch();
    $response = $front->getResponse();
    if ($response->isException()) {
        // log, mail, etc...
    }

    // Oder den Front Controller dispatch Prozess auffordern, es zurück zu geben
    $front->returnResponse(true);
    $response = $front->dispatch();

    // mache irgend was...

    // zum Schluß, gebe die Antwort aus
    echo $response;

Standardmäßig werden Ausnahmennachrichten nicht ausgegeben. Dieses Verhalten kann durch den Aufruf von `renderException()` überschrieben werden oder indem der Front Controller aufgefordert wird, die Exceptions durch throwExceptions() auszuwerfen, wie oben gezeigt:

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

    // oder:
    $front->returnResponse(true);
    $response = $front->dispatch();
    $response->renderExceptions();
    echo $response;

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

Zurück |  | Weiter
6.4. Configuring Zend_Console_Getopt | Zum Anfang | 7.2. Erste Schritte