Drupal theme api : Hook into the theme (partie 2)

drupal

Dans mon précédent article, j’ai présenté rapidement la theme layer et les fonctions qui permettent de déclarer de nouveaux theme hooks, les fonctions de thème, et comment les surcharger. Nous allons maintenant voir comment utiliser cette même api pour définir de nouveaux templates, ainsi que les fonctions de preprocess.

Introduction

Nous allons reprendre notre module Foo avec l’implémentation de hook_theme(). Cette fois-ci nous lui ajouterons un template.

Templates

<?php //@file foo.module
/**
* Implements hook_theme()
*/
function foo_theme() {
return array(
‘bar’ => array(
‘variables’ => array(‘baz’ => null),
‘template’ => ‘foo-template’,
),
);
}

Jusqu’à présent nous utilisions la fonction theme_bar(). Or ici elle n’a plus de raison d’être car c’est le template « foo-template.tpl.php » qui devient responsable de la sortie html. Il doit être placé dans le même répertoire que le module.

Les fonctions de thème qui ont un template n’ont pas besoin de déclaration de type theme_HOOK().

Il est par contre possible de surcharger une fonction de thème avec un template. De base, Drupal cherchera un template avec un nom identique au thème hook. Il serait ainsi possible de surcharger theme_bar() dans notre thème avec le template bar.tpl.php (s’il n’y a pas de template déclaré dans foo_theme).

Si vous suivez toujours, on va maintenant parler des étapes dîtes de preprocess.

Remarque : le moteur de template est définit par un fichier « .engine » dans le dossier /themes/engines. Par défaut il s’agit du classique PHPTemplate (phptemplate.engine). Quelques alternatives existent pour Drupal 7, notamment Twig et qui est devenu le moteur par défaut sous Drupal 8. Dans notre « .info » de thème on aurait ainsi pu spécifier le moteur utilisé : engine = php.

Preprocess

Que l’on utilise un template ou une fonction de thème, la fonction theme() est également chargée d’appeler un ensemble de fonctions qui préparent les variables avant leur utilisation. Ces fonctions sont de deux formes : hook_preprocess() et hook_preprocess_HOOK().

template_preprocess()

Si on déclare un template, la fonction template_preprocess() est exécutée en tout début de chaîne. Cela est toujours vrai. Elle prépare un certain nombre de variables disponibles par défaut pour l’ensemble des templates. Elle n’est pas à déclarer dans son module ou son thème.

Si on déclare une fonction de thème, template_preprocess n’est pas exécutée.

hook_preprocess_HOOK()

Les fonctions de type hook_preprocess_HOOK() sont utilisables par les templates aussi bien que par les fonctions de thème.

Dans le cas de notre module Foo, on peut donc déclarer dans foo.module la fonction :

<?php //@file foo.module
function template_preprocess_bar(&$variables) {
// do something useful…
}

Celle-ci sera exécutée dans les 2 situations, avec ou sans template (le préfixe peut porter ici à confusion mais vous avez bien lu).

Remarque : les fonctions de type theme_HOOK() et template_preprocess_HOOK() doivent être déclarées dans le même module qui définit le theme hook. Autrement dit, theme_bar() et template_preprocess_bar() ne pourraient pas être définies en dehors de notre module Foo.

Usage des preprocess

Le rôle des preprocess est, comme leur nom l’indique, de préparer et pré-traiter les variables avant leur utilisation. Par exemple :
– faire un check_plain sur une variable avant son affichage,
– rajouter d’autres variables…

La fonction theme() détermine lesquelles de ces fonctions doivent être exécutées et dans quel ordre, avant de passer les variables au template ou à la fonction de thème. Voilà pourquoi theme() est aussi centrale.

Une fois qu’on a compris tout ceci, il va falloir se poser la question de la manière de surcharger ces sorties html et ces traitements. Nous savons déjà comment procéder avec les fonctions de thème. Ici ce n’est pas différent et dans la majorité des cas, ce sera dans notre thème que tout cela se passe.

Remarque : les fonctions de « process » interviennent plus tard, après les preprocess. Elles permettent de donner une dernière chance pour éventuellement finaliser des pré-traitements et mieux découper son code. Leurs règles d’exécution et d’utilisation sont le miroir des fonctions de preprocess.

Thème et surcharges

Surcharge des preprocess

Reprenons notre thème Footheme et son fichier template.php. Pour rappel son but est de contenir toutes les implémentations de hook et/ou de fonctions de thème que l’on souhaitera overrider. Ainsi, pour surcharger les fonctions « theme_bar » et « template_preprocess_bar » définies dans notre module « Foo », il suffira d’y déclarer :

<?php // @file template.php
/**
* Implements theme_bar()
*/
function footheme_bar($variables) {
$baz = $variables[‘baz’];
$class = $variables[‘class’];
return ‘<div class= »‘.$class.' »>’.$baz.'</div>’
}
/**
* Implements template_preprocess_bar()
*/
function footheme_preprocess_bar(&$variables) {
$variables[‘class’] = « override »;
}

J’ai volontairement choisi en premier l’exemple avec la fonction de thème car c’est plus parlant. Nous avons ici rajouté une variable dans la fonction de preprocess, qui est réutilisable plus tard dans la fonction de thème.

Si on avait eu un template à la pace de theme_bar(), on aurait pu générer la même sortie html que footheme_bar() dans notre fichier foo-template.tpl.php ainsi :

<!-- @file foo-template.tpl.php -->
<div class= »<?php print $class; ?> »><?php print $baz; ?></div>

Surcharge de templates

Pour les templates c’est un peu plus simple. La fonction drupal_find_theme_templates() va rechercher dans tous les répertoires du thème courant les templates correspondant à des implémentations de hook_theme(). Il suffit donc de recopier le fichier d’origine dans son thème pour y apporter les modifications voulues.

On peut organiser son thème en les plaçant dans un sous-dossier /templates ou les classer par type : templates/views, templates/nodes, templates/blocks, il n’y a pas d’autre convention ici que le bon sens.

Remarque : les templates d’origine déclarés par drupal_common_theme() sont dans le dossier /modules/system. Les autres peuvent être trouvés dans le dossier de leurs modules respectifs (node.tpl.php dans /modules/node, etc.)

Les suggestions

Je terminerai cet article sur une possibilité bien utile de l’api : les suggestions de templates et de fonctions de thème.

Suggestions de templates

De base des modifications dans page.tpl.php ou node.tpl.php affecteront l’ensemble de nos contenus, sans distinction aucune. Pour pallier à ce problème Drupal permet d’utiliser des suggestions qui correspondent à des variantes d’un même template. Ce sont les fonctions de preprocess qui sont responsables de leur déclaration.

Reprenons notre module Foo avec un template foo-template.tpl.php :

// @file foo.module
/**
* Implements template_preprocess_hook()
*/
function template_preprocess_bar(&$variables) {
$variables[‘theme_hook_suggestions’][] = ‘foo-template__’.$variables[‘baz’];
}

Deux remarques :

– les suggestions doivent respecter la règle de nommage des doubles underscores
– on aurait tout aussi bien pu utiliser une autre fonction de preprocess qui intervient plus tard dans la chaîne de rendu, soit au niveau du module : « foo_preprocess_bar() », soit au niveau du thème : « footheme_preprocess_bar() »

Désormais, la fonction theme() ira chercher un template différent en fonction de la valeur de « $baz », en respectant la règle de nommage : foo-template–BAZ.tpl.php (les double underscores deviennent des doubles tirets). Si des suggestions sont définies mais que theme() ne trouve pas de template, il fallback sur le template de base (ici foo-template.tpl.php). C’est ce qui permet d’utiliser les templates de node en fonction du type de contenu : node–TYPE.tpl.php ou en fonction de leur id : node–NID.tpl.php

Si une suggestion n’existe pas, rien n’empêche de la rajouter soi-même en implémentant la fonction de preprocess adéquate.

Suggestions de fonctions

Sur le même principe de nommage avec des double underscores, il est possible d’utiliser des suggestions de fonctions de thème. Il faudra les appeller via la fonction theme() directement. C’est le cas par exemple pour la fonction theme_links() utilisée dans le template de page page.tpl.php :

<?php // @file page.tpl.php
print theme(‘links__system_main_menu’, $variables);
print theme(‘links__system_secondary_menu’, $variables);

On peut alors déclarer les fonctions footheme_links__system_main_menu() ou footheme_links__system_secondary_menu(). Si aucune d’entre elles n’existe, la fonction theme() fallback sur le theme hook de base, theme_links().

Résumé

En résumé, nous avons vu ensemble les différents hooks et fonctions qui permettent d’utiliser la theme layer de Drupal afin de générer du html, soit via une fonction de thème, soit via un template. Nous avons également vu comment nous pouvons pré-traiter ces variables avant de les utiliser et les différentes possibilités de modifier des composants natifs ou de modules tiers.

Concernant les theme hooks, ils peuvent également être déclarés pour recevoir autre chose que des variables. Ce sont les « render elements ». Nous verrons ceux-ci dans un prochain article traitant des renderable arrays où nous apprendrons comment utiliser la render api au lieu d’appeler directement la fonction theme().

Resources

Templates et preprocess : https://www.drupal.org/node/223430
Preprocess/process : https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme/7.x

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

3 − = deux