# ViewRenderer

## Introduction

L'aide `ViewRenderer` apporte les comportements suivants :

- Élimine le besoin d'instancier un objet de vue dans ses contrôleurs. Ceci devient automatique.

- Configure automatiquement les chemins vers les scripts de vue, les aides, et les filtres, en se basant sur le module actuel et associe le nom du module comme préfixe de classe pour les aides et les filtres.

- Créer un objet de vue général accessible pour tous les contrôleurs et donc pour toutes les actions disptachées.

- Autorise le développeur à personnaliser les options de rendu de la vue.

- Donne la possibilité de rendre automatiquement un script de vue.

- Donne accès aux paramètres configurant le chemin de base (base path) et le chemin des scripts (script path), de la vue.

Su vous utilisez `_forward()`, `redirect()`, ou `render()` manuellement, le rendu automatique sera annulé car `ViewRenderer` saura que vous prenez la main.

Le `ViewRenderer` est activé par défaut dans le contrôleur frontal. Pour le désactiver, utilisez le paramètre `noViewRenderer` (`$front->setParam('noViewRenderer', true)`) ou retirez l'objet du gestionnaire d'aides (`Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer')`).

Si vous voulez modifier un paramètre du `ViewRenderer` avant la distribution du contrôleur frontal, il existe deux moyens :

- Instanciez et enregistrez votre propre objet `ViewRenderer` et passez le au gestionnaire d'aides :

      $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
      $viewRenderer->setView($view)
                   ->setViewSuffix('php');
      Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

- Initialisez et/ou récupérez l'objet `ViewRenderer` via le gestionnaire d'aides :

      $viewRenderer =
          Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
      $viewRenderer->setView($view)
                   ->setViewSuffix('php');

## API

L'usage le plus banal consiste à instancier et passer l'objet `ViewRenderer` au gestionnaire d'aides. La manière la plus simple est d'utiliser la méthode statique `getStaticHelper()` du gestionnaire, qui s'occupe de tout ceci en une passe :

    Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

La première action demandée instancie ou récupère l'objet de vue. A chaque instanciation de contrôleur, la méthode `init()` de l'objet `ViewRenderer` est appelée. Elle va configurer la vue et appeler `addScriptPath()` avec un chemin relatif au dossier courant. Ceci a pour effet de "namespacer" toutes les aides et les filtres de vue pour le module en cours.

A chaque `postDispatch()` d'action, `render()` est appelée.

Voici un exemple :

    // Bar controller , module foo :
    class Foo_BarController extends Zend_Controller_Action
    {
        // Rend bar/index.phtml par défaut; rien à faire c'est automatique
        public function indexAction()
        {
        }

        // Rend bar/populate.phtml avec une variable 'foo' à la valeur 'bar'.
        // l'objet de vue est défini dans le preDispatch(), il est donc
        // accessible.
        public function populateAction()
        {
            $this->view->foo = 'bar';
        }
    }

    ...

    // Dans un des scripts de vue :
    $this->foo(); // appelle Foo_View_Helper_Foo::foo()

Le `ViewRenderer` définit de même des accesseurs pour configurer la vue qu'il encapsule :

- `setView($view)` Passage de l'objet de vue. Il devient accessible comme une propriété publique de l'objet : `$view`.

- `setNeverRender($flag = true)` peut être utilisée pour désactiver le rendu automatique de la vue dans tous les contrôleurs. `postDispatch()` est alors court-circuitée. `getNeverRender()` retourne ce paramètre.

- `setNoRender($flag = true)` peut être utilisée pour désactiver le rendu automatique de la vue dans le contrôleur actuel. `postDispatch()` est alors court-circuitée, mais `preDispatch()` réactive le paramètre pour l'action suivante. `getNoRender()` retourne cette option.

- `setNoController($flag = true)` est utilisée pour spécifier à `render()` de ne pas chercher le script de vue dans le sous répertoire après le contrôleur (correspondant à l'action). Par défaut, cette recherche est effectuée. `getNoController()` retourne ce paramètre.

- `setNeverController($flag = true)` fonctionne de manière similaire à `setNoController()`, mais pour tous les contrôleurs.`getNeverController()` est l'autre accesseur.

- `setScriptAction($name)` peut être utilisée pour spécifier le script de vue d'une action à rendre. `$name` doit être le nom de l'action sans le suffixe (et sans le nom du contrôleur sauf si `noController` a été activé). Si vous n'utilisez pas cette méthode elle cherchera le script de vue correspondant au nom de l'action en cours (issue de l'objet de requête). `getScriptAction()` retourne la valeur actuelle de ce paramètre.

- `setResponseSegment($name)` dit dans quel segment de la réponse rendre le script. Par défaut, la vue est rendue dans le segment par défaut. `getResponseSegment()` retourne cette valeur.

- `initView($path, $prefix, $options)` doit être appelée pour configurer la vue : son "base path", le préfixe de classe pour les filtres et aides, et des options matérialisées par : `neverRender`, `noRender`, `noController`, `scriptAction`, et `responseSegment`.

- `setRender($action = null, $name = null, $noController = false)` vous permet de spécifier les paramètres `scriptAction`, `responseSegment`, et `noController` en une fois. `direct()` est un alias qui permet un appel rapide depuis le contrôleur :

      // Rend 'foo' au lieu du script correspondant à l'action courante
      $this->_helper->viewRenderer('foo');

      // rend form.phtml dans le segment 'html' de la réponse sans utiliser le
      // sous repertoire contrôleur pour chercher le script de vue :
      $this->_helper->viewRenderer('form', 'html', true);

  `setRender()` et `direct()` ne rendent pas un script de vue à proprement parler, mais elles notifient au `postDispatch()` de le faire.

Le constructeur de `ViewRenderer` accepte aussi un objet de vue et un tableau d'options, de la même manière que `initView()` :

    $view    = new Zend_View(array('encoding' => 'UTF-8'));
    $options = array('noController' => true, 'neverRender' => true);
    $viewRenderer =
        new Zend_Controller_Action_Helper_ViewRenderer($view, $options);

Il est aussi possible de personnaliser les chemins utilisés pour déterminer le base path (chemin de base) de la vue ainsi que le script path (chemin vers les scripts de vue). Des méthodes le permettent, utilisez les options suivantes avec :

- `:moduleDir` représente le module courant (par convention le dossier parent au dossier contrôleur).

- `:module` pointe vers le module actuel.

- `:controller` pointe vers le contrôleur actuel.

- `:action` représente l'action actuellement traitée.

- `:suffix` est utilisée pour le suffixe du script de vue. `setViewSuffix()` permet aussi de le modifier.

Toutes ces options s'utilisent avec les méthodes ci-après :

- `setViewBasePathSpec($spec)` vous permet de changer le dossier donnant accès aux dossiers de la vue : le base path. Par défaut il s'agit de `:moduleDir/views`. L'accesseur de récupération est `getViewBasePathSpec()`.

- `setViewScriptPathSpec($spec)` : une fois dans le base path, le rendu cherche le script de vue dans le script path, que cette méthode permet de définir. La valeur par défaut est `:controller/:action.:suffix` et l'autre accesseur est `getViewScriptPathSpec()`.

- `setViewScriptPathNoControllerSpec($spec)` Une fois dans le base path, si `noController` est activé, le rendu cherche le script de vue dans le chemin que cette méthode permet de définir. La valeur par défaut est `:action.:suffix` et l'autre accesseur est `getViewScriptPathNoControllerSpec()`.

`ViewRenderer` utilise un inflecteur : Zend_Filter_Inflector, pour résoudre les options de chemin, en chemins réels. Pour une personnalisation maximale, vous pouvez interagir avec cet inflecteur à l'aide des méthodes suivantes :

- `getInflector()` retourne l'inflecteur. Si aucun n'existe, `ViewRenderer` en crée un avec des options par défaut.

  Par défaut, les règles de l'inflecteur sont statiques autant pour le suffixe et le répertoire module, que pour la cible. Ceci permet au `ViewRenderer` de modifier ces valeurs dynamiquement.

- `setInflector($inflector, $reference)` peut être utilisée pour passer son propre inflecteur à `ViewRenderer`. Si `$reference` est à `TRUE`, alors le suffixe, le répertoire du module et la cible seront affectés en fonction des propriétés de `ViewRenderer`.

### Règles de résolution par défaut

Le `ViewRenderer` utilise certaines règles par défaut pour chercher ses scripts de vue, voyez plutôt :

- `:module` : casseMélangée et motsEnNotationCamel qui deviennent des mots séparés par des tirets, et en minuscules. "FooBarBaz" devient "foo-bar-baz".

  En interne, l'inflecteur utilise les filtres `Zend_Filter_Word_CamelCaseToDash` et `Zend_Filter_StringToLower`.

- `:controller` : casseMélangée et motsEnNotationCamel qui deviennent des mots séparés par des tirets; les tirets bas eux, se transforment en séparateur de dossier et tout est passé en minuscules. "FooBar" devient "foo-bar"; "FooBar_Admin" devient "foo-bar/admin".

  En interne, l'inflecteur utilise les filtres `Zend_Filter_Word_CamelCaseToDash`, `Zend_Filter_Word_UnderscoreToSeparator`, et `Zend_Filter_StringToLower`.

- `:action` : casseMélangée et motsEnNotationCamel qui se transforment en mots séparés par des tirets, minuscules. Les caractères non alphanumériques deviennent des tirets. "fooBar" devient "foo-bar"; "foo-barBaz" devient "foo-bar-baz".

  Pour ceci, l'inflecteur interne utilise les filtres `Zend_Filter_Word_CamelCaseToDash`, `Zend_Filter_PregReplace`, et `Zend_Filter_StringToLower`.

Enfin, l'API `ViewRenderer` vous propose aussi des méthodes pour déterminer les scripts de vue, et rendre la vue. Celles-ci se décomposent en :

- `renderScript($script, $name)` va vous permettre de spécifier pleinement le script de vue à rendre, et éventuellement un nom de segment de réponse dans lequel rendre. `ViewRenderer` s'attend à un paramètre `$script` représentant un chemin complet vers un script de vue, telle que la méthode de la vue `render()` l'attend.

  Une fois rendue, la vue utilise `noRender` pour éviter un double rendu automatisé.

  Par défaut, `Zend_Controller_Action::renderScript()` est un proxy vers la méthode `renderScript()` de `ViewRenderer`.

- `getViewScript($action, $vars)` récupère le chemin du script de vue en se basant sur les paramètres $action et $vars. `$vars` peut contenir "moduleDir", "module", "controller", "action", et "suffix"), sinon les valeurs de la requête actuelle seront utilisées.

  `getViewScript()` utilisera `viewScriptPathSpec` ou `viewScriptPathNoControllerSpec` selon le paramètre `noController`.

  Les délimiteurs apparaissant dans les modules, contrôleurs ou actions seront remplacés par des tirets ("-"). Ainsi pour un un contrôleur "`foo.bar`" et une action "`baz:bat`", il résultera un chemin de vue "`foo-bar/baz-bat.phtml`".

  Par défaut `Zend_Controller_Action::getViewScript()` est un proxy vers la méthode `getViewScript()` de `ViewRenderer`.

- `render($action, $name, $noController)` a beaucoup de responsabilités : d'abord, elle vérifie si `$name` ou `$noController` lui ont été passés, si c'est le cas, elle configure correctement les paramètres `responseSegment` et `noController` dans le ViewRenderer. Elle passe ensuite `$action`, si spécifié, à `getViewScript()`. Enfin, elle passe le script de vue calculé à `renderScript()`.

  Attention aux effets secondaires avec `render()` : les valeurs segment de réponse, et `noController` vont persister dans l'objet ViewRenderer. De plus, `noRender()` va être appelée.

  Par défaut, `Zend_Controller_Action::render()` est un proxy vers `render()` de `ViewRenderer`.

- `renderBySpec($action, $vars, $name)` vous fournit le moyen de passer des paramètres de spécification pour le dossier de script de vue. Cette méthode passe `$action` et `$vars` à `getScriptPath()`, pour en déduire un chemin qu'elle envoie alors avec `$name` à `renderScript()`.

## Exemples

### Usage de base

L'utilisation la plus basique consiste à initialiser ou et enregistrer un objet `ViewRenderer` dans le gestionnaire d'aides (helper broker), et ensuite lui passer des variables dans vos contrôleurs.

    // Dans le fichier de démarrage :
    Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

    ...

    // 'foo' module, contrôleur 'bar' :
    class Foo_BarController extends Zend_Controller_Action
    {
        // Rend bar/index.phtml par défaut, rien à faire
        public function indexAction()
        {
        }

        // Rend bar/populate.phtml avec la variable 'foo' à la valeur 'bar'.
        // L'objet de vue est rendu disponible en preDispatch().
        public function populateAction()
        {
            $this->view->foo = 'bar';
        }

        // Ne rend rien, car on demande un nouveau jeton de distribution
        public function bazAction()
        {
            $this->_forward('index');
        }

        // Ne rend rien, une redirection est demandée
        public function batAction()
        {
            $this->_redirect('/index');
        }
    }

### Conventions de noms : délimiteurs de mots dans les noms de contrôleur et d'action

Si les noms de votre contrôleur ou de votre action sont composés de plusieurs mots, le distributeur s'attend à ce qu'ils soient séparés par des caractères bien définis, dans l'URL. Le `ViewRenderer` les transforme alors en '/' pour les chemins, ou tirets '-' pour les mots. Ainsi, un appel à `/foo.bar/baz.bat` distribuera `FooBarController::bazBatAction()` dans FooBarController.php, et ceci rendra `foo-bar/baz-bat.phtml`. Un appel à `/bar_baz/baz-bat` distribuera vers `Bar_BazController::bazBatAction()` dans `Bar/BazController.php` (notez la séparation du chemin), et rend `bar/baz/baz-bat.phtml`.

Notez dans le second exemple, le module est celui par défaut, mais comme un séparateur de chemin (tiret bas ou "_") est donné, alors le contrôleur distribué devient `Bar_BazController`, dans `Bar/BazController.php`.

### Désactivation du rendu automatique

Il peut être nécessaire dans certains cas de désactiver manuellement le rendu automatique de vue effectué par ViewRenderer. Par exemple, si le contrôleur doit retourner une sortie spéciale, comme XML ou JSON. Deux options s'offrent à vous : `setNeverRender()`) et `setNoRender()`.

    // Baz controller class, bar module :
    class Bar_BazController extends Zend_Controller_Action
    {
        public function fooAction()
        {
            // Ne rend pas automatiquement cette action
            $this->_helper->viewRenderer->setNoRender();
        }
    }

    // Bat controller class, bar module :
    class Bar_BatController extends Zend_Controller_Action
    {
        public function preDispatch()
        {
            // Ne rend plus aucune action de ce contrôleur
            $this->_helper->viewRenderer->setNoRender();
        }
    }

Utiliser `setNeverRender()`), pour désactiver totalement le rendu automatique de vue vous fera perdre un des avantages majeur de `ViewRenderer`.

### Choix d'un script de vue différent

Il peut arriver que vous éprouviez le besoin de rendre un script de vue différent de celui correspondant à l'action en cours de distribution. Par exemple, un contrôleur qui possède deux actions ajout et édition, qui sont susceptibles toutes les deux de rendre le même script de vue. Utilisez alors `setScriptAction()`, `setRender()`, ou appelez l'aide ViewRenderer directement :

    // Bar controller class, foo module :
    class Foo_BarController extends Zend_Controller_Action
    {
        public function addAction()
        {
            // Rend 'bar/form.phtml' plutôt que 'bar/add.phtml'
            $this->_helper->viewRenderer('form');
        }

        public function editAction()
        {
            // Rend 'bar/form.phtml' au lieu de 'bar/edit.phtml'
            $this->_helper->viewRenderer->setScriptAction('form');
        }

        public function processAction()
        {
            // un peu de validation...
            if (!$valid) {
                // Rend 'bar/form.phtml' à la place de 'bar/process.phtml'
                $this->_helper->viewRenderer->setRender('form');
                return;
            }

            // continue le processus...
        }
    }

### Modification de l'objet de vue

Si vous désirez modifier l'objet de vue absorbé par `ViewRenderer`, pour par exemple ajouter un chemin vers des aides spécifique, ou spécifier l'encodage, vous pourriez par exemple récupérer l'objet de vue depuis le `ViewRenderer`, ou dans un contrôleur.

    // Bar controller class, foo module :
    class Foo_BarController extends Zend_Controller_Action
    {
        public function preDispatch()
        {
            // change l'encodage de la vue
            $this->view->setEncoding('UTF-8');
        }

        public function bazAction()
        {
            // Récupère l'objet de vue, et lui passe la fonction
            // d'2chappement 'htmlspecialchars'
            $view = $this->_helper->viewRenderer->view;
            $view->setEscape('htmlspecialchars');
        }
    }

## Utilisation avancée

### Changement des spécifications de dossier

Dans certains cas, il peut être nécessaire d'utiliser un chemin absolu, fixe. Par exemple si vous ne donnez accès à vos graphistes qu'à un seul dossier, en utilisant un moteur de template tel que Smarty.

Pour ceci, imaginons que le base path soit fixé à "/opt/vendor/templates", et que vous voulez que vos scripts de vues soit référencés par ":moduleDir/:controller/:action.:suffix"; si le paramètre `noController` est activé, vous désirez utiliser le dossier plus haut ":action.:suffix". Enfin, vous désirez un suffixe en "tpl" :

    /**
     * Dans le fichier de démarrage :
     */

    // Une implémentation personnalisée de la vue
    $view = new ZF_Smarty();

    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
    $viewRenderer->setViewBasePathSpec('/opt/vendor/templates')
                 ->setViewScriptPathSpec(':module/:controller/:action.:suffix')
                 ->setViewScriptPathNoControllerSpec(':action.:suffix')
                 ->setViewSuffix('tpl');
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

### Rendu de plusieurs scripts de vue dans une même action

Afin de rendre plusieurs scripts de vue dans une même action, appelez tout simplement plusieurs fois `render()` :

    class SearchController extends Zend_Controller_Action
    {
        public function resultsAction()
        {
            // Considérons $this->model comme étant un modèle valide
            $this->view->results = $this->model
                                        ->find($this->_getParam('query', '');

            // render() est proxiée vers ViewRenderer
            // Rend d'abord un formulaire, puis un résultat
            $this->render('form');
            $this->render('results');
        }

        public function formAction()
        {
            // Rien : ViewRenderer rend automatiquement un script de vue
        }
    }