ContextSwitch et AjaxContext
L'aide d'action ContextSwitch est destinée à faciliter le retour de
différents formats de réponse à une requête.L'AjaxContext est une aide
spécialisée de ContextSwitch qui permet le renvoi de réponses à
XmlHttpRequest.
Pour l'activer, vous devez indiquer à votre contrôleur quelles actions répondent à
quel contexte. Si une requête d'entrée indique un contexte valide pour une action, alors
l'aide d'action en charge :
Va désactiver les layouts, si elles sont activées.
Va changer le suffixe de la vue à rendre, il faudra donc créer une vue par
contexte.
Va envoyer les bons en-têtes de réponse en fonction du contexte désiré.
Va éventuellement en option appeler des fonctions pour configurer le
contexte, ou des fonctions de post-processing.
Comme exemple, prenons le contrôleur suivant :
_forward('list');
}
/**
* Liste les news
*/
public function listAction()
{
}
/**
* Affiche une new particulière
*/
public function viewAction()
{
}
}
]]>
Imaginons que nous voulions que listAction() soit aussi accessible
au format XML. Plutôt que de créer une autre action, nous pouvons lui
indiquer qu'elle doit retourner du XML :
_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext('list', 'xml')
->initContext();
}
// ...
}
]]>
Ce code aura pour effet :
De changer le "Content-Type" de la réponse en
"application/xml".
De changer le suffixe de vue vers "xml.phtml" (ou un autre
suffixe si vous en utilisez un personnalisé "xml.[votre suffixe]").
Il est donc nécessaire de créer un nouveau script de vue,
"news/list.xml.phtml", qui créera et rendra le
XML.
Pour savoir si la requête doit utiliser un contexte switch, l'aide vérifie un jeton
dans l'objet de requête. Par défaut, l'aide va chercher le paramètre de requête "format",
ceci peut être changé. Ceci signifie que dans la plupart des cas, pour changer le contexte
d'une réponse, il faut simplement injecter un paramètre "format" à la requête:
Via l'URL : /news/list/format/xml
(le routeur par défaut utilise les paires clés et valeurs fournies après l'action)
Via un paramètre GET :
/news/list?format=xml
ContextSwitch vous permet d'écrire des contextes, ceux-ci spécifient le
suffixe de vue qui change, les en-têtes de réponse à modifier, et les fonctions de
rappel éventuelles.
Contextes inclus par défaut
Par défaut, il existe 2 contextes dans l'aide ContextSwitch :
json et XML.
JSON. Le contexte
JSON met le "Content-Type" de la réponse à
"application/json", et le suffixe de la vue est
"json.phtml".
Par défaut cependant, aucun script de vue n'est nécessaire, il va simplement
sérialiser en JSON toutes les variables de vues, et les
envoyer en tant que réponse.
Ce comportement peut être désactivé en éteigant le paramètre de
sérialisation JSON :
_helper->contextSwitch()->setAutoJsonSerialization(false);
]]>
XML. Le contexte XML
met le "Content-Type" de la réponse à "application/xml", et
utilise un suffixe de vue "xml.phtml". Vous devrez
créer une nouvelle vue pour ce contexte.
Créer ses propres contextes
Vous pouvez créer vos propres contextes d'action. Par exemple pour retourner du
YAML, du PHP sérialisé, ou encore du
RSS ou du ATOM. ContextSwitch
est là pour cela.
La manière la plus simple d'ajouter un nouveau contexte d'action est la méthode
addContext(). Elle prend 2 paramètres : le nom du contexte,
et un tableau d'options. Ce tableau d'option doit comporter au moins une des clés
suivantes :
suffix : Le préfixe qui va s'ajouter au suffixe de
vue. Il sera utiliser par le ViewRenderer.
headers : un tableau d'en-têtes et de valeurs que
vous voulez ajouter à la réponse.
callbacks : un tableau dont les clés peuvent être
"init" ou "post", et les valeurs représentent des noms de fonctions
PHP valides, qui seront utilisées pour initialiser ou
traiter la fin du contexte.
Les fonctions d'initialisation interviennent lorsque le contexte est détecté
par ContextSwitch. Par exemple dans le contexte intégré
JSON, la fonction désactive le ViewRenderer lorsque la
sérialisation automatique JSON est activée.
Les fonctions de traitement de fin de contexte (Post processing) interviennent
durant le processus de postDispatch() de l'action en
cours. Par exemple pour le contexte intégré JSON, la
fonction de post process regarde si la sérialisation automatique
JSON est active, si c'est le cas, elle va sérialiser les
variables de la vue en JSON, et envoyer la réponse ;
mais dans le cas contraire, elle va réactiver le ViewRenderer.
Voici les méthodes d'interaction avec les contextes :
addContext($context, array $spec) : Ajoute un
nouveau contexte. Si celui-ci existe déjà, une exception sera lancée.
setContext($context, array $spec) : Ajoute un
nouveau contexte, mais écrase celui-ci s'il existait déjà. Utilise les mêmes
spécifications que addContext().
addContexts(array $contexts) : Ajoute plusieurs
contextes d'un coup. Le tableau $contexts doit être un
tableau de paires contexte et specifications. Si un des contextes existe déjà,
une exception est lancée.
setContexts(array $contexts) : Ajoute des
nouveaux contextes, mais écrase ceux déjà présents éventuellement. Utilise
les mêmes spécifications que addContexts().
hasContext($context) : retourne
TRUE si le contexte existe déjà,
FALSE sinon.
getContext($context) : retourne un contexte
par son nom. Le retour est un tableau qui a la même syntaxe que celui utilisé
par addContext().
getContexts() : retourne tous les contextes.
Le tableau de retour est de la forme contexte => spécifications.
removeContext($context) : Supprime un contexte
grâce à son nom. Retourne TRUE si réussi,
FALSE si le contexte n'a pas été trouvé.
clearContexts() : Supprime tous les contextes.
Affecter des contextes par action
Il existe deux mécanismes pour créer et affecter des contextes. Vous pouvez créer
des tableaux dans vos contrôleurs, ou utiliser plusieurs méthodes de
ContextSwitch pour les assembler.
La méthode principale pour ajouter des contextes à des actions est
addActionContext(). Elle attend 2 arguments, l'action et le
contexte (ou un tableau de contextes). Par exemple, considérons la classe
suivante :
Imaginons que nous voulions ajouter un contexte XML à l'action
"list", et deux contextes XML et JSON à
l'action "comments". Nous pourrions faire ceci dans la méthode
init() :
_helper->contextSwitch()
->addActionContext('list', 'xml')
->addActionContext('comments', array('xml', 'json'))
->initContext();
}
}
]]>
De la même manière, il est aussi possible de simplement définir la propriété
$contexts :
array('xml'),
'comments' => array('xml', 'json')
);
public function init()
{
$this->_helper->contextSwitch()->initContext();
}
}
]]>
Cette syntaxe est simplement moins pratique et plus prompte aux erreurs.
Pour construire vos contextes, les méthodes suivantes vous seront utiles :
addActionContext($action, $context) : Ajoute un
ou plusieurs contextes à une action. $context doit donc être
une chaîne, ou un tableau de chaînes.
Passer la valeur TRUE comme contexte marquera tous les
contextes comme disponibles pour cette action.
Une valeur vide pour $context désactivera tous les contextes
donnés à cette action.
setActionContext($action, $context) : Marque un
ou plusieurs contextes comme disponibles pour cette action. Si ceux-ci existent
déjà, ils seront remplacés. $context doit être une chaîne ou
un tableau de chaînes.
addActionContexts(array $contexts) : Ajoute
plusieurs paires action et contexte en une fois. $contexts
doit être un tableau associatif action et contexte. Cette méthode proxie vers
addActionContext().
setActionContexts(array $contexts) : agit comme
addActionContexts(), mais écrase les paires
action et contexte existantes.
hasActionContext($action, $context) : détermine
si une action possède un contexte donné.
getActionContexts($action = null) : Retourne tous
les contextes d'une action donnée, si pas d'action passée, retourne alors toutes
les paires action et contexte.
removeActionContext($action, $context) : Supprime
un ou plusieurs contextes pour une action. $context doit être
une chaîne ou un tableau de chaînes.
clearActionContexts($action = null) : Supprime
tous les contextes d'une action. Si aucune action n'est spécifiée, supprime
alors tous les contextes de toutes les actions.
Initialiser le Context Switch
Pour initialiser la permutation de contextes (contexte switching), vous devez
appeler initContext() dans vos contrôleurs d'action :
_helper->contextSwitch()->initContext();
}
}
]]>
Dans certains cas, vous voudriez forcer un contexte pour une action ; par exemple
vous pouvez vouloir seulement le contexte XML si la permutation de
contexte est active. Passez le alors à initContext() :
initContext('xml');
]]>
Fonctionnalités avancées
Voici quelques méthodes qui peuvent être utilisées pour changer le comportement
de l'aide ContextSwitch :
setAutoJsonSerialization($flag): Par défaut, le
contexte JSON va sérialiser toute variable en notation
JSON et les retourner en tant que réponse. Si vous voulez
créer votre propre réponse, vous voudriez désactiver cet effet. Ceci doit être
fait avant l'appel à initContext().
setAutoJsonSerialization(false);
$contextSwitch->initContext();
]]>
Pour récupérer la valeur actuelle, utilisez
getAutoJsonSerialization().
setSuffix($context, $suffix,
$prependViewRendererSuffix) :
Cette méthode permet de personnaliser le suffixe de vue d'un contexte. Le
troisième argument indique si le suffixe actuel du ViewRenderer doit être
utilisé comme préfixe de votre suffixe. Par défaut, c'est le cas.
Passer une valeur vide au suffixe aura pour effet de n'utiliser que le
suffixe du ViewRenderer.
addHeader($context, $header, $content) : Ajoute
un en-tête à la réponse pour un contexte donné. $header
est le nom de l'en-tête et $content sa valeur.
Chaque contexte peut posséder plusieurs en-têtes,
addHeader() ajoute des en-têtes dans une pile,
pour un contexte donné.
Si l'en-tête $header spécifié pour le contexte existe déjà,
une exception sera alors levée.
setHeader($context, $header, $content) :
setHeader() agit comme
addHeader(), sauf qu'il va écraser un en-tête
qui aurait déjà été présent.
addHeaders($context, array $headers) : Ajoute
plusieurs en-têtes en une seule fois. Proxie vers
addHeader().$headers est un
tableau de paires header => contexte.
setHeaders($context, array $headers.) : comme
addHeaders(), sauf que cette méthode proxie vers
setHeader(), vous permettant d'écraser des en-têtes
déjà présents.
getHeader($context, $header) : retourne une
valeur d'en-tête pour un contexte. Retourne NULL
si non trouvé.
removeHeader($context, $header) : supprime
un en-tête d'un contexte.
clearHeaders($context, $header) : supprime
tous les en-têtes d'un contexte.
setCallback($context, $trigger, $callback) :
affecte une fonction de rappel (callback) pour un contexte. Le déclencheur
peut être soit "init" ou "post" (la fonction de rappel sera appelée soit à
l'initialisation du contexte, ou à la fin, en postDispatch).
$callback doit être un nom de fonction PHP
valide.
setCallbacks($context, array $callbacks) :
affecte plusieurs fonctions de rappel pour un contexte.
$callbacks doit être un tableau de paires trigger et
callback. Actuellement, seules deux fonctions maximum peuvent être enregistrées
car il n'existe que 2 déclencheurs (triggers) : "init" et "post".
getCallback($context, $trigger) : retourne un
nom de fonction de rappel affectée à un contexte.
getCallbacks($context) : retourne un tableau
de paires trigger et callback pour un contexte.
removeCallback($context, $trigger) : supprime
une fonction de rappel d'un contexte.
clearCallbacks($context) : supprime toutes
les fonctions de rappel d'un contexte.
setContextParam($name) : affecte le paramètre
de requête à vérifier pour savoir si un contexte a été appelé. La valeur par
défaut est "format".
getContextParam() en retourne la valeur actuelle.
setAutoDisableLayout($flag) : Par défaut, les
layouts sont désactivées lorsqu'un contexte intervient, ceci provient du fait
que les layouts n'ont en théorie pas de signification particulière pour un
contexte, mais plutôt pour une réponse 'normale'. Cependant si vous désirez
utiliser les layouts pour des contexte, passez alors la valeur
FALSE à setAutoDisableLayout().
Ceci devant être fait avant l'appel à
initContext().
Pour récupérer la valeur de ce paramètre, utilisez
getAutoDisableLayout().
getCurrentContext() est utilisée pour savoir quel
contexte a été détecté (si c'est le cas). Cette méthode retourne
NULL si aucune permutation de contexte a été détectée,
ou si elle est appelée avant initContext().
Fonctionnalité AjaxContext
L'aide AjaxContext étend l'aide de permutation de contexte
ContextSwitch, donc toutes les fonctionnalités de
ContextSwitch s'y retrouvent. Il y a cependant quelques
différences :
Cette aide utilise une propriété de contrôleur d'action différente pour
déterminer les contextes, $ajaxable. Vous pouvez avoir différents
contextes utilisés avec les requêtes AJAX ou HTTP.
Les différentes méthodes ActionContext() de
AjaxContext vont écrire dans cette propriété.
De plus, cette aide ne sera déclenchée que si la requête répond au critère
isXmlHttpRequest(). Donc même si le paramètre "format" est
passée à la requête, il faut nécessairement que celle ci soit une requête
XmlHttpRequest, sinon la permutation de contexte n'aura pas lieu.
Enfin, AjaxContext ajoute un contexte, HTML.
Dans ce contexte, le suffixe de vue est "ajax.phtml". Il n'y a
pas d'en-tête particulier ajouté à la réponse.
Autoriser les actions à répondre aux requêtes AJAX
Dans l'exemple qui suit, nous autorisons les actions "view", "form", et
"process" à répondre aux requêtes AJAX. Dans les actions,
"view" et "form", nous retournerons des portions de HTML ;
dans "process", nous retournerons du JSON.
_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('view', 'html')
->addActionContext('form', 'html')
->addActionContext('process', 'json')
->initContext();
}
public function viewAction()
{
// Voir les commentaires.
// Quand le AjaxContext est détecté, il utilise le script de vue
// comment/view.ajax.phtml
}
public function formAction()
{
// Rend les formulaire "ajoutez un commentaire".
// Lorsque le AjaxContext est détecté, il utilise le script de
// vue : comment/form.ajax.phtml
}
public function processAction()
{
// Traite un commentaire
// Retourne les résultats sous forme JSON ; assignez simplement
// vos résultats comme variables de vues.
}
}
]]>
Coté client, votre bibliothèque AJAX requêtera simplement
"/comment/view",
"/comment/form", et "/comment/process",
en passant le paramètre "format" :
"/comment/view/format/html",
"/comment/form/format/html",
"/comment/process/format/json".
(Ceci fonctionne aussi avec "?format=json".)
Il est nécessaire que votre bibliothèque envoie l'en-tête
"X-Requested-With: XmlHttpRequest", ce qui est en général le cas.