lun, 13/04/2009 - 20:15 | Yoran   Tutoriels Drupal

Créer ses blocs

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
...

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 :

 

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...

Commentaires

sayes1, le sam, 04/04/2009 - 11: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 ;)
sayes

Yoran, le dim, 05/04/2009 - 22: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 :-)

Louis (non vérifié), le jeu, 03/02/2011 - 12:49

Bonjour, j'ai le même problème, pas d'affichage de block hello world alors que mon code est exactement le votre ...

<?php

function hook_block($op = 'list', $delta = 0, $edit = array()) {
switch($op) {
case 'list' :
// Énumération des blocs disponibles
{
error_log("Je suis ici..");
$blocs = array();
$blocs['hello_world'] = array(
'info' => t('Hello World'),
'cache' => BLOCK_CACHE_GLOBAL
);
return $blocs;
} // fin du cas 'list'
case 'view' :
// Génération du contenu à afficher pour le block
{
$bloc = 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
$bloc['subject'] = t('Hello World');
$bloc['content'] = $contenu;
}
return $bloc;
} // fin du cas 'view'
case 'configure' :
// Génération du formulaire de configuration
{
$formulaire = array();
if ($delta == 'hello_world') {
$formulaire['hello_world_message'] = array(
'#type' => 'textfield',
'#title' => t("Quelle message afficher ?"),
'#default_value' => variable_get('hello_world_message', ' ')
);
}
return $formulaire;
} // fin du cas 'configure'
case 'save' :
// Sauvegarde du formulaire de configuration
{
if ($delta == 'hello_world') {
variable_set('hello_world_message', $edit['hello_world_message']);
}
return;
} // fin du cas 'save'
} // fin switch
} // fin hook

Voilà si vous avez une idée ...

Pak1ange (non vérifié), le jeu, 16/07/2009 - 14:41

Bonjour,
Tout d'abord, merci pour le liens ou j'ai pu récupérer les icônes qui m'avaient quelques peu séduit ;)
Ensuite, j'ai récupéré les code sur l'un de vos billet. "Créer un module et un bloc". J'ai récupéré les sources sur votre serveur SVN et j'ai collé ca dans mon répertoire.

Nickel l'affaire !

J'ai détourné son utilisation "Hello World" pour coller une image dans le header. Mais quelques soucis me tracassent...

  • Je ne sais comment supprimer les paramètres introduit dans le code même :"salut le monde!" par exemple...
  • Une fois activé le bloc, lors de sa configuration, le champs "Quelle message afficher ? :" est pré-rempli et même lorsque on y à déclaré une valeur précédemment (dans mon cas une image) le champs reste toujours à la valeur pré-définit dans le fichier PHP.
  • Je viens de lire l'article sur lequel je poste, et malgré une évidente logique et la clartés de vos explications, je ne sais pas vraiment quels sont les fichiers visés, lesquels modifier et/ou créer et surtout ou les trouver et les disposer :(

Pourriez vous me proposer un code corrigé afin de destiner ce module "Hello World", à l'insertion de bloc image, et d'autres formes de contenu, avec pourquoi pas ;) une fonction permettant d'accéder directement à un répertoire du site ou seraient enregistrées les images ou du texte ?.

Je comprend bien que je vous demande quelque part de faire des modules qui me seraient personnalisés ;) et c'est un peu le cas.

Je suis en train "d'essayer" de créer un site dédié aux auto-constructeurs. Moi même étant menuisier-agenceur.

Je prend conscience de la "complexité" de mon projet et d'un manque manifeste de compétences en PHP.

Drupal est le support que j'ai choisi pour répondre à mes attentes, mais après quelques mois d'auto-apprentissage, je sais devoir me former à la maitrise des modules personnalisés afin de pouvoir mieux déléguer par la suite la gestion du contenu, qui devraient ci mon projet abouti, être relativement vaste et surtout imbriqué par un concept de taxonomie "complexe".

Encore merci pour ce que vous faite sur votre site ou je trouve plus que jamais matière à avancer avec Drupal ;)

Yoran, le sam, 18/07/2009 - 12:35

1. La valeur par défaut du paramètre du bloc est défini dans le second paramètre de la fonction variable_get. Tu peux y mettre ce que tu veux, y compris une chaîne vide.

2. J'ai vérifié, en insérant dans la zone de texte du paramètrage du bloc un <img src="..."/>, il apparaît bien dans le bloc. Peut-être t'es-tu trompé de chemin ?

Tu as donc là tout ce qu'il faut je pense pour t'en sortir. Ceci étant dit, en utilisant ce module comme cela, il n'y a pas grand intérêt par rapport à un simple module personnalisé créé par l'interface de Drupal.

pak1ange (non vérifié), le dim, 19/07/2009 - 23:01

Merci pour la réponse. Hum... j'ai d'abord un peu flippé en ne trouvant plus mon commentaire :(. Merci de l'avoir déplacè ;)

  1. Je n'ai pas eu de soucis pour mettre la photo dans le bloc en question. Elle apparait correctement quelque soit l'endroit ou je place celui-ci. Mais lorsque l'on réédite le bloc, même en ayant supprimé la valeur par défaut de la fonction variable_get, il perd l'ancienne saisie :~ .
  2. En fait je voulais adapter ce bloc pour l'édition de bannières pouvant être actualisées. Par exemple avec une liste déroulante pour accéder directement à des fichier .gif, .jpg, .flash ;)... Meuh non c'est pas pour ma pub :p
  3. Je replonge dans tes tutox maintenant que je sais que j'en encore pour des lustres avant de maitriser la bête :bigsmile:
Gilles (non vérifié), le mar, 21/07/2009 - 16:33

Merci pour le tuto
ma question est la suivante, si j'ai 10 pages différentes et que j'ai besoin de 5 blocs par page avec des contenus différents vais-je donc devoir créer 50 modules de bloc?
Y-a-t'il un moyen avec drupal de créer un type de bloc et de modifier son contenu selon les page?
Merci

Yoran, le mar, 21/07/2009 - 16:39

Dit comme cela je ne vois pas trop quoi répondre. Qu'est-ce qu'ils font tes blocs ?

Gilles (non vérifié), le mer, 22/07/2009 - 09:41

Bon, en cherchant bien j'ai pu créer plusieurs blocs avec des styles différents dans un module.
en fait j'ai un site à faire avec plusieurs blocs différents sur la partie droite de chaque page avec soit un texte une image, soit un texte seule, soit un titre un texte une image et encore un texte et avec des aspects différents (super quoi ;-) et je m'apperçoit que drupal c'est pas top pour je genre de truc.

david (non vérifié), le ven, 24/07/2009 - 10:28

Vu que tu codes directement dans les scripts php,
tu peux aussi bien créer ton contenu pour toutes les pages que tu souhaites créer, et faire un test sur le titre de la page pour afficher tel ou tel contenu.

Yoran, le ven, 24/07/2009 - 17:43

e m'apperçoit que drupal c'est pas top pour je genre de truc.
Je veux bien mais qu'est-ce qui serait "top" alors ? ;-) Tu peux créer des blocs avec des modules, tu peux rendre ces blocs génériques et par exemple mettre tes données de bloc dans un tableau et n'avoir du coup qu'un seul code qui en fonction du delta ira piocher les données qui te conviennent. Tu peux même définir les contents (titre et corps) de tes blocs dans un grand tableau et l'exploiter directement dans hook_bloc. En bref, je ne comprend pas bien ce qu'il te faut de plus.

Stereotype (non vérifié), le mer, 23/09/2009 - 17:49

Super merci beaucoup pour ce tuto! Je commençais à desesperer!!!

Bon allez! Y'a plus qu'a maintenant ; )

Yoran, le lun, 28/09/2009 - 07:43

Bon courrage :)

guizm0w (non vérifié), le mar, 24/11/2009 - 12:14

Bonjour et merci pour ces tutoriels vraiment bien expliqués. C'est assez difficile de débuter dans drupal car il n'y a pas énormement de tutos en français ;)

J'ai suivi le tutoriel sur l'écriture du premier module, puis celui-ci, et j'avoue avoir été éclairci, car je débute sous Drupal et rame pas mal dans la compréhension du moteur de ce CMS.

Mes félicitations !

Yoran, le lun, 21/12/2009 - 11:23

Et bien j'espère que la suite va te plaire aussi ;p

Gantier Nicolas, le mer, 13/01/2010 - 15:47

Bonjour et merci pour tous ces tutos.

Je cherche une méthode pour rendre dynamique un article.

Je m'explique:
J'ai créer un module permettant de faire des recherches dans un catalogue documentaire distant (en Soap). Mais ce n'est pas suffisant.
Je voudrai donc, dans un article de type News, afficher par exemple le titre, l'auteur et résumé d'un document, sans le retaper manuellement.
Existe-t'il une méthode dans Drupal (avec des balises...) s'insérant dans le corps du texte que mon module puisse intercepter afin d'insérer du contenu.
Merci.
Cordialement.

lo, le sam, 13/02/2010 - 01:12

Salut et merci beaucoup pour tous ces tutoriels.

Pour info, au début de cette page, le lien sur le texte "savons créé un module" (petite faute d'orthographe au passage : "créer" plutôt que "créé") mène à une page qui n'existe pas (node/1726). Je pense qu'il faut remplacer node/1726 par node/14. Même chose pour le lien suivant sur le texte "module basique avec ses hooks".

Je vais maintenant lire attentivement ce tutoriel qui m'intéresse beaucoup.

Merci encore !

Yoran, le dim, 21/02/2010 - 22:38

A vue de nez, tu peux régler cela avec hook_filter.

Yoran, le dim, 21/02/2010 - 22:41

C'est corrigé, merci beaucoup :)

MisterCoquille (non vérifié), le mar, 22/06/2010 - 14:22

Une coquille :

  • '#default_value' =>
    "Le monde!"
  • '#default_value' => variable_get('hello_world_message', ' ')
Yoran, le sam, 25/09/2010 - 07:26

Corrigé, merci :)

Publier un nouveau commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
  • Les adresses de pages web et de courriels sont transformées en liens automatiquement.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
CAPTCHA
Cette question est là pour déterminer si vous êtes humain ou pas...