Créer ses blocs
Ecrit par Yoran, le lun, 13/04/2009 - 21:15

Maintenant que nous savons créé un module certes fonctionnel, mais d'une utilité toute relative, voyons comment en faire quelque chose de plus profitable sans pour autant taper tout de suite dans le compliqué. Ici, nous allons donc voir comment réaliser un simple bloc d'information Drupal qui sera malgré tout complet, et donc paramétrable.

Pré-requis

Les seuls pré-requis sont ici de savoir créer un module basique avec ses hooks.

Les sources

L'ensemble des sources de ce tutoriel est disponible ici. Il s'agit d'un serveur Subversion, donc vous pouvez aussi directement récupérer les sources dans votre dossier site/all/modules par la commande suivante :

gaston$cd /var/www/drupal/site/all/modules
gaston$mkdir tutoriels
gaston$cd tutoriels
gaston$svn co http://svn.arnumeral.fr/subversion/public/tutoriels_drupal/tutoriel_blocs
...
gaston$ 

Le module

Nous savons maintenant créer un module vide qui sera reconnu par Drupal. Nous allons donc en fabriquer un rapidement que l'on nommera tutoriel_blocs avec un fichier .module pour l'instant vide, et un fichier .info qui devrait ressembler à cela :

name = "Blocs"
description = "Code du tutoriel sur les blocs Drupal."
package = "karma-lab - tutoriels"
project = "tutoriel_blocs"
version = "6.x-1.2"
core = 6.x
tutoriel_blocs.info (Sources)

Blocs et modules

Les blocs drupal sont des petites sections d'informations constituées d'un titre et d'un "texte". Ces blocs seront placés autour du contenu principal (généralement un node) par l'intermédiaire du menu d'administration admin/build/block et habillés par le template bloc.tpl.php de votre thème. Dans ce menu d'administration, vous avez un certain nombre de blocs prêt à l'usage qui ne sont pas tous, et loin de là, activés et placés. Chacun de ces blocs est produit par l'intermédiaire d'un un module grâce à hook_block. Ce hook permet donc à un module de créer autant de blocs que désiré, disponibles dans l'administration des blocs si le module est activé.

Savoir fabriquer ses propres blocs se révèle extrêmement pratique à l'usage. En effet, lors de la conception d'un site ou d'une application WEB, on a toujours besoin d'accrocher de l'information, éventuellement contextuelle, comme une liste d'utilisateurs, de personnes, la date, ou que sais-je encore. Alors Drupal fournit déjà beaucoup de blocs prêt à l'usage et il existe aussi pas mal de modules contrib. Mais savoir faire ce type de petit développement soi-même est un fil à la patte en moins. Il nous permet de créer des choses très précises et efficaces sans pour autant surcharger notre site d'un module énorme dont le seul intérêt est de fournir une liste de noms qui se résumerait à 3 lignes de PHP...

Au passage, petit avertissement à l'usage de ceux qui vont construire des sites professionnels avec Drupal. Je profite de l'occasion pour affirmer que passer par la création d'un module est le seul moyen valable de fabriquer des blocs dynamiques. N'utilisez jamais la possibilité qui vous est pourtant offerte de faire cela en passant par l'interface d'administration des blocs (onglet "Ajouter un bloc") et en utilisant un format de type "interprétation de code PHP". Cette pratique est totalement anti-qualité logiciel. De tels blocs sont absolument impossible à maintenir car les bugs qu'ils génèrent sont très difficiles à traquer pour qui ne sait pas que du code PHP est ainsi collé dans la base de donnée. En chercher là ne viendrait jamais à l'idée de quelqu'un de sain d'esprit.
Cette possibilité de créer des blocs doit être uniquement réservé à des contenus textuels purs, avec, éventuellement, l'insertion d'une ou deux variables PHP.
Et tout ceci sans compter le fait que si l'on active l'interprétation PHP dans un bloc, on désactive de manière automatique sa mise en cache, ce qui peut avoir de graves impacts sur les performances.

Le hook block

Il est maintenant temps d'implémenter notre hook_block. Si nous jetons un oeil à la documentation drupal, nous voyons que nous devons donc commencer par ajouter une fonction comme celle-ci :

<?php

function hook_block($op = 'list', $delta = 0, $edit = array()) {
  switch($op) {

    // Nous placerons notre code ici

  } // fin switch
} // fin hook

Le switch pour l'instant vie va nous permettre de gérer le paramètre $op qui nous indique que ce hook sera appelé plusieurs fois pour des besoins différents. En l'occurrence il le sera pour obtenir la liste des blocs ($op=="list"), configurer un bloc ($op=="configure"), sauvegarder la configuration d'un bloc ($op=="save") et enfin obtenir le contenu du bloc ($op=="view").

Nous allons donc commencer à implémenter le contenu du ce switch pour prendre en charge tous les cas de figure en commençant par les opérations list et view.

Liste des blocs

Lorsque notre hook est appelé avec $op=="list", Drupal (en réalité le module block) s'attend à ce que la fonction renvoie un tableau associant un id (ici "hello_world") à une définition de bloc. Cette définition est elle aussi un tableau associant une attribut du bloc à sa valeur. Cette organisation des données est un grand classique Drupalien, autant s'y faire.

// Énumération des blocs disponibles
  case 'list': {
    $blocs=array();
    $blocs['hello_world'] = array(
      'info'    =>  t('Hello World'),
      'cache'   =>  BLOCK_CACHE_GLOBAL,
    );
    return $blocs;
  }
Définition de la liste des blocs

Ici nous renvoyons donc à Drupal un seul bloc d'id hello_world ayant pour description (l'attribut info) "Hello World". Notez l'utilisation de la fonction t(...) qui permet de prévoir une prise en charge ultérieure de la traduction des textes du module. Pensez à utiliser systématiquement cette fonction pour tous les textes.

L'attribut 'cache' est quant à lui très important à comprendre car il permet de paramétrer la gestion de la mise en cache du bloc. Par défaut, si rien n'est précisé, le mode utilisé est BLOCK_CACHE_PER_ROLE, signifiant que le bloc peut changer de contenu en fonction du rôle l'utilisateur ET de la page visualisée. Cela implique qu'un cache sera créé par combinaison de ces deux facteurs. En mode BLOCK_CACHE_PER_USER le bloc est caché par utilisateur, ce qui améliore les performances lorsque vous avez beaucoup d'utilisateur authentifiés mais qui peut aussi consommer de la ressource. En mode BLOCK_CACHE_PER_PAGE nous indiquons que le bloc peut changer de contenu en fonction de la page. Il y aura donc un cache de ce bloc par URL. Enfin nous avons les deux extrêmes avec BLOCK_NO_CACHE qui demande à ce que le bloc ne soit jamais mis en cache si par exemple il bouge beaucoup trop souvent (attention aux performances !!), et BLOCK_CACHE_GLOBAL pour un bloc qui ne change jamais et qui sera donc tout le temps en cache.

Dans notre exemple, ce contenu ne changera jamais, donc nous utilisons logiquement le mode BLOCK_CACHE_GLOBAL.

Affichage d'un bloc

Poursuivons donc avec le second cas de notre switch correspondant à la génération du titre et du contenu de notre bloc. Il ne faut pas "se faire avoir" par le nom de cette opération, view. Il s'agit de l'entendre au sens de "préparation de l'affichage", à savoir la création contenu du bloc :

// Génération du contenu à afficher pour le bloc
    case 'view': {
      $block=array();
      if ($delta == 'hello_world') {
        // Construction du contenu
        $contenu="Salut le monde";

        // Ajout du message
        $contenu.="".variable_get('hello_world_message', "pas de message défini");

        // Construction du block
        $block['subject'] = t('Hello World');
        $block['content'] = $contenu;
      }
      return $block;
    }
génération du contenu du bloc

Lorsque l'on placera notre bloc sur une région donnée, Drupal va donc relancer notre hook en passant en paramètre un $delta contenant l'id hello_world que nous avons défini plus haut. Dans ce cas notre fonction doit renvoyer à Drupal un tableau associatif contenant un attribut subject qui représente le titre du bloc, et un attribut content, pour son contenu XHTML.

Il ne vous reste plus qu'à activer notre module et aller dans l'interface d'administration des blocs pour l'ajouter dans une des régions de notre thème. Enregistrez et le bloc devrait apparaître avec la mention "pas de message défini". Première étape achevée, passons maintenant à ce fameux message.

Paramétrage du bloc

Tous les blocs ont la capacité d'être paramétrés en passant par le lien configurer associé à chacun d'entre eux dans l'interface d'administration des blocs. Nous allons donc maintenant ajouter un formulaire pour notre bloc en passant par l'opération $op==configure. Dans ce cas, Drupal s'attend à ce que vous lui renvoyez un formulaire en utilisant le Drupal Form API. Cette API n'étant pas le sujet de ce papier, voyons cela comme une première approche rapide du sujet :

// Génération du formulaire de configuration
  case 'configure': {
    $form=array();
    if ($delta == 'hello_world') {
      $form['hello_world_message'] = array(
        '#type' => 'textfield',
        '#title' => t("Quelle message afficher ?"),
        '#default_value' => "Le monde!",
      );
    }
    return $form;
  }
paramétrage du bloc

Le formulaire est construit dans la variable $form qui est encore un double tableau associatif, contenant la liste par id des champs. Puis par champ, la liste des attributs de ses et leurs valeurs. Je vous avais bien dit que cette organisation était un classique.

La propriété #type détermine le type du champ (zone de texte,liste déroulante, etc). Ici nous choisissions un champ texte (textfield). La liste de complète des types et de leurs propriétés associées est disponible dans la documentation de Drupal.

Mais la propriété vraiment intéressante est #default_value. Elle définit la valeur que dois prendre le champ au moment de l'affichage du formulaire. Cette valeur est extraite des propriétés générales de Drupal par la fonction variable_get(...).

La dernière propriété #title définit le titre du champ. Là aussi nous utilisons la fonction t(...) en prévision d'éventuelles traductions à venir.

Notre micro-formulaire est maintenant utilisable en retournant dans l'administration des blocs, en allant à la ligne que vous avez activée plus haut et en cliquant sur le lien configurer. Mais sauver ne servirait pas à grand chose tant que nous n'avons pas ajouter la dernière partie de notre switch, la sauvegarde justement...

Sauvegarde du paramétrage

A la sauvegarde du formulaire, notre hook est à nouveau appelé mais cette fois avec l'opération save nous permettant de récupérer les données saisies par l'utilisateur à travers la paramètre $edit:

  // Sauvegarde du formulaire de configuration
    case 'save': {
      if ($delta == 'hello_world') {
        variable_set('hello_world_message', $edit['hello_world_message']);
      }
      return;
    }
  } // fin switch
} // fin hook
paramétrage du bloc

Comme toujours $delta nous indique le bloc qui a été paramétré. Nous n'avons donc plus qu'à récupérer le message contenu dans le tableau associatif $edit et de le sauver avec la fonction Drupal permettant de stocker une variable persistante par variable_set(...);.

Cette fois c'est terminé. Il ne vous reste plus qu'à retourner dans l'interface d'administration des blocs configurer notre bloc et, après sauvegarde, voir apparaître notre message. Notez au passage que bien que nous ayons défini le bloc en mode BLOCK_CACHE_GLOBAL, Drupal a assez de bon sens pour vider ce cache à chaque re-configuration.

Conclusion

Voilà, nous n'avons maintenant plus d'excuse pour ne pas créer nos propres blocs aussi optimisés qu'adaptés à nos besoins. A noter que l'avantage immense de passer par les modules plutôt que de créer des blocs à la main dans l'interface d'administration est qu'un module est très facile à déployer, plus facile en tout cas que le contenu d'une base de donnée...

sayes1, le sam, 04/04/2009 - 12:24

Tout d'abord merci pour ces tutos.

Alors j'ai bien créé le module, drupal le voit bien et je l'ai activé.
Mais dans le menu bloc, il n'y a aucun changement, pas de nouveau bloc.

J'en profit aussi pour vous dire de faire attention au couleur de votre code car tous les signes = ; => sont quasi invisible avec le fond utilisé.

Encore merci Wink
sayes

Yoran, le dim, 05/04/2009 - 23:23

Lorsque vous dites "dans le menu bloc", vous faites référence à "admin/build/block" ?

Si c'est le cas, si vous mettez une trace à la ligne 39 (case 'list') du genre
error_log("Je suis ici..");

Et que vous refaites F5 dans la liste des blocs, voyez vous la trace dans le fichier de log des erreurs apache ?

PS: merci pour les infos. Mes feuilles de styles, et le thème en général d'ailleurs, n'est pas encore très sec Smile