Ajouter des fonctionnalités à Temphplate

On a vu que l'on pouvait déjà personnaliser Temphplate de nombreuses façons:

C'est déjà plus que suffisant (et même superflu) pour la plupart des utilisateurs. Pourtant on peut aller encore plus loin. En effet on peut même ajouter sa propre syntaxe de structure. Pour rappel, deux types de structure existent:

Vous pouvez ajouter vos propres structures de blocs {tag de debut:parametre} ... {tag de fin}. Ces structures auront leur propre résultat que vous définissez, et sont bien sûr imbricables.

Comment faire ?

La définition d'un nouveau "tag de structure de bloc" se fait en créant une classe qui héritera de Temphplate. Dans cette classe, vous définirez la syntaxe et la méthode d'analyse pour cette nouvelle structure.

class MonTemphplate extends Temphplate
{

    function MonTemphplate ( $include_dir = '' )
    {
        // Hériter de Temphplate
        parent::Temphplate($include_dir);
        // Définition de la syntaxe de ma nouvelle structure
        $this->addStructure('STRUCTURE', 'TAG_DEBUT', 'TAG_FIN');
    }

    function parseStructureSTRUCTURE ( $tree , $assigns , $path = '' )
    {
        // Méthode qui analysera la structure et génèrera le code HTML adéquat
        return TRUE;
    }

}

Décrire la syntaxe de ma nouvelle structure

Il s'agit d'un simple appel à la méthode addStructure. Cette méthode prend 3 paramètres dans cet ordre:

Définir le résultat de l'analyse de cette structure

Il s'agit ici de définir le comportement de la méthode parseStructureSTRUCTURE. Cette partie est bien évidemment délicate, et il faut comprendre un peu du fonctionnement interne de Temphplate pour y arriver correctement. Tout d'abord, cette méthode prend 3 paramètres:

La méthode renvoie un booléen qui indique si l'analyse a réussi ou échoué. Elle doit modifier la variable $this->html_result. Avant l'appel de la méthode, cette variable a pour valeur le code HTML correspondant à tout ce qui précède le bloc courant. Il suffit donc d'ajouter à cette variable le code correspondant à ce bloc.

Conseils

Faites de nombreux tests pour vérifier votre implémentation avant de la publier, suivez l'exemple ci-dessous, lisez bien le source de Temphplate pour comprendre comment est généré le code HTML.

Parfois vous allez avoir un code HTML complètement décalé. C'est l'erreur la plus fréquente, c'est simplement que vous n'avez pas modifié $this->html_result au bon moment (avant, ou après l'appel à parent::parseTree()). Il n'y a pas de recette miracle: il faut refléchir et penser son code de façon bien structurée.

Une autre erreur fréquente est du code HTML généré incomplet. Vérifiez là encore la façon dont vous avez modifié $this->html_result, en particulier vérifiez que vous avez bien AJOUTÉ des morceaux et non supprimé. On oublie vite un point (concaténation).

Note: si un utilisateur de votre classe désire modifier la syntaxe de votre structure, il lui suffira d'affecter à $tpl->syntax['structure'] le nom du nouveau tag ouvrant, et à $tpl->syntax['endstructure'] le nom du nouveau tag fermant, exactement comme pour des structures 'block' ou 'if'.

Exemple

Objectif

On veut définir une structure qui permette d'intégrer un tableau associatif simplement, autant côté modèle que côté PHP. On va appeler cette structure 'loop'.

Un bloc de type 'loop' sera répété autant de fois que le tableau n'a d'éléments, et à chaque répétition les variables 'key' et 'val' seront respectivement assignés à la clé, et à la valeur de l'élément courant du tableau. Le paramètre du tag d'ouverture est le nom du tableau à utiliser.

Exemple:

Modèle:
... {loop:tableau} [{key}] => {val} {end:loop} ...

Côté PHP:
$tableau = array('un'=>'uno', 'deux'=>'due', 'trois'=>'tre');

Résultat attendu:
...  [un] => uno  [deux] => due  [trois] => tre  ...

Mise en oeuvre

Voir ici le code source.

Explications...

class MonTemphplate extends Temphplate

Notre nouveau moteur s'appellera "MonTemphplate", il doit hériter de Temphplate (on pourrait aussi le faire hériter de BasicTemphplate, quoiqu'on n'aura alors accès qu'à très peu de fonctionnalités à la base, mais cela ne dérange absolument pas la création d'une nouvelle structure).

function MonTemphplate ( $include_dir = '' )

Le constructeur, qui prend comme celui de la classe mère une liste de répertoires facultative.

parent::Temphplate($include_dir);

On hérite de Temphplate, il ne faut pas oublier de concrétiser cet héritage par l'appel du constructeur de la classe mère.

$this->addStructure('loop');

Les choses sérieuses commencent. Ici on déclare notre nouvelle structure. Elle s'appellera "loop". Le tag ouvrant d'un bloc de type loop est {loop:paramètre}. Le tag fermant est {end:loop}.

function parseStructureLoop ( $tree , $assigns , $path )

Il faut définir cette méthode, sinon la déclaration de notre structure aurait échoué. Elle doit avoir comme décrit plus haut trois paramètres.

$nom = $tree['param'];

Le nom du tableau a récupérer est donné par le paramètre du tag ouvrant de notre bloc.

if (!isset($GLOBALS[$nom]))
    $this->error('La variable "'.$tree['param'].'" est indéfinie', E_USER_ERROR);
if (!is_array($GLOBALS[$nom]))
    $this->error('La variable "'.$tree['param'].'" n\'est pas un tableau', E_USER_ERROR);

Si la variable globale portant le nom passé en paramètre du tag ouvrant de notre bloc n'existe pas, ou n'est pas un tableau, on lève une erreur fatale.

$array = $GLOBALS[$nom];

Sinon on récupère son résultat dans la variable $array.

foreach ($array as $key => $val) { ... }

Pour chaque élément du tableau indexé sur la clé $key et ayant la valeur $val, on va répéter cette action.

$assigns['key'] = $key;
$assigns['val'] = $val;

On crée des assignations en modifiant la variable $assigns. Si l'on parse le contenu du bloc avec le paramètre d'assignements ainsi modifié, le moteur va remplacer les variables {key} et {val} par leur valeurs respectives.

if (!$this->parseTree($tree['contents'], $assigns, $path)) return FALSE

C'est d'aileurs ce qu'on fait. On appelle la méthode parseTree sur le contenu du bloc $tree['contents'], avec les assignements modifiés $assigns, et le chemin non modifié $path. Cela ajoute à $this->html_result le contenu du bloc avec les variables {key} et {val} modifiées. Si cet appel échoue, on arrête là et on remonte l'erreur en retournant FALSE.

Comme cet appel à parseTree est effectué pour chaque valeur du tableau, le résultat de l'analyse du contenu sera ajouté d'autant, ce qui est bien le comportement souhaité.

Résultats

Modèle:
    [ {loop:races}- {key} est un {val} {end:loop}- ]
Tableau [source]:
    $tableau = array('Droopy'=>'chien', 'Ratus'=>'rat', 'Einstein'=>'génie');
Résultat:
    [ - Droopy est un chien - Ratus est un rat - Einstein est un génie - ]

Bravo :) Testons maintenant l'imbrication

Modèle:
    {loop:tab1} tab1[{key}]={val}, [ {loop:tab2}tab2[{key}]={val} {end:loop}]{end:loop}
Tableaux:
    $tab1 = array('a'=>'A', 'b'=>'B', 'c'=>'C');
    $tab2 = array(1=>'un', 2=>'deux');
Résultat:
    tab1[a]=A, [ tab2[1]=un tab2[2]=deux ]
    tab1[b]=B, [ tab2[1]=un tab2[2]=deux ]
    tab1[c]=C, [ tab2[1]=un tab2[2]=deux ]
    

Vous commencez à entrapercevoir l'intérêt d'une tel extension... Si on ajoutait à cette structure quelques petits détails comme par exemple des variables {is_first} (TRUE si l'élément est le premier du tableau) et {is_false} (TRUE si l'élément est le dernier du tableau), et éventuellement une notion de chemin comme pour les blocs (il suffirait de s'inspirer de la méthode BasicTemphplate::parseStructureBlock), on pourrait obtenir une structure très puissante de façon finalement simplissime.

Un avertissement cependant, chaque fois que l'on ajoute une fonctionnalité à Temphplate on s'éloigne généralement de l'esprit initial des templates de séparer la présentation du code. La structure 'loop' est typiquement un élément qui devrait être géré avec des blocs côtés présentation, et avec des setBlock côté code. Cependant cette fonctionnalité existe, pourquoi s'en priver si vous souhaitez l'utiliser ?



Generation time: 26.317ms