juillet
2010
Pour faire suite au précédent billet sur ContextSwitch et en attendant de creuser un peu plus avant le sujet de la validation client-side des Zend_Form, je vais vous parler brièvement aujourd’hui de la création de contexte personnalisé.
Il ne s’agit pas ici d’apprendre comment créer ses propres contextes. Pour ça je vous renvoie à cette adresse qui s’acquittera de cette tâche beaucoup mieux que je ne saurais le faire : http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelpers.contextswitch
Ce dont nous allons parler aujourd’hui, c’est d’un piège dans lequel il est très facile de tomber lorsque l’on utilise un contexte « maison » pour la première fois.
Vous l’avez sentis venir, faîtes chauffer Zend_Tool car on va créer une nouvelle application. Par manque évident d’imagination, notre application se contentera d’afficher la date et l’heure dans une unique action datetime.
Voici notre contrôleur :
{
public function datetimeAction()
{
$this->view->datetime = Zend_Date::now()->toString('dd/MM/YYYY HH:mm:ss');
}
}
Notre vue, datetime.phtml :
Nous sommes le : <b><?php echo $this->escape($this->datetime); ?></b>
Vous m’en voudrez si on saute les explications ? Non ? Continuons, alors. Nous avons pour besoin d’afficher notre vue d’une autre façon, simplement sous forme de texte brut. Les deux contextes fournis par défaut par le Zend Framework étant xml et json, nous allons avoir besoin d’un contexte txt.
Nous rajoutons une méthode init à notre contrôleur :
{
$this->_helper
->contextSwitch
->addContext('txt', array ('suffix' => 'txt',
'headers'=> array('content-type' => 'text/plain')))
->addActionContext('datetime', 'txt')
->initContext();
}
Rien de très compliqué, on créé un contexte texte avec pour suffixe txt, un content-type défini à text/plain, on associe ce contexte à l’action datetime et on initialise le ContextSwitch.
On créé la vue associée, datetime.txt.phtml :
Nous sommes le : <?php echo $this->escape($this->datetime); ?>
Notre application fonctionne correctement, mais il y a deux problèmes :
- Le premier soucis, c’est que notre nouveau contexte, txt, est un contexte qui sera tôt ou tard utile ailleurs : il aurait plus sa place quelque part où on pourrait le réutiliser. Admettons que vous êtes sûr à 100% que votre contexte est totalement unique, et laissons en l’état.
- Le second soucis est plus sournois, nous allons l’illustrer.
Mettons que nous ayons pour besoin que la vue index nous redirige vers l’action datetime. Il suffit de l’implémenter de cette manière :
{
$this->_forward('datetime');
}
Si on appelle notre application via http://myapp/index/datetime, aucun soucis. Si on appelle notre application via http://myapp/, c’est le drame, une exception est levée :
Message: Cannot add context « txt »; already exists
La raison est simple : en utilisant forward, la boucle de dispatch instancie à nouveau le contrôleur et déclenche un second appel de la méthode init. Le contexte est donc créé deux fois ce qui provoque une exception.
Si notre contexte était si unique, nous aurions pu créer le contexte directement dans la méthode datetime me direz-vous ? Et vous auriez raison, le peu de temps avant que la direction ne vous demande une méthode date et une méthode time qui utilise le même contexte…
Nous allons résoudre le problème autrement, et définir le contexte une bonne fois pour toute, accessible dans toute notre application. De cette manière, il sera réutilisable ailleurs, et même exploitable en dehors de ce projet si nécessaire.
La première chose à faire est de retirer l’ajout de contexte de la méthode init :
{
$this->_helper
->contextSwitch
->addActionContext('datetime', 'txt')
->initContext();
}
Ensuite, il faut configurer notre ActionHelper en amont. Après m’être penché un peu sur le sujet, je pense que le plus simple et efficace consiste à utiliser le Bootstrap, par exemple en ajoutant une méthode _initContextSwitch :
{
protected function _initContextSwitch()
{
$contextSwitch = new Zend_Controller_Action_Helper_ContextSwitch();
$contextSwitch->addContext('txt', array ('suffix' => 'txt',
'headers' => array('content-type' => 'text/plain')));
Zend_Controller_Action_HelperBroker::addHelper($contextSwitch);
}
}
Vous êtes libres de configurer à votre guise l’ActionHelper soit directement dans la méthode du Bootstrap, soit un utilisant des paramètres passés dans le fichier de configuration, créer une ressource pour Zend_Application…
La seule chose que je ne recommanderais pas serait de créer une classe héritant de Zend_Application_ContextSwitch : si vous voulez vous faire votre avis, allez jeter un oeil sur le constructeur et la méthode init de cet ActionHelper : il est assez délicat de modifier son comportement sans casser son fonctionnement exact, du moins sans réécriture massive du code.
Je suis bien entendu ouvert à tout avis, critiques, questions et retours d’expérience à ce sujet !
Le code a été écrit et testé avec le Zend Framework dans sa version 1.10.5.