I. Prérequis▲
La version du Zend Framework 2 utilisée pour cet article est la 2.0.0beta3. L'utilisation du Zend Framework 2 nécessite au minimum la version 5.3.3 de PHP.
Zend Framework 2.0 RequirementsZend Framework 2.0 Requirements.
II. Qu'est-ce qui change▲
Le Zend Framework 2 a complètement modifié sa gestion des vues. La première version du framework séparait le layout des vues de contenus. Il n'existe maintenant plus de composant lié au layout, celui-ci est traité comme une vue à part entière. Le framework hiérarchise maintenant ses fichiers de template afin de déterminer celui correspondant au layout, vue qui englobe les autres. Ce système d'imbrication offre une plus grande flexibilité pour les développeurs.
III. Imbrication des vues▲
Par défaut, la vue créée par l'action de notre contrôleur est injectée dans la vue initialisée dans le bootstrap. Cette première vue représente notre layout, voici son initialisation :
protected function setupView($application
)
{
[...]
// Inject MVC Event with view model $mvcEvent = $application->getMvcEvent();
$viewModel
=
$mvcEvent
->
getViewModel();
$viewModel
->
setTemplate($defaultViewStrategy
->
getLayoutTemplate());
[...]
}
Une fois l'objet de vue retourné depuis notre méthode d'action, celui-ci est injecté dans le layout depuis l'écouteur d'injection de vue :
public function injectViewModel(MvcEvent $e
)
{
$result
=
$e
->
getResult();
if (!
$result
instanceof ViewModel) {
return;
}
$model
=
$e
->
getViewModel();
if ($result
->
terminate()) {
$e
->
setViewModel($result
);
return;
}
$model
->
addChild($result
);
}
Cette méthode nous fait remarquer que l'injection se produit uniquement si l'attribut $terminate est à « true ». Il est alors possible de supprimer le rendu du layout depuis les instructions suivantes :
public function indexAction()
{
$viewModel
=
new ViewModel();
$viewModel
->
setTerminal(true);
return $viewModel
;
}
L'injection de la vue courante au sein du layout est le comportement le plus courant. Cependant, il est possible d'avoir un niveau d'imbrication de vues plus élevé. Voici un exemple de l'imbrication d'une vue fille au sein de la vue courante, elle-même injectée dans le layout :
<
html lang
=
"
en
"
>
<
head>
<
meta charset
=
"
utf-8
"
>
<?php
echo $this
->
headTitle('ZF2 Lazy loading module'
) ?>
<?php
echo $this
->
headMeta()->
appendName('viewport'
,
'width=device-width, initial-scale=1.0'
) ?>
<?php
echo $this
->
headLink() ?>
<?php
echo $this
->
headScript() ?>
<
/head
>
<
body>
<
div class
=
"
navbar navbar-fixed-top
"
>
<
div class
=
"
navbar-inner
"
>
<
div class
=
"
container
"
>
<
a class
=
"
btn btn-navbar
"
data-toggle
=
"
collapse
"
data-target
=
"
.nav-collapse
"
>
<
span class
=
"
icon-bar
"
></span
>
<
span class
=
"
icon-bar
"
></span
>
<
span class
=
"
icon-bar
"
></span
>
<
/a
>
<
a class
=
"
brand
"
href
=
"
<?php
echo $this
->
url('home'
) ?>
"
>
Home<
/a
>
<
/div
>
<
/div
>
<
/div
>
<
div class
=
"
container
"
>
<?php
echo $this
->
content;
?>
<
hr>
<
footer>
My footer
<
/footer
>
<
/div
>
<
/body
>
<
/html
>
<
div class
=
"
row
"
>
<
div class
=
"
span12
"
>
<?php
echo $this
->
content;
?>
<
/div
>
<
/div
>
<
h2>
3 niveaux d'imbrications<
/h2
>
public function indexAction()
{
$viewModelChild
=
new ViewModel();
$viewModelChild
->
setTemplate('
index/child
'
);
$viewModel
=
new ViewModel();
$viewModel
->
addChild($viewModelChild
);
return $viewModel
;
}
Il est possible d'imbriquer encore de nombreuses vues les unes dans les autres, mais le code de votre application risque alors de perdre en clarté.
Dans nos exemples, nous retournons des objets de type ViewModel pour représenter la vue courante, mais il est également possible de retourner d'autres valeurs.
IV. Création de l'objet de vue▲
Deux méthodes écoutent la distribution de l'action (évènement de nom « dispatch ») avec de faibles priorités, ce qui leur permet d'intervenir après le retour de l'action. Les deux écouteurs sont chargés de convertir le retour de l'action en un objet ViewModel, type d'objet attendu lors du rendu :
/**
* Attach listeners
*
*
@param
Events
$events
*
@return
void
*/
public function attach(Events $events
)
{
$this
->
listeners[]
=
$events
->
attach(MvcEvent::
EVENT_DISPATCH,
array($this
,
'
createViewModelFromArray
'
),
-
80
);
$this
->
listeners[]
=
$events
->
attach(MvcEvent::
EVENT_DISPATCH,
array($this
,
'
createViewModelFromNull
'
),
-
80
);
}
/**
* Inspect the result, and cast it to a ViewModel if an assoc array is detected
*
*
@param
MvcEvent
$e
*
@return
void
*/
public function createViewModelFromArray(MvcEvent $e
)
{
$result
=
$e
->
getResult();
if (!
ArrayUtils::
hasStringKeys($result
,
true)) {
return;
}
$model
=
new ViewModel($result
);
$e
->
setResult($model
);
}
/**
* Inspect the result, and cast it to a ViewModel if
null
is detected
*
*
@param
MvcEvent
$e
*
@return
void
*/
public function createViewModelFromNull(MvcEvent $e
)
{
$result
=
$e
->
getResult();
if (null !==
$result
) {
return;
}
$model
=
new ViewModel;
$e
->
setResult($model
);
}
Le type de retour de nos actions gérées sont donc :
- Zend\View\Model ;
- null ;
- Array.
Tout autre type renvoyé par l'action de notre contrôleur ne générera pas d'erreur, mais aura pour conséquence de ne pas afficher la vue courante. Comme nous le voyons depuis l'injection de vue, seuls les objets de type Zend\View\Model sont injectés dans le layout :
public function injectViewModel(MvcEvent $e
)
{
$result
=
$e
->
getResult();
if (!
$result
instanceof ViewModel) {
return;
}
[...]
}
Les méthodes-écouteurs « createViewModelFromArray » et « createViewModelFromNull » qui transforment le retour de l'action courante en objet de type ViewModel interviennent avant l'injection.
Les trois méthodes suivantes produisent donc un objet de vue identique :
Avec l'insertion de variables dans nos vues, les deux exemples suivants produisent un résultat identique :
public function monactionAction()
{
return new ViewModel(array('
key
'
=>
'
value
'
));
}
public function monactionAction()
{
return array('
key
'
=>
'
value
'
);
}
Par défaut, l'objet créé pour le rendu est toujours de type ViewModel. Ce type de vue gère le rendu de code HTML, et il existe d'autres objets de vue pour la gestion d'autres formats, comme le JSON.
V. Le modèle de vue JsonModel▲
Nous venons d'expliquer le comportement de l'objet ViewModel, mais deux autres modèles de vue sont proposés par le framework : JsonModel et FeedModel. Intéressons-nous au premier de ces modèles. La classe JsonModel permet d'encapsuler les données au format JSON. Afin de pouvoir gérer les vues au format JSON, nous avons besoin de modifier la stratégie de rendu qui, par défaut, utilise un objet de type PhpRendererStrategy. Nous allons alors utiliser la stratégie propre au format JSON :
public function jsonAction()
{
$locator
=
$this
->
getLocator();
$jsonRendererStrategy
=
$locator
->
get('
Zend\View\Strategy\JsonStrategy
'
);
$view
=
$locator
->
get('
Zend\View\View
'
);
$view
->
events()->
attachAggregate($jsonRendererStrategy
,
10
);
return new \Zend\View\Model\JsonModel(array(
'
key
'
=>
'
value
'
,
'
state
'
=>
'
ok
'
));
}
Nous confions à la classe JsonStrategy la responsabilité de choisir l'objet de rendu à utiliser. Notre vue sera alors rendue par l'objet de type JsonRenderer, objet de rendu par défaut de la stratégie de rendu de JSON.
VI. Conclusion et ressources▲
La vue et le layout sont désormais plus flexibles et il devient plus simple de gérer le format de retour. Cet article est tiré en partie du Livre « Au cœur de Zend Framework 2 », disponible dans le début du quatrième trimestre de l'année 2012. Dans ce livre, vous retrouverez en détail tous les composants intervenant lors du rendu de la vue.
Retrouvez mes modules ou autres fonctionnalités, créés pour le Zend Framework 2, sur mon compte github disponible à l'adresse https://github.com/blanchonvincent.
Retrouvez aussi la liste des tutoriels du Zend Framework 2 sur mon compte developpez.com : blanchon-vincent.developpez.comblanchon-vincent.developpez.com