Close search
Hoa

Hack book de Hoa\View

Les vues sont des systèmes complexes et très variés. Hoa\View propose une interface capable de répondre à la majorité des besoins à travers un mécanisme souple et extensible.

Table des matières

  1. Introduction
  2. Une seule interface
  3. Exemple
  4. Conclusion

Introduction

Une vue est un mécanisme permettant d'agencer graphiquement des données à l'utilisateur. Il existe plusieurs approches, assez différentes les unes des autres et qui peuvent parfois être très complexes. La bibliothèque Hoa\View propose une seule interface qui regroupe l'essentiel des fonctionnalités afin de pouvoir intégrer une vue à n'importe quelle autre bibliothèque.

Une seule interface

Le mécanisme principal d'une vue est presque toujours le même :

Nous arrivons à un total de quatre notions : des données, un « agenceur », un routeur et un canal de sortie dans lequel écrire le résultat.

La bibliothèque Hoa\View ne propose qu'une seule interface : Hoa\View\Viewable. Cette dernière définit une méthode pour chacune des quatres notions précédentes :

Et c'est tout ! Pour être plus précis, voici les retours attendus pour chacune de ces méthodes :

Même si cette approche peut sembler simpliste au premier abord, cette interface couvre l'ensemble des besoins, et vous permet d'écrire la vue que vous désirez avec vos propres outils ou avec des outils déjà existants.

Exemple

La bibliothèque Hoa\Xyl utilise Hoa\View\Viewable, mais elle est un peu complexe pour servir de premier exemple (ce qui ne vous empêche pas d'aller y jeter un œil !). C'est pourquoi nous allons donner un exemple plus simple et plus basique.

Commençons par définir ce que nous voulons. Nous souhaitons mettre en place un mécanisme de vues et de sous-vues répondant aux caractéristiques suivantes :

Commençons par écrire la classe SuperView :

class SuperView implements Hoa\View\Viewable
{
    protected $_in     = null;
    protected $_out    = null;
    protected $_data   = null;
    protected $_router = null;

    public function __construct(
        $in,
        Hoa\Stream\IStream\Out $out,
        Hoa\Router\Router $router = null,
        StdClass $data            = null
    ) {
        if (null === $data) {
            $data = new StdClass();
        }

        $this->_in     = $in;
        $this->_out    = $out;
        $this->_data   = $data;
        $this->_router = $router;

        return;
    }

    public function getOutputStream()
    {
        return $this->_out;
    }

    public function getData()
    {
        return $this->_data;
    }

    public function render()
    {
        $data   = $this->getData();
        $router = $this->getRouter();

        ob_start();
        require $this->_in;
        $content = ob_get_contents();
        ob_end_clean();

        $this->getOutputStream()->writeAll($content);

        return;
    }

    public function getRouter()
    {
        return $this->_router;
    }
}

Très classique. Notre constructeur demande un fichier à lire, un flux de sortie, un routeur qui est optionel, tout comme des données. Nous retrouvons nos méthodes getOutputStream, getData, render et getRouter. La méthode render va inclure le fichier $in en ayant préalablement mis à sa disposition deux variables (en plus de $this), à savoir $data et $router, pour lui faciliter l'écriture. Enfin, la ligne 46 utilise le flux de sortie pour écrire les données.

Testons en écrivant un fichier Test.php :

// Output.
$output = new Hoa\Http\Response();

// Router.
$router = new Hoa\Router\Http();
$router->get('a', '/Foo\.html');

// View.
$superview   = new SuperView('Out.phtml', $output, $router);

// Data.
$data        = $superview->getData();
$data->title = 'foobar';
$data->foo   = (object) ['bar' => 'baz', 'qux' => 'hop'];

// Render.
$superview->render();

Et enfin, écrivons notre fichier Out.phtml :

<h1><?php echo $data->title; ?></h1>

Observons maintenant le résultat de l'exécution de Test.php :

$ php Test.php
<h1>foobar</h1>

Excellent. Maintenant ajoutons le moyen de créer des sous-vues avec la méthode import par exemple. Ainsi, nous ajoutons dans la classe SuperView :

    public function import($in, $data = null)
    {

        $new = new static(
            $in,
            $this->getOutputStream(),
            $this->getRouter(),
            $data
        );
        $new->render();

        return;
    }

Nous utilisons le mot-clé static pour faire une référence statique à la classe elle-même ou à ses enfants (voir Late Static Bindings).

Nous voyons apparaitre nos vues imbriquées. Modifions Out.phtml pour qu'il utilise Sub.phtml qui sera une nouvelle vue. À cette sous-vue, nous ne donnerons qu'une partie des données (uniquement foo) :

<h1><?php echo $data->title; ?></h1>

<?php $this->import('Sub.phtml', $data->foo); ?>

Et dans Sub.phtml, nous pouvons utiliser nos données comme bon nous semble. En bonus, nous allons utiliser le routeur pour créer un lien (pour rappel, notre routeur définissait la règle a vers /Foo.html). Ainsi :

<p>Sub-view! This is a link:
   <a href="<?php echo $router->unroute('a'); ?>">click click</a>!</p>

<ul>
<?php foreach($data as $key => $value): ?>
    <li><?php echo $key, ' => ', $value; ?></li>
<?php endforeach; ?>
</ul>

Exécutons à nouveau Test.php pour voir le résultat :

$ php Test.php
<h1>foobar</h1>

<p>Sub-view! This is a link:
   <a href="/Foo.html">click click</a>!</p>

<ul>
    <li>bar => baz</li>
    <li>qux => hop</li>
</ul>

Notre sous-vue est en place, avec notre lien et nos données isolées (nous itérons sur toutes les données sans pour autant avoir les données de title, uniquement celles de foo).

Cette classe SuperView est très basique mais les performances sont intéressantes car le résultat du rendu est envoyé directement dans le flux de sortie, sans jamais manipuler de grosse chaîne de caractères. Cela devient particulièrement intéressant quand nous manipulons beaucoup de sous-vues imbriquées par exemple.

Conclusion

La bibliothèque Hoa\View ne définit qu'une seule interface avec quatre méthodes. À l'usage, nous nous rendons compte que ces méthodes sont suffisantes pour décrire et manipuler plusieurs systèmes de vue potentiellement complexes.

Une erreur ou une suggestion sur la documentation ? Les contributions sont ouvertes !

Comments

menu