# ContextSwitch und AjaxContext

Der ContextSwitch Action Helfer ist vorgesehen für die Erleicherung bei der Rückgabe von unserschiedlichen Antwortformaten oder Anfragen. Der AjaxContext Helfer ist eine spezialisierte Version von ContextSwitch welche die Rückgabe von Antworten zu XmlHttpRequests erleichtert.

Um einen von Ihnen zu aktivieren, muß der Controller darauf hingewiesen werden welche Aktion zu welchem Kontext antworten kann. Wenn eine hereinkommende Anfrage einen gültigen Kontext für eine gegebene Aktion indiziert, dann wird der Helfer:

- das Layout ausschalten wenn es eingeschaltet ist.

- einen alternativen View suffix setzen, was effektiv ein separates View Skript für den Kontext benötigt.

- die richtigen Antwort Header für den gewünschten Kontext senden.

- optional den spezifizierten Callback aufrufen um den Kontext zu definieren und/oder anschließende Berechnungen durchführen.

Als Beispiel wird der folgende Controller angenommen:

    class NewsController extends Zend_Controller_Action
    {
        /**
         * Landeseite; leitet zu listAction() weiter
         */
        public function indexAction()
        {
            $this->_forward('list');
        }

        /**
         * News Items auflisten
         */
        public function listAction()
        {
        }

        /**
         * Ein News Item anzeigen
         */
        public function viewAction()
        {
        }
    }

Angenommen wir wollen das `listAction()` auch im XML Format vorhanden ist. Statt der Erstellung einer anderen Aktion, kann angezeigt werden das eine XML Antwort zurückgegeben wird:

    class NewsController extends Zend_Controller_Action
    {
        public function init()
        {
            $contextSwitch = $this->_helper->getHelper('contextSwitch');
            $contextSwitch->addActionContext('list', 'xml')
                          ->initContext();
        }

        // ...
    }

Was macht das:

- Setzt den 'Content-Type' Antwort Header auf '`text/xml`'.

- Ändert den View Suffix auf '`xml.phtml`' (oder, wenn ein alternativer View Suffix verwendet wird, 'xml.[dein Suffix]').

Jetzt muß ein neues View Skript erstellt werden, '`news/list.xml.phtml`', welches das XML erzeugt und darstellt.

Um zu ermitteln ob eine Anfrage eine Kontextänderung initiiert, prüft der Helfer auf ein token im Anfrage Objekt. Standardäßig schaut er auf den 'format' Parameter, durch den das konfiguriert werden kann. Das bedeutet das, in den meisten Fällen, um eine Kontextänderung zu triggern, ein 'format' Parameter in der Anfrage hinzugefügt werden kann:

- über URL Parameter: `/news/list/format/xml` (beachte, das Standard Routing Schema erlaubt übliche Schlüssel/Werte Paare nach der Aktion)

- über `GET` Parameter: z.B., `/news/list?format=xml`

ContextSwitch erlaubt es beliebige Kontexte zu spezifizieren, inklusive welche Kontextänderung stattfinden wird (wenn überhaupt), jegliche Antwort Header die gesendet werden sollen, und beliebige Callbacks für Initialisierung und folgende Bearbeitung.

## Vorhandene Standard Kontexte

Standardmäßig sind zwei Kontexte im ContextSwitch Helfer vorhanden: json und xml.

- JSON. Der JSON Kontext setzt den 'Content-Type' Antwort Header auf '`application/json`' und den View Skript Suffix auf '`json.phtml`'.

  Trotzdem wird standardmäßig kein View Skript benötigt. Es serialisiert einfach alle View Variablen und sendet die JSON Antwort sofort aus.

  Dieses Verhalten kann deaktiviert werden indem die automatische JSON Serialisierung abgeschaltet wird:

      $this->_helper->contextSwitch()->setAutoJsonSerialization(false);

- XML. Der XML Kontext setzt den 'Content-Type' Antwort Header auf '`text/xml`' und den View Skript Suffix auf '`xml.phtml`'. Es muß ein neues View Skript für den Kontext erstellt werden.

## Eigene Kontexte erstellen

Manchmal sind die Standardkontexte nicht genug. Zum Beispiel wenn man YAML zurückgeben will, oder PHP serialisieren, ein RSS oder ATOM Feed, usw. ContextSwitch erlaubt es das zu tun.

Der einfachste Weg einen neuen Kontext hinzuzufügen ist über die `addContext()` Methode. Diese Methoe nimmt zwei Argumente, den Namen des Kontextes, und eine Array Spezifikation. Die Spezifikation sollte ein oder mehrere der folgenden Dinge enthalten:

- suffix: Der Suffix der dem Standard View Suffix angefügt werden soll der im ViewRenderer registriert ist.

- headers: Ein Array von Header zu Wert Paaren das als Teil der Antwort gesendet werden soll.

- callbacks: Ein Array das ein oder mehrere der Schlüssel 'init' oder 'post' enthält, die auf gültige PHP Callbacks zeigen und die für die Initialisierung des Contextes und die Nachbearbeitung verwendet werden können.

  Initialisierungs Callbacks treten auf wenn der Kontext durch ContextSwitch erkannt wird. Sie können verwendet werden um spezielle Logik auszuführen die stattfinden soll. Als Beispiel verwendet der JSON Kontext einen Callback um den ViewRenderer auszuschalten wenn die automatische JSON Serialisierung eingeschaltet ist.

  Nachbearbeitung tritt wärend der `postDispatch()` Routine der Aktion auf, und kann verwendet werden um spezielle Logik auszuführen. Als Beispiel verwendet der JSON Kontext einen Callback um festzustellen ob die automatische JSON Serialisierung eingeschaltet ist; wenn dem so ist, serialisiert es die View Variablen zu JSON und sendet die Antwort, aber wenn dem nicht so ist, schaltet es den ViewRenderer wieder ein.

Es gibt eine Vielzahl an Methoden für die Interaktion mit Kontexten:

- `addContext($context, array $spec)`: Fügt einen neuen Kontext hinzu. Wirft eine Ausnahme wenn der Kontext bereits existiert.

- `setContext($context, array $spec)`: Fügt einen neuen Kontext hinzu oder überschreibt einen bestehenden Kontext. Verwendet die gleiche Spezifikation wie `addContext()`.

- `addContexts(array $contexts)`: Fügt viele Kontexte auf einmal hinzu. Das `$contexts` Array sollte ein Array von Kontext zu Spezifikation Paaren sein. Wenn einer der Kontexte bereits existiert, wird eine Ausnahme geworfen.

- `setContexts(array $contexts)`: Fügt neue Kontexte hinzu und überschreibt bestehende. Verwendet die gleiche Spezifikation wie `addContexts()`.

- `hasContext($context)`: Gibt `TRUE` zurück wenn der Kontext existiert, andernfalls `FALSE`.

- `getContext($context)`: Empfängt einen einzelnen Kontext durch den Namen. Gibt ein Array zurück das der Spezifikation folgt die in `addContext()` verwendet wird.

- `getContexts()`: Empfängt alle Kontexte. Gibt ein Array von Kontext zu Spezifikation Paaren zurück.

- `removeContext($context)`: Entfernt einen einzelnen Kontext durch seinen Namen. Gibt im Erfolgsfall `TRUE` zurück, und `FALSE` wenn der Kontext nicht gefunden wurde.

- `clearContexts()`: Entfernt alle Kontexte.

## Kontexte per Aktion setzen

Es gibt zwei Mechanismen für das Setzen vorhandener Kontexte. Es kann entweder manuell ein Array im Controller erstellt werden, oder es können verschiedene Methoden in ContextSwitch verwendet werden um Sie zu bauen.

Die prinzipielle Methode für das Hinzufügen von Aktion zu Kontext Relationen ist `addActionContext()`. Sie erwartet zwei Argumente, die Aktion zu welcher der Kontext hinzugefügt werden soll, und entweder den Namen des Kontextes oder ein Array von Kontexten. Als Beispiel nehmen wir die folgende Controller Klasse an:

    class FooController extends Zend_Controller_Action
    {
        public function listAction()
        {
        }

        public function viewAction()
        {
        }

        public function commentsAction()
        {
        }

        public function updateAction()
        {
        }
    }

Angenommen wir sollen einen XML Kontext zu der 'list' Aktion hinzufügen, und XML und JSON Kontexte zu der 'comments' Aktion. Wir können das in der `init()` Methode machen:

    class FooController extends Zend_Controller_Action
    {
        public function init()
        {
            $this->_helper->contextSwitch()
                 ->addActionContext('list', 'xml')
                 ->addActionContext('comments', array('xml', 'json'))
                 ->initContext();
        }
    }

Alternativ kann einfach die Array-Eigenschaft `$contexts` definiert werden:

    class FooController extends Zend_Controller_Action
    {
        public $contexts = array(
            'list'     => array('xml'),
            'comments' => array('xml', 'json')
        );

        public function init()
        {
            $this->_helper->contextSwitch()->initContext();
        }
    }

Das obige ist weniger Overhead, aber enthält auch potentielle Fehler.

Die folgenden Methoden können verwendet werden um die Kontext-Mappings zu erstellen:

- `addActionContext($action, $context)`: markiert einen oder mehrere Kontexte als in einer Aktion vorhanden. Wenn bereits Mapping existieren wird einfach bei diesen Mappings angehängt. `$context` kann ein einzelner Kontext sein, oder ein Array von Kontexten.

  Ein Wert von `TRUE` für den Kontext markiert alle vorhandenen Kontexte als vorhanden für die Aktion.

  Ein leerer Wert für `$context` deaktiviert alle Kontexte für die gegebene Aktion.

- `setActionContext($action, $context)`: markiert einen oder mehrere Kontexte als in einer Aktion vorhanden. Wenn bereits Mappings existieren werden Sie mit den spezifizierten ersetzt. `$context` kann ein einzelner Kontext sein, oder ein Array von Kontexten.

- `addActionContexts(array $contexts)`: fügt mehrere Aktion zu Kontext Paare auf einmal hinzu. `$contexts` sollte ein assoziatives Array von Aktion zu Kontext Paaren sein. Es entspricht `addActionContext()`, was bedeutet das wenn eine Paarung bereits existiert, diese hinzugefügt wird.

- `setActionContexts(array $contexts)`: fungiert wie `addActionContexts()`, überschreibt aber bestehende Aktion zu Kontext Paare.

- `hasActionContext($action, $context)`: ermittelt ob eine spezielle Aktion einen gegebenen Kontext hat.

- `getActionContexts($action = null)`: gibt entweder alle Kontexte für eine gegebene Aktion zurück, oder alle Aktion zu Kontext Paare.

- `removeActionContext($action, $context)`: entfernt ein oder mehrere Kontexte von einer gegebenen Aktion. `$context` kann ein einzelner Kontext sein, oder ein Array von Kontexten.

- `clearActionContexts($action = null)`: entfernt alle Kontexte von einer gegebenen Aktion, oder von allen Aktionen mit Kontexten.

## Kontext Switching initialisieren

Um Kontext Switching zu initialisieren, muß `initContext()` im Action Controller aufgerufen werden:

    class NewsController extends Zend_Controller_Action
    {
        public function init()
        {
            $this->_helper->contextSwitch()->initContext();
        }
    }

In einigen Fällen, will man einen Kontext erzwingen der verwendet werden soll; zum Beispiel wenn man nur den XML Kontext erlauben will wenn Kontext Switching aktiviert ist. Das kann durch die Übergaben des Kontextes zu `initContext()` getan werden:

    $contextSwitch->initContext('xml');

## Zusätzliche Funktionalitäten

Eine Vielzahl an Methoden kann verwendet werden um das Verhalten des ContextSwitch Helfers zu verändern. Diese sind:

- `setAutoJsonSerialization($flag)`: Standardmäßig serialisieren JSON Kontexte alle View Variablen in die JSON Notierung und geben diese als Antwort zurück. Wenn man seine eigene Antwort erstellen will, sollte das ausgeschaltet werden; das muß vor dem Aufruf von `initContext()` geschehen.

      $contextSwitch->setAutoJsonSerialization(false);
      $contextSwitch->initContext();

  Der Wert des Flags erhält man mit `getAutoJsonSerialization()`.

- `setSuffix($context, $suffix, $prependViewRendererSuffix)`: Mit dieser Methode kann ein anderer Suffix spezifiziert werden der für einen gegebenen Kontext verwendet werden soll. Das dritte Argument wird verwendet um anzuzeigen ob der aktuelle Suffix des ViewRenderers dem neuen Suffix vorangestellt werden soll oder nicht; dieses Flag ist standardmäßig aktiviert.

  Die Übergabe eines leeren Werte an den Suffix führt dazu das nur der Suffix des ViewRenderers verwendet wird.

- `addHeader($context, $header, $content)`: Fügt einen Antwort Header für einen gegebenen Kontext hinzu. `$header` ist der Headername, und `$content` ist der Wert der an diesen Header übergeben werden soll.

  Jeder Kontxt kann mehrere Header haben; `addHeader()` fügt zusätzliche Header in den Header Stack des Kontextes ein.

  Wenn der spezifizierte `$header` bereits für diesen Kontext existiert, wird eine Ausnahme geworfen.

- `setHeader($context, $header, $content)`: `setHeader()` funktioniert wie `addHeader()`, ausser das sie erlaubt das existierende Kontext Header überschrieben werden.

- `addHeaders($context, array $headers)`: Fügt einen gegebenen Kontext mehrere Header auf einmal hinzu. Entspricht `addHeader()`, was bedeutet das eine Ausnahme geworfen wird wenn der Header bereits existiert. `$headers` ist ein Array von Header zu Kontext Paaren.

- `setHeaders($context, array $headers.)`: Wie `addHeaders()`, nur das es `setHeader()` entspricht und damit das Überschreiben existierender Header erlaubt.

- `getHeader($context, $header)`: Enpfängt den Wert eines Headers für einen gegebenen Kontext. Gibt `NULL` zurück wenn dieser nicht gefunden wird.

- `removeHeader($context, $header)`: Entfernt einen einzelnen Header für einen gegebenen Kontext.

- `clearHeaders($context, $header)`: Entfernt alle Header für einen gegebenen Kontext.

- `setCallback($context, $trigger, $callback)`: Setzt einen Callback bei einem gegebenen Trigger für einen gegebenen Kontext. Trigger können entweder 'init' oder 'post' sein (was zeigt das der Callback entweder bei der Initialisierung oder der Nachbearbeitung aufgerufen wird). `$callback` sollte ein gültiger PHP Callback sein.

- `setCallbacks($context, array $callbacks)`: Setzt mehrere Callbacks für einen gegebenen Kontext. `$callbacks` sollte ein Trigger zu Callback Paar sein. Aktuell sind die meisten Callbacks die registriert werden können zwei, einer für Initialisierung und einer für die Nachbearbeitung.

- `getCallback($context, $trigger)`: Empfängt einen Callback für einen gegebenen Trigger in einem gegebenen Kontext.

- `getCallbacks($context)`: Empfängt alle Callbacks für einen gegebenen Kontext. Gibt ein Array von Trigger zu Callback Paaren zurück.

- `removeCallback($context, $trigger)`: Entfernt einen Callback für einen gegebenen Trigger und Kontext.

- `clearCallbacks($context)`: Entfernt alle Callbacks für einen gegebenen Kontext.

- `setContextParam($name)`: Setzt den Anfrageparameter der geprüft werden muß um zu entscheiden ob eine Kontextumschaltung angefragt wurde oder nicht. Der Wert ist standardmäßig 'format', aber dieser Zugriffspunkt kann verwendet werden um einen alternativen wert zu setzen.

  `getContextParam()` kann verwendet werden um den aktuellen Wert zu erhalten.

- `setAutoDisableLayout($flag)`: Standardmäßig sind Layouts ausgeschaltet wenn eine Kontextumschaltung erfolgt; das ist weil Layouts typischerweise dafür verwendet werden um normale Antworten darzustellen, und Sie in alternativen Kontexten keine Bedeutung haben. Wenn man trotzdem Layouts verwenden will (möglicherweise hat man ein Layout für einen neuen Kontext), kann dieses Verhalten mit der Übergabe eines `FALSE` Wertes an `setAutoDisableLayout()` geändert werden. Das sollte aber before dem Aufruf von `initContext()` geschehen.

  Um den Wert dieses Flags zu erhalten, kann der Zugriffspunkt `getAutoDisableLayout()` verwendet werden.

- `getCurrentContext()` kann verwendet werden um festzustellen welcher Kontext erkannt wurde, wenn überhaupt. Er gibt `NULL` zurück wenn keine Kontextumschaltung stattgefunden hat, oder wenn er aufgerufen wurde bevor `initContext()` stattgefunden hat.

## AjaxContext Funktionalität

Der AjaxContext Helfer erweitert ContextSwitch, sodas alle für ContextSwitch gelisteten Funktionalitäten in Ihm vorhanden sind. Es gibt trotzdem ein paar wichtige Änderungen.

Zuerst, verwendet es eine andere Action Controller Eigenschaft `$ajaxable` um Kontexte festzustellen. Damit kann man verschiedene Kontexte verwenden für AJAX gegenüber normalen HTTP Anfragen. Die verschiedenen *`ActionContext()`* Methoden von AjaxContext schreiben in diese Eigenschaft.

Zweitens, wird nur dann getriggert wenn ein XmlHttpRequest stattgefunden hat, was durch die `isXmlHttpRequest()` Methode den Anfrageobjektes festgestellt wird. Deshalb wird, wenn der Kontextparameter ('format') in der Anfrage übergeben wird, aber die anfrage nicht als XmlHttpRequest gemacht wurde, keine Kontextumschaltung getriggert.

Drittens, fügt der AjaxContext einen zusätzlichen, HTML, Kontext hinzu. In diesem Kontext setzt er den Suffix auf '`ajax.phtml`' um diesen Kontext von einer normalen Anfrage zu unterscheiden. Es werden keine zusätzlichen Header zurückgegeben.

### Aktionen erlauben auf Ajax Anfragen zu antworten

In dem folgenden Beispiel erlauben wir Anfragen auf die Aktionen 'view', 'form' und 'process' auf AJAX Anfragen zu antworten. In den ersten zwei Fällen, 'view' und 'form' wird ein HTML Teilstück zurückgegeben mit dem die Seite aktualisiert werden soll; im letzteren wird JSON zurückgegeben.

    class CommentController extends Zend_Controller_Action
    {
        public function init()
        {
            $ajaxContext = $this->_helper->getHelper('AjaxContext');
            $ajaxContext->addActionContext('view', 'html')
                        ->addActionContext('form', 'html')
                        ->addActionContext('process', 'json')
                        ->initContext();
        }

        public function viewAction()
        {
            // Ein einzelnes Kommentar holen um es anzuzeigen.
            // Wenn AjaxContext erkannt wurde, verwendet es das
            // comment/view.ajax.phtml View Skript.
        }

        public function formAction()
        {
            // Stellt die "add new comment" Form dar.
            // Wenn AjaxContext erkannt wurde, verwendes es das
            // comment/form.ajax.phtml View Skript.
        }

        public function processAction()
        {
            // Bearbeitet einen neuen Kommentar
            // Gibt das Ergebnis als JSON zurück; fügt die Ergebnisse einfach als
            // View Variablen hinzu, und JSON wird zurückgegeben.
        }
    }

Auf der Seite des Clients, wird die AJAX Bibliothek einfach die Endpunkte '`/comment/view`', '`/comment/form`', und '`/comment/process`' anfragen, und den 'format' Parameter übergeben: '`/comment/view/format/html`', '`/comment/form/format/html`', '`/comment/process/format/json`'. (Oder der Parameter kann über den Abfrage String übergeben werden: z.B. , "?format=json".)

Angenommen die Bibliothek übergibt den 'X-Requested-With: XmlHttpRequest' Header, dann werden diese Aktionen das richtige Antwortformat zurückgeben.