Drupal 7 utilise le moteur de rendu PhpTemplate pour la gestion du thème, le principe est simple, chaque module implémente ses fonctions de thèmes (hook_theme) pour générer un code, le plus souvent HTML de façon directe ou en utilisant un fichier de template (XXXXX.tpl.php).
La fonction de thème permet également au module de déterminer des variables qui après traitement pourront être utilisé dans l'affichage HTML.
Il y a 2 possibilités pour cette fonction de thème
Soit le HOOK_theme défini une fonction qui va fabriquer du HTML avec des variables, ex :
function theme_breadcrumb($variables) {
$breadcrumb = $variables ['breadcrumb'];
if (!empty($breadcrumb)) {
// Provide a navigational heading to give context for breadcrumb links to
// screen-reader users. Make the heading invisible with .element-invisible.
$output = '' . t('You are here') . '
';
$output .= ' ';
return $output;
}
}
Ensuite, on utilise dans notre code cette fonction de thème, qui va attendre un paramètre (ici $variables) pour nous fabriquer la sortie HTML, ex :
function template_process_page(&$variables) {
if (!isset($variables ['breadcrumb'])) {
// Build the breadcrumb last, so as to increase the chance of being able to
// re-use the cache of an already rendered menu containing the active link
// for the current page.
// @see menu_tree_page_data()
$variables ['breadcrumb'] = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb()));
}
if (!isset($variables ['title'])) {
$variables ['title'] = drupal_get_title();
}
// Generate messages last in order to capture as many as possible for the
// current page.
if (!isset($variables ['messages'])) {
$variables ['messages'] = $variables ['show_messages'] ? theme('status_messages') : '';
}
}
La 2 ème méthode la plus utile est de transmettre les variables à un fichier dont l'extension doit être tpl.php, qui contient le HTML et gère uniquement l'affichage des variables (print ou echo) définis par la fonction HOOK_theme.
Ce fichier sera créé dans le répertoire du module qui implémente le HOOK_theme, exemple avec les blocs :
function block_theme() {
return array(
'block' => array(
'render element' => 'elements',
'template' => 'block',
),
'block_admin_display_form' => array(
'template' => 'block-admin-display-form',
'file' => 'block.admin.inc',
'render element' => 'form',
),
);
}
Le premier HOOK_theme va passer une variable (au format Render API) pour construire du HTML à un fichier de template : block, qui doit donc être block.tpl.php
>
subject): ?>
>subject ?>
>
Ce qu'il faut donc retenir c'est que nativement les modules core et le core (includes) de Drupal fournissent tous les fichiers tpl.php pour fabriquer l'ensemble de l'affichage d'un site Drupal, avec un ordre de précédence, on retrouve sur cette page un très bon aperçu de ce qu'il y a :
Et là la liste des templates qui sont donc crées par des fonctions HOOK_theme dans les modules core de Drupal :
Comme vu précédemment chaque module complémentaire peut implémenter un nouveau template si il veut, qui sera dans le dossier du module.
Template de base Drupal
Si je crée un thème Drupal en suivant la documentation (https://www.drupal.org/node/171205) sans aucun fichier tpl.php au niveau de ce thème, je vais quand même avoir un rendu HTML car Drupal va aller chercher tous les templates dans chaque module qui l'implémente.
Le problème aujourd'hui sera que le modèle HTML fourni avec Drupal correspond aux standards de 2011 et depuis le HTML à beaucoup évolué, il sera donc impératif de partir d'un thème de base HTML5 pour démarrer un projet Drupal ou d'utiliser un Framework de front (voir bonus en fin de page)
Surcharge (override) de template
Abordons l'un des aspects les plus intéressant du système, la surcharge. Le module core system de Drupal "modules/system/..." va déclarer en HOOK_theme le fichier HTML.tpl.php, qui est le wrapper global de notre page Drupal :
Imaginons que ce fichier ne me convienne pas, par exemple parce qu'il correspond au standard XHTML et que l'on veut du HTML 5, il est hors de question de le modifier directement dans son emplacement, puisqu'à la moindre mise à jour de Drupal il sera écrasé, pour le surcharger (override) je vais simplement copier le fichier dans le répertoire de mon thème front actif.
Je vais ensuite devoir vider le cache de registre et Drupal va utiliser le fichier de mon thème à la place du fichier natif ! Et ainsi de suite avec tous les templates possibles, ceux core ou déclarer par des modules complémentaires.
En général dans un thème on ne supplantera pas tous les templates core de Drupal, car souvent ils conviennent, mais on fera la surcharge de quelques uns selon le besoin, la bonne méthode est toujours de commencer par le moins de surcharge possible et de les faire petit à petit selon ses besoins... Ce n'est jamais utile de se dire qu'on en aura besoin plus tard.
On constate que les templates sont souvent génériques, par exemple tous les blocs sont fabriqués avec block.tpl.php
- block--[region|[module|--delta]].tpl.php (https://www.drupal.org/node/1089656)
Par exemple je veux que les block du module complémentaire Simplenews soient différents de ce qu'ils sont, je dois copier le fichier block.tpl.php dans mon thème front actif et le renommer block--simplenews.tpl.php, vider les cache de registre et modifier le code HTML de ce bloc.
Donc au final je peux me retrouver dans mon thème avec un fichier :
- block.tpl.php (tous les blocs, sauf ceux supplantés)
- block--header.tpl.php (tous les block en en-tête)
- block--block--1.tpl.php (le block custom 1 crée par l'utilisateur)
Tout ça avec une précédence, du plus générique à celui qui vise un élément précis.
Surcharge (override) de variables de template
Enfin il y a le cas ou le template me convient, mais j'aimerais modifier une variable qui est passé à ce template, un exemple :
Je veux ajouter une classe à la balise selon le rôle utilisateur. Dans ce cas on va utiliser la fonction hook_preprocess_HOOK.
Ce hook peut être placé dans un module ou dans le thème, dans notre cas c'est plus cohérent de le mettre dans le thème, si il était à la fois dans un module et dans le thème, il serait joué en dernier par la fonction du thème.
Je veux donc ajouter une classe au body, cela revient à modifier les variables envoyés au fichier HTML.tpl.php, je vois que dans ce fichier il y a déjà une variable $classes, c'est donc elle que je veux modifier, j'aurai donc cette fonction dans mon fichier template.php de mon thème (c'est en fait le fichier php pour les fonctions du thème) :
function THEMENAME_preprocess_HTML(&$variables) {
foreach($variables['user']->roles as $role){
$variables['classes_array'][] = 'role-' . drupal_HTML_class($role);
}
}
J'ajoute simplement une valeur à la variables, je pourrais en supprimer, la filtrer... Et dans ce cas je n'ai pas besoin d'avoir un fichier HTML.tpl.php, le preprocess se fait sur le HOOK_theme HTML, d'où que vienne le fichier.
Un module pour aider à identifier les templates et les suggestions de surcharge :
Conseils sur retour d'expériences
Par expérience je dirais qu'il faut partir d'un thème de 0 quand on sait exactement ce qu'on veut et qu'on maîtrise très bien le theming Drupal et qu'on est prêt à faire un peu de PHP.
Sinon il vaut mieux démarrer avec un thème d'ossature comme vu plus haut, ça fait quand même gagner du temps... Ou mieux, partir d'un Framework front qui en plus de la structure fourni un css et des composants javascript tout prêts.
Parmi les thèmes de base les plus avancés et utilisés :
Pour les Framework front on pourra utiliser :
La règle d'or que souvent les themeurs bafouent :
Ne jamais, jamais mais alors jamais faire de traitement php dans les templates ! C'est mal !
On peux faire des conditions (if else..) des switch ou des test exist ou empty, mais pas de traitement ! les traitements doivent aller dans une fonction de thème ou de module !
Le risque se situe au niveau de la maintenance et l'évolution du site qui va devenir très très compliqué,ainsi qu'un impact non négligeable sur les performances.