La prochaine version majeure de TYPO3 (qui se fait attendre) sera basée sur un moteur de template appelé FLUID.
Beaucoup attendront la sortie de ce nouveau TYPO3 pour s’y mettre, mais comme dirait l’autre l’avenir est déjà parmi nous…
En effet, FLUID est utilisable depuis la version 4.3 de TYPO3, et depuis la version 4.5 FLUID est utilisable de manière autonome (Standalone View) directement depuis n’importe quel plugin.
A) Présentation
Pour ceux qui ne connaissent pas encore FLUID, c’est un moteur de template assez proche de Smarty.
Cela permet d’inclure des directives directement dans le template HTML, comme par exemple:
– spécifier l’emplacement des variables PHP,
– organiser des boucles dans le template HTML,
– mettre en place des conditions d’affichage de certains éléments,
ainsi que la mise en place de fonctionnalités simples comme par exemple :
– l’alternance des couleurs dans une liste d’éléments,
– l’affichage d’un libellé seulement si le champ associé au libellé n’est pas vide,
– la mise en place d’un lien en fonction des informations de l’enregistrement.
Toutes ces fonctionnalités devaient être prévues dans le code PHP, sans quoi elles ne pouvaient exister.
Ce moteur de template étant lié au noyau TYPO3, il est possible d’accéder aux fonctions internes du CMS comme par exemple :
– les liens typolinks (voir article sur le typolink),
– le système de traduction,
– le typoscript,
– le traitement stdWrap (via le typoscript),
– l’élargissement des fonctionnalités de base avec des plugins (ViewHelper).
Un système équivalent appelé Twig est utilisé par Symfony2.
B) Mise en situation
Présentation d’un cas concret
Nous allons voir un cas concret d’utilisation avec la méthode « à l’ancienne » (des vieux 🙂 ) puis avec FLUID et enfin faire un tour d’horizon des fonctionnalités de FLUID.
Prenons l’exemple de l’affichage d’une liste de tâches à traiter.
On affichera en premier une liste simple basée sur les éléments d’une table.
On ajoutera ensuite des informations dans la liste.
On ajoutera enfin des informations de contact (téléphone email en fonction de la disponibilité du contact).
Attention, nous ne tiendrons pas compte d’éventuels problèmes de cache, d’encodage, de traduction ou de valeur en dur dans le code, ce n’est qu’un exemple; il faut que cela reste simple à comprendre.
C) Méthode « à l’ancienne »
Étape 1 : Principes et préparation du code PHP
Pour les « anciens » de TYPO3, le passage par des templates signifiait la création d’un fichier HTML souvent organisé en blocs (subparts) dans lesquels on trouve des marqueurs ou d’autres subparts.
Le but du jeu était de charger le template, de récupérer la subpart correspondant à la fonctionnalité à afficher, et de remplacer les marqueurs par des valeurs utiles (depuis la base de données par exemple).
Concernant le code PHP, il n’y a pas besoin de préparation du code pour pouvoir utiliser la méthode traditionnelle.
La classe du plugin doit simplement hériter de la classe « tslib_pibase » qui rend accessible les méthodes de templating.
Pour la simplicité du code, tous les textes ont été laissés en dur… oui c’est mal.
Étape 2 : affichage minimal
On va donc essayer d’afficher une liste d’éléments, basée sur des enregistrements dans la base de données.
Cette liste est simplement composée d’un titre.
Le but est d’analyser la composition du template HTML ainsi que le code PHP nécessaire au traitement de cette liste.
Template HTML :
<!-- ###SUBPART-LISTE### debut --> <ul> ###LISTE_TACHES### </ul> <!-- ###SUBPART-LISTE### fin --> <!-- ###SUBPART-TACHE### debut --> <li>###TACHE_TITRE###</li> <!-- ###SUBPART-TACHE### fin -->
Code PHP associé :
<?php //..... //table à traiter $table='tx_test_element'; //chargement du template $this->template='EXT:'.$this->extKey.'/pi1/template.html'; $this->templateCode = $this->cObj->fileResource($this->template); //on recupere le code HTML d'une seule ligne $templateLigne = $this->cObj->getSubpart($this->templateCode, "###SUBPART-TACHE###"); //on lance la requete SQL $lignes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( '*', $table, ' 1 '.$this->cObj->enableFields($table) ); //on prépare la variable de concaténation $codeLignes=''; //pour chaque enregistrement foreach($lignes as $uid=>$ligne){ //on prepare le tableau des marqueurs HTML $markerLigne=array(); //on remplie le tableau de marqueurs $markerLigne['###TACHE_TITRE###']=$ligne['titre']; //on récupère le code HTML d'une seule ligne $codeLignes.=$this->cObj->substituteMarkerArray($templateLigne,$markerLigne); //on supprime le tableau unset($markerLigne); } //on récupère le code final $templateListe = $this->cObj->getSubpart($this->templateCode, "###SUBPART-LISTE###"); //on remplace la liste $content=$this->cObj->substituteMarkerArray($templateListe,array( '###LISTE_TACHES###'=>$codeLignes )); //..... ?>
Résultat généré :
Étape 3 : ajout d’informations complémentaires
Imaginons maintenant que l’on souhaite afficher le numéro de chaque tache, le nom du responsable de la tache et mettre en place une alternance de couleur dans la liste.
Template HTML :
<!-- ###SUBPART-LISTE### debut --> <ul> ###LISTE_TACHES### </ul> <!-- ###SUBPART-LISTE### fin --> <!-- ###SUBPART-TACHE### debut --> <li class="classe###CLASSE###">###COMPTEUR### - ###TACHE_TITRE### ( ###TACHE_RESPONSABLE### )</li> <!-- ###SUBPART-TACHE### fin -->
Code PHP associé :
<?php //..... //table a traiter $table='tx_test_element'; //chargement du template $this->template='EXT:'.$this->extKey.'/pi1/template.html'; $this->templateCode = $this->cObj->fileResource($this->template); //on récupère le code HTML d'une seule ligne $templateLigne = $this->cObj->getSubpart($this->templateCode, "###SUBPART-TACHE###"); //on lance la requête SQL $lignes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( '*', $table, ' 1 '.$this->cObj->enableFields($table) ); //on prépare la variable de concaténation $codeLignes=''; //on prépare le compteur de ligne $compteur=1; //pour chaque enregistrement foreach($lignes as $uid=>$ligne){ //on prépare le tableau des marqueurs HTML $markerLigne=array(); //on remplie le tableau de marqueurs $markerLigne['###TACHE_TITRE###']=$ligne['titre']; $markerLigne['###TACHE_RESPONSABLE###']=$ligne['responsable']; $markerLigne['###COMPTEUR###']=$compteur; $markerLigne['###CLASSE###']=$compteur%2; //on récupère le code HTML d'une seule ligne $codeLignes.=$this->cObj->substituteMarkerArray($templateLigne,$markerLigne); //on supprime le tableau unset($markerLigne); //on incrémente le compteur $compteur++; } //on récupère le code final $templateListe = $this->cObj->getSubpart($this->templateCode, "###SUBPART-LISTE###"); //on remplace la liste $content=$this->cObj->substituteMarkerArray($templateListe,array( '###LISTE_TACHES###'=>$codeLignes )); //..... ?>
Résultat généré :
Étape 4 : Ajout d’un lien de contact
Imaginons maintenant que l’on souhaite afficher une phrase d’introduction, et que le besoin apparaisse d’avoir, en fonction de la disponibilité du responsable, l’affichage de son numéro de téléphone du responsable, ou uniquement un lien vers son email.
Template HTML :
<!-- ###SUBPART-LISTE### debut --> <p>###INTRO###</p> <ul> ###LISTE_TACHES### </ul> <!-- ###SUBPART-LISTE### fin --> <!-- ###SUBPART-TACHE### debut --> <li class="classe###CLASSE###">###COMPTEUR### - ###TACHE_TITRE### ( ###TACHE_RESPONSABLE### : ###CONTACT### )</li> <!-- ###SUBPART-TACHE### fin -->
Code PHP associé :
<?php //..... //table a traiter $table='tx_test_element'; //chargement du template $this->template='EXT:'.$this->extKey.'/pi1/template.html'; $this->templateCode = $this->cObj->fileResource($this->template); //on récupère le code HTML d'une seule ligne $templateLigne = $this->cObj->getSubpart($this->templateCode, "###SUBPART-TACHE###"); //on lance la requete SQL $lignes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( '*', $table, ' 1 '.$this->cObj->enableFields($table) ); //on prépare la variable de concaténation $codeLignes=''; //phrase d'intro $intro='Liste de taches sur le projet. <br/>Il y a '.count($lignes).' résultat'; //on gere le cas du S en cas de plusieurs resultats if (count($lignes)>1){ $intro.='s'; } $intro.=' à traiter.'; //on prépare le compteur de ligne $compteur=1; //pour chaque enregistrement foreach($lignes as $uid=>$ligne){ //on prépare le tableau des marqueurs HTML $markerLigne=array(); //on remplie le tableau de marqueurs $markerLigne['###TACHE_TITRE###']=$ligne['titre']; $markerLigne['###TACHE_RESPONSABLE###']=$ligne['responsable']; $markerLigne['###COMPTEUR###']=$compteur; $markerLigne['###CLASSE###']=$compteur%2; //si l'utilisateur est disponible if ($ligne['disponible']==='OUI'){ //on utilise le téléphone $lienContact=$ligne['telephone']; }else{ //sinon on génère un lien vers l'email $lienContact = $GLOBALS['TSFE']->cObj->typoLink( 'Il est à la pêche, spammez son email', array('parameter'=>$ligne['email']) ); } $markerLigne['###CONTACT###']=$lienContact; //on récupère le code HTML d'une seule ligne $codeLignes.=$this->cObj->substituteMarkerArray($templateLigne,$markerLigne); //on supprime le tableau unset($markerLigne); // on incrémente le compteur $compteur++; } //on récupère le code final $templateListe = $this->cObj->getSubpart($this->templateCode, "###SUBPART-LISTE###"); //on remplace la liste $content=$this->cObj->substituteMarkerArray($templateListe,array( '###LISTE_TACHES###'=>$codeLignes, '###INTRO###'=>$intro )); //..... ?>
Résultat généré :
Étape 5 : Conclusion pibase:
Nous voyons que chaque ajout de fonctionnalité nécessite une modification du code PHP.
Une fonctionnalité comme l’affichage conditionnel d’une variable ou rajouter un « S » dans une phrase présentant un nombre de résultats nécessite une modification dans le code PHP, tout comme la mise en place d’une alternance de couleur ou de classe CSS, etc…
Dans un plugin complexe cela peut devenir un casse-tête de ne rien « casser » lorsqu’on ajoute une fonctionnalité.
Un plugin comme le TT_news, par exemple, comporte un nombre impressionnant de lignes et prend en compte beaucoup de fonctionnalités, des plus complexes (ex les différents modes de fonctionnement) au plus légères ( formatage des dates). Mais l’ajout de fonctionnalité dans ce code semble complexe si on n’a pas passé des jours à lire le code pour tout comprendre.
Regardons maintenant la même problématique, mais cette fois en utilisant le moteur de template FLUID.
D) Méthode « Avec FLUID »
Étape 1: Principes et préparation du code PHP
Nous avons vu, dans la première partie, comment utiliser les templates via les fonctions de templating « $this->cObj->getSubpart(….) » et « $this->cObj->substituteMarkerArray(….)« .
Nous allons maintenant repartir de zéro, et utilise les templates FLUID.
Pour pouvoir utiliser les fonctionnalités offertes par FLUID il est nécessaire de (au choix) :
– dépendre des classes extbase et FLUID
– instancier le système FLUID de manière autonome.
On va se baser sur la 2nd possibilité.
tutoriel : utilisation de FLUID sans extbase
Le code PHP de base :
// on fait appelle au fonctionnement en standalone $view = t3lib_div::makeInstance('Tx_Fluid_View_StandaloneView'); // Emplacement du template $this->template = t3lib_extMgm::extPath(strtolower($this->extKey)) . 'Resources/Private/Layouts/template.html'; //on specifie l'emplacement du template $view->setTemplatePathAndFilename($this->template); //on assigne une variable $view->assign('variable', $variable); //on genere le rendu HTML return $view->render();
Etape 2 : affichage minimal
On va donc essayer à nouveau d’afficher une liste d’éléments, basée sur des enregistrements dans la base de données.
Cette liste est toujours composée, pour commencer, d’un titre.
Template HTML :
<ul> <f:for each="{taches}" as="tache" key="number"> <li >{tache.titre}</li> </f:for> </ul>
Code PHP associé :
<?php //..... //table a traiter $table='tx_test_element'; //on cree l'objet fluid $view = t3lib_div::makeInstance('Tx_Fluid_View_StandaloneView'); // instanciate standalone view // Emplacement du template $this->template = t3lib_extMgm::extPath(strtolower($this->extKey)) . 'Resources/Private/Layouts/template.html'; //on specifie l'emplacement du template $view->setTemplatePathAndFilename($this->template); //on lance la requete SQL $lignes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $table, ' 1 '.$this->cObj->enableFields($table) ); //on assigne les donnees dans l'objet tache $view->assign('taches', $lignes); //on genere le rendu HTML return $view->render(); ?>
Résultat généré :
Etape 3 : ajout d’informations complémentaires
On va afficher le numéro de chaque tâche, le nom du responsable de la tâche et mettre en place une alternance de couleur dans la liste.
Template HTML :
<ul> <f:for each="{taches}" as="tache" key="tachekey" iteration="tacheiteration"> <li class="classe<f:cycle values="{0: '1', 1: '0'}" as="cycle">{cycle}</f:cycle>">{tacheiteration.cycle} - {tache.titre} ( {tache.responsable} )</li> </f:for> </ul>
Code PHP associé :
<?php //..... //table a traiter $table='tx_test_element'; //on cree l'objet fluid $view = t3lib_div::makeInstance('Tx_Fluid_View_StandaloneView'); // instanciate standalone view // Emplacement du template $this->template = t3lib_extMgm::extPath(strtolower($this->extKey)) . 'Resources/Private/Layouts/template.html'; //on specifie l'emplacement du template $view->setTemplatePathAndFilename($this->template); //on lance la requête SQL $lignes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $table, ' 1 '.$this->cObj->enableFields($table) ); //on assigne les données dans l'objet tache $view->assign('taches', $lignes); //on génère le rendu HTML return $view->render(); ?>
Résultat généré :
On peut remarquer qu’il n’y a aucun changement du code PHP.
Les seuls changements sont dans le template HTML avec l’ajout de marqueurs.
Etape 4 : Ajout d’un lien de contact
On ajoute une phrase d’introduction avec le nombre de résultats ainsi que l’affichage, en fonction de la disponibilité, du numéro de téléphone du responsable ou uniquement un lien vers son email.
Template HTML :
<p>Liste de taches sur le projet. <br/>Il y a <f:count subject="{taches}" /> résultat<f:if condition="<f:count subject='{taches}' /> > 1"><f:then>s</f:then></f:if> à traiter.</p> <ul> <f:for each="{taches}" as="tache" key="tachekey" iteration="tacheiteration"> <li class="classe<f:cycle values="{0: '1', 1: '0'}" as="cycle">{cycle}</f:cycle>"> {tacheiteration.cycle} - {tache.titre} ( {tache.responsable} : <f:if condition="{0:tache.disponible} == {0:'OUI'}"> <f:then>{tache.telephone}</f:then> <f:else><f:link.email email="{tache.email}">Il est à la pêche, spammez son email</f:link.email></f:else> </f:if> ) </li> </f:for> </ul>
Code PHP associé :
<?php //..... //table à traiter $table='tx_test_element'; //on crée l'objet fluid $view = t3lib_div::makeInstance('Tx_Fluid_View_StandaloneView'); // instanciate standalone view // Emplacement du template $this->template = t3lib_extMgm::extPath(strtolower($this->extKey)) . 'Resources/Private/Layouts/template.html'; //on spécifie l'emplacement du template $view->setTemplatePathAndFilename($this->template); //on lance la requete SQL $lignes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $table, ' 1 '.$this->cObj->enableFields($table) ); //on assigne les données dans l'objet tâche $view->assign('taches', $lignes); //on genere le rendu HTML return $view->render(); ?>
Résultat généré :
Etape 5 : Conclusion FLUID:
Nous voyons qu’aucun ajout de fonctionnalité ne nécessite une modification du code PHP, tout se passe au niveau du template HTML.
Le but n’est pas de mettre un maximum de code dans le template, mais simplement de placer ce qui relève de l’affichage au niveau du template HTML comme par exemple :
– l’ajout conditionnel d’un « S » à la fin d’un mot
– l’affichage conditionnel d’une variable
– l’alternance de classes CSS ou de valeurs dans une liste
– la mise en place de liens ou de transformations d’affichage (mise en forme HTML, formatage des dates, formatage des numériques, etc).
cela permet de garder le code PHP aussi simple que possible, tout en gardant un résultat complexe.
E) Évolution « Avec FLUID »
Nous avons vu qu’avec FLUID il est possible d’avoir un résultat identique à la méthode Pibase avec un code PHP beaucoup plus simple.
Cela impose, à nouveau, une petite remise en cause personnelle pour apprendre les éléments de base de FLUID, mais le jeu en vaut la chandelle comme on dit.
Avoir des codes PHP simples permet une meilleure gestion des développements, et donc de réduire les bugs de fonctionnement.
Les modifications de présentation effectuées suite à une demande client pour adapter l’affichage sont ainsi réduites à de l’édition de template.
Les fonctionnalités de base du moteur de template FLUID permettent de gérer directement dans le template HTML des fonctionnalités intéressantes comme:
- Formatage des dates : f:format.date
- Formatage des montants : f:format.currency
- Formatage des liens interne : f:link.page
- Transformation HTML : f:format.html
- Gestion des traductions : f:translate
Ensuite, il est aussi possible d’étendre les fonctionnalités de base en créant ses propres fonctions (appelées « ViewHelper ») ou d’en récupérer via des extensions sur le TER de TYPO3.
Enfin, si une extension permet de spécifier l’emplacement du template HTML à utiliser (via typoscript ou flexform par exemple), il est alors possible de rajouter des fonctionnalités complexes dans le rendu final, tout en limitant le risque de casser quelque chose lors de la mise à jour de l’extension d’origine (Voir l’article présentant l’ajout de fonctionnalité à un plugin existant)
Très bon tuto pour débuter sur Fluid !
Merci Olivier ! 😉
Excellent tuto Olivier. En plus ce genre de tuto de qualité manque sur le Net …. Continue 🙂 Excellent !
Le meilleur tuto pour bien débuter avec Fluid. Merci.