Tout savoir sur WordPress

Timber : Une approche vue / controller dans vos thèmes pour un code plus propre

Si toi aussi tu aimes WordPress, mais que tu en as marre de mélanger PHP et HTML dans tes templates de thèmes, que la loop à force c’est finalement pas si super, alors j’ai une solution pour toi qui devrais te plaire à base d’approche vue / controller et de templating via twig

Les développeurs qui arrivent de Symfony par exemple et qui découvrent la première fois WordPress sont souvent interloqués par le code et l’approche de theming. Forcément, WordPress a un code vieux de 14 ans et comme la rétrocompatibilité est un point majeur de la philosophie WordPress, les choses ne vont pas bouger de sitôt.

Drupal au contraire va revoir son code à chaque nouvelle version majeure. C’est ce qui leur a permis de se moderniser (core à base de symfony, système de templating…)

Mais du coup j’ai pour vous une solution qui a révolutionné ma manière de créer des thèmes et qui s’appelle Timber.

Timber c’est quoi ?

Disponible sous forme de plugin ou de composant à intégrer directement dans son thème via Composer (j’y reviens après), Timber va apporter une petite surcouche vous permettant d’utiliser du templating et bien séparer le code PHP de vos pages, du HTML.

Le theming WordPress de base

Voici actuellement à quoi ressemble un fichier de template, par exemple archive.php :

<?php
  get_header();

  // case : display a category
  if (is_category()) {
    	$title = "Catégorie &bullet; ".single_tag_title( '', false);
  }
  // case : display a tag
  elseif (is_tag()) {
    	$title = "Mot clé &bullet; ".single_tag_title( '', false);
  }
  else {
    	$title = 'Le Blog &bullet; dernières actus';
  }
?>
  
  <div class="section blog">
    	<div class="wrapper flex-container">
      		<div class="f3 blog__posts">
        		<h1 class="blog__title"><?php echo $title; ?></h1>

        		<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>       	
        			<div class="post">
          				<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>

          				<div class="post__meta">
            				<span class="post__date">Le <?php the_time('d F Y'); ?></span>
            				<span class="post__categories">Thème : <?php the_category(); ?></span> 			
            				<span class="post__tags"> Tags : <?php the_tags('',' ',''); ?></span>
          				</div>
          
          				<div class="post__thumbnail">
            				<a href="<?php the_permalink(); ?>"><?php the_post_thumbnail(); ?></a>
          				</div>
          
          				<div class="post__content">
            				<?php the_excerpt(); ?>
            				<p class="right"><a href="<?php the_permalink(); ?>" class="button--type1 button--small button--bold">Lire l'article</a></p>
          				</div>
        			</div>
        		<?php endwhile; endif; ?>
      			</div>
    		</div>
  	</div>
<?php
  get_footer();
?>

On voit bien du PHP mixé à du HTML, c’est le bordel à écrire et à lire. Alors oui, WordPress nous fourni de jolies fonctions comme the_title() ou carrément la boucle (the Loop) pour nous éviter à avoir à écrire du code, mais bien souvent on va devoir ajouter quelques lignes faites maison pour traiter des fonctionnalités spécifiques. Ici par exemple je définis le titre de la page en fonction du cas (normal, dans une catégorie ou étiquette spécifiques).

Bref, c’est facile à prendre en main, mais pas super propre d’un point de vue développeur.

Le theming avec Timber

Une fois Timber en place, vos fichiers de template deviennent des Controlleurs : c’est à dire qu’ils ne gèrent que le code. Voici maintenant archive.php :

<?php

$context = Timber::get_context();

$template = 'archive.twig';

// Define title
if (is_tag()) {
  $context['title'] = single_tag_title( '', false );
} 
elseif (is_category()  {
  $context['title'] = 'Catégorie &bullet; '.single_cat_title( '', false );  
} 
elseif(is_post_type_archive()) {
  $context['title'] = post_type_archive_title( '', false );
  $template = 'archive-' . get_post_type() . '.twig';
}
else {
  $context['title'] = 'Le Blog';
}

// Pagination
$context['pagination'] = Timber::get_pagination();

// Posts
$context['posts'] = Timber::get_posts();


Timber::render($template, $context);

Sympa non ? On récupère le contexte (c’est à dire notamment les données de la page en cours), on fait ce que l’on a  à faire au milieu (définir le titre en fonction du cas par exemple, faire une WPquery custom) et enfin on invoque le template.

Votre HTML ira du coup dans un fichier de template au format .twig . Twig est un système de templating très connu et très simple à utiliser. Plus de PHP du coup dans les templates ! les variables sont appelées de cette forme {{variable}} :

{% extends "base.twig" %}

{% block content %}
  <div class="section blog">
    <div class="wrapper flex-container">
      <main class="blog__main">
        <h1 class="blog__title">{{title}}</h1>
        
        <div class="blog__posts">
        {% for post in posts %}
          {% include 'teaser.twig' %}
        {% endfor %}
        </div> <!-- blog__posts -->

        {% include 'navigation.twig' %}

      </main> <!-- blog__main -->

      <div class="blog__sidebar">
        {{sidebar}}
      </div> <!-- blog__sidebar -->

    </div> <!-- wrapper -->
  </div>
{% endblock %}

Cette fois on arrête de faire un get_header et get_footer mais on va plutôt appeler un fichier base.twig qui contient la structure générale et commune du site, dont voici un exemple :

<!DOCTYPE html>
<html {{site.language_attributes}}>
<head>
  <meta charset="{{site.charset}}">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <link rel="stylesheet" href="{{site.theme.link}}/css/main.css">
  
  {{wp_head}}
</head>

<body {{body_class}}>
  <header class="header">
    <div class="wrapper">
      <a href="{{site.url}}">{{site.name}}</a>
      <ul class="main-nav">
      {% for item in main_menu.get_items %}
        <li class="nav-main-item"><a class="nav-main-link" href="{{item.get_path}}">{{item.title}}</a>
      {% endfor %}
      </ul>
    </div>
  </header> <!-- header -->

  {% block content %}
  {% endblock %}

  {{wp_footer}}
</body>
</html>

Toutes les fonctions natives de WordPress comme wp_head, wp_footer, get_bloginfo… ont leur équivalent twig.

Pour aller un peu plus loin, sachez qu’il y a aussi une fonction globale Timber permettant de récupérer tout le nécessaire qui est global au site, pas seulement à la page, comme par exemple les menus, les sidebars pour ensuite les passer à votre contexte. Cette fonction peut être placée dans functions.php :

// Global context, available to all templates
function add_to_context( $context ) {

  // Menus
  $data['main_menu'] = new TimberMenu('main'); 
  $data['footer_menu'] = new TimberMenu('footer');

  // Sidebar
  if(is_single() or is_archive() or is_home() or is_front_page()):
    $data['sidebar'] = Timber::get_widgets('Blog');
  endif;

  return $context;
}
add_filter('timber_context', 'add_to_context');

Et en plus Timber est directement compatible avec Advanced Custom Fields ! Etant un grand fan d’ACF, ça ne pouvait que me plaire ! J’ai quelques tutoriels à propos d’ACF si vous êtes débutant.

le gros avantage selon moi

Pour moi, ce qui m’a définitivement convaincu c’est qu’au lieu de boucler en plein milieu du code, on prérécupère toutes les données dans le controller. Du coup niveau template c’est plus propre. Exit donc la boucle originale.

Lorsque vous aurez besoin de faire plusieurs boucles et custom WP queries parfois imbriquées, le code s’en ressortira bien plus propre.

Plus besoin d’invoquer wp_reset_postdata pour réinitialiser les globales (et oui, WordPress met des infos dans des variables globales et parfois on les écrase).

La documentation explique très bien comment créer une WP Query en Timber et comment l’afficher dans le template et croyez-moi, c’est plus propre !

Et ça c’est définitivement ce qui m’a convaincu de passer à Timber et Twig.

Comment l’installer : plugin VS composer

Il existe 2 méthodes pour installer Timber. La plus simple étant d’installer le plugin Timber dans votre installation WordPress et ça marchera. Pas de souci de performances à dénoter (je sais ce que vous pensez : un plugin de plus, une nouvelle surcouche, ça doit perdre en performances, mais non !).

Installer en tant que plugin WordPress

timber-plugin

Oui, mais…

Le souci c’est que si votre client désactive le plugin Timber, votre site ne fonctionnera plus ! Donc j’en viens à la seconde solution qui me parait vraiment plus pratique : installer Timber sous forme de composant dans votre thème via Composer.

Installer via Composer

Et ça tombe bien, si vous n’êtes pas un expert de Composer il y a un tutoriel pour ça ici :  Utiliser composer sur un projet WordPress. Pour faire simple composer permet de gérer des librairies et paquets que vous intégrez à votre projet. Pour ma part il y a encore quelques semaines j’étais pas un caid de composer mais au final c’est pratique et ça m’a permis d’installer Timber dans mon thème de base.

cd /mon-site/wp-content/themes/mon-theme

composer require timber/timber

Une fois installé via composer vous verrez apparaitre un dossier vendor dans votre thème :

composer

et vous n’aurez qu’à ajouter l’include vers  composer dans votre functions.php ainsi qu’ajouter l’appel de la classe Timber.

<?php

// Composer
require_once(__DIR__ . '/vendor/autoload.php');

// Timber
$timber = new \Timber\Timber();

Vous pouvez désormais utiliser Timber dans votre thème ! A noter que vous n’êtes pas obligé de tout passer en twig tout de suite ! Le code original de WordPress continuera de marcher, pratique pour améliorer au fur et à mesure votre thème.

Vous aurez besoin de la documentation officielle, notamment la partie twig, pour comprendre comment fonctionne les templates :

Documentation Timber / Twig

Vous verrez, c’est vraiment simple !

Un thème de base pour commencer

Afin de vous simplifier la tâche, voici un joli petit thème de base pour timber : Timber Starter theme. Pour mes projets j’ai mon propre thème qui répond à mes besoins spécifique, mais au niveau templates il ressemble énormément à cette base !

Starter theme Timber

Elle est plutôt simple à comprendre et vous permettra de vous mettre dans le bain plus rapidement. Je vous conseille toutefois de connaitre un minimum le theming WordPress et le template hierarchy avant de vous mettre à Timber.

conclusion

Cet article est une approche de Timber, afin de le faire découvrir et vous faire comprendre ses avantages. Vous pourrez du coup facilement l’installer et le tester de votre côté.

N’hésitez pas à le tester ! Comme je disais plus haut : nul besoin de tout convertir dans votre thème, vous pourrez faire d’abord le templating d’une seule page, et améliorer les autres au fur et à mesure. La méthode originale de WordPress continuant de fonctionner.

Je m’attarderais plus tard sur une suite de cours complets sur le theming à base de Timber et avec twig sur la nouvelle plateforme que je lancerais début 2017.

Je trouve que c’est une excellente alternative à l’approche de Genesis (coucou Grégoire) qui se rapproche bien mieux du Modèle / Vue : Controller dont les développeurs PHP aguérris auront l’habitude.

Couplé à une approche POO sur le reste du thème (les features spécifiques) ça donne là un code propre, maintenable et efficace.

Cet article a été mis à jour il y a 351 jours

Article écrit par Maxime BJ

Développeur, bloggeur et formateur Web spécialisé WordPress. 31 ans. Grenoblois. Co-fondateur de WPChef, l’organisme de formation WordPress.

Organisateur de WPInAlps, le meetup WordPress Grenoblois. Vous pouvez me rencontrer lors d’événements tels que WordCamp Paris et Europe. Traducteur Français de l’extension Advanced Custom Fields. Également développeur d’applications web avec MeteorJs. Je m’occupe un site pour apprendre l’informatique aux débutants gratuitement.

J’aime les jeux vidéo, la rando, la bouffe bien grasse et les voyages.

9 Commentaires

Laisser un commentaire

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

  1. Salut et merci pour cet article très intéressant, je ne connaissais pas Timber ! J’en suis venu progressivement à une approche un peu similaire au fil du temps (sans la couche Twig), finalement ça fonctionne pas mal d’adopter une approche plus ou moins MVC dans wordpress ! D’ailleurs, connais-tu cet autre plugin http://wpmvc.org/ ? Je n’ai pas encore eu l’occasion de tester, ça me semble un petit peu similaire à Timber ?

    • Merci !
      WPmvc de mémoire il venait trop interférer sur le fonctionnement natif de WP. Timber lui continue d’executer les hooks, garder somme toute à peu près la même approche. J’ai l’impression qu’avec wpmvc tout est à refaire mais il faudra que je teste à l’occase !

  2. Hello,

    Déjà : merci pour l’article. Ça fait du bien de lire des articles qui explorent les possibilités :-)

    J’avoue avoir été un peu sceptique à la lecture de cet article, en ce qui concerne Timber. Je me suis demandé : « Ok, l’extension schunte les templates tags et la loop, mais conserve-t’il les hooks ? ». Je l’ai aussitôt installé pour tester…

    J‘ai commencé par voir si les hooks de la class Walker_Nav_menu étaient toujours en place. Pour ça j’ai simplement testé d’ajouter une class sur un élément de menu via :

    add_filter( 'nav_menu_css_class', 'willy_class');
    function willy_class( $classes ) {
    $classes[] = 'lol';
    return $classes;
    }

    et surprise : ça fonctionne :-) Continuons : avec un autre filtre, tel que the_title, est-ce que cela fonctionne ?


    add_filter( 'the_title', 'willy_stupide_title' );
    function willy_stupide_title( $title ) {
    return 'test' . $title;
    }

    … ici ça fonctionne seulement à moitié : les titres des articles et des pages ont bien été « préfixés » sauf pour les entrées du menu qui ne sont pas des articles, là ça n’a rien fait :-/

    Bon… ça coupe court à la boucle, mais là aussi : est-ce que ça remet les hooks de la loop ?
    J’ai donc testé avec :

    add_action( 'loop_start', 'willy_stupide_dump');
    function willy_stupide_dump() {
    var_dump('haha');
    }

    add_action( 'loop_end', 'willy_stupide_dump2');
    function willy_stupide_dump2() {
    var_dump('huhu');
    }

    add_action( 'the_post', 'willy_stupide_dump3');
    function willy_stupide_dump3() {
    var_dump('loililol');
    }

    … et là le résultat est plus mitigé : le hook the_post est bien appelé, par contre loop_start n’est pas appelé sur la front page, et loop_end n’est pas appelé du tout… :-/

    Je précise que j’ai aussi essayé tout ça avec un thème twenty, et que tous ces hooks ont été appelé comme il faut, ça ne vient donc pas de mon installation, mais bien de Timber. Voilà pourquoi je préfère recaler ce plugin (pour l’instant). L’idée est belle mais elle ne suis malheureusement pas le reste du fonctionnement de WordPress…

    • J’étais bien chaud de testé en lisant ton article, mais le retour de Willy m’a un peu calmé.
      Mais merci de la découverte, ça fait du bien de voir qu’il y a des gens qui cherche à mettre en place une séparation controller / vue, qui manque cruellement à WordPress.

    • Ouais effectivement pour les hooks ça peut être un peu bloquant et je n’y avais pas pensé. Pour l’instant c’est pas bloquant de mon côté mais ça peut l’être pour beaucoup et je peux comprendre

    • Bah… faut tester quand-même… et puis j’imagine que y’a moyen de proposer des pull requests sur le dépôt de Timber
      Le truc c’est quand suivant cette voie c’est sur qu’on rencontrera tôt ou tard des problèmes d’incompatibilité, à moins que le moteur de template ne soit vraiment qu’une surcouche (et là je ne vois pas l’intérêt du tout).
      De mon côté, je préfère simplement utiliser WordPress avec des templates « simples », en utilisant la boucle et les templates tags, mais sans coder de fonctions dans les gabarits.

    • pas eu de souci de mon côté sur des sites à assez fort traffic. Couplé à un cache comme WP Rocket ça passe super !

0173500811a7e4f04550256b887fb11eRRRR