Makina Blog

Le blog Makina-corpus

Les 13 erreurs les plus courantes d'un développeur Drupal


En réalisant nos audits techniques, nous rencontrons régulièrement des choses à ne pas reproduire dans vos développements Drupal. Retour sur les bonnes pratiques…

Nota bene : les titres de paragraphes sont des choses à éviter, nous parlons ici d'erreurs !

Utiliser les variables pour autre chose que de la configuration ou un état global et persistant du site

Les variables persistantes de Drupal, c'est très pratique si bien que dans d'autres languages/frameworks parfois ça nous manque. Mais sur des sites à fort traffic ou très complexes, la table qui stocke celles-ci devient vite très importante et non optimisée.

Gardez donc cette API (variable_{get|set|del}()) pour de la configuration simple (dont le schéma s'arrête à quelques tableaux PHP) ou un état (souvent booléen ou numérique) et persistant.

Exemples de mauvaises pratiques :

  • Les utiliser au lieu d'un cache statique PHP ;
  • Stocker des données relatives à l'utilisateur courant ;
  • Stocker un timestamp, oui même Drupal core avec cron_last a tort, car un cron peut être configuré à toutes les minutes.

Déclarer des valeurs par défaut inconsistantes pour les variables

On voit parfois dans les modules de la communauté tout juste installés des pages de configuration où une option est cochée, de ce genre :

function modulemalcode_settings_form() {
  $form['ma_variable_de_conf'] = array(
    '#type' => 'checkbox',
    '#title' => "Mon option",
    '#default_value' => variable_get('ma_variable_de_conf', TRUE),
  );
  return system_settings_form($form);
}

Puis lorsque le code métier associé est exécuté, celui-ci ne fonctionne pas. C'est souvent que la valeur par défaut utilisée dans le code métier (2e paramètre de variable_get()) n'est pas la même :

if (variable_get('ma_variable_de_conf', FALSE)) {
  // Ici mon code métier
}

Si vous n'arrivez pas à vous rappeler de la valeur par défaut, utilisez des constantes (ex : MA_VARIABLE_DE_CONF_DEFAULT).

La non prise en compte du volume de données dans les requêtes

Quand on développe un module qui charge des données, tout va bien, nos 10 données exemples se chargent rapidement. Dans un contexte de production à fort traffic, ça se corse forcément avec des dizaines de milliers de données.

Utilisez donc la méthode ->range($start = NULL, $length = NULL) pour ajouter un LIMIT à votre requête. N'oubliez pas non plus de créer des index sur les champs interrogés dans le WHERE si vous travaillez sur des tables que vous avez créé.

Ne pas utiliser des constantes pour les chaînes de caractères

Drupal est friand d'identifiants sous forme de chaînes de caractères, l'exemple le plus courant est celui des permissions que l'on établit au début du développement et dont on oublie le nom ultérieurement, mais il y a aussi le nom des variables persistantes, des options de formulaire, ou même des chemins de base pour les routes. Une coquille est si vite arrivée, n'hésitez donc pas à créer des MYMODULE_PERM_VIEW, MYMODULE_STATE_ENABLED ou MYMODULE_BASE_PATH et vous retrouverez votre amie l'autocomplétion.

Appeler des fonctions en dehors du scope des hooks

Hop, hop, hop ! Qu'alliez vous faire ? Si, si, je vous ai vu taper define('YOURMODULE_BASE_PATH', drupal_get_path('module', 'yourmodule'));, et bien c'est perdu.

De manière générale, appeler une fonction en dehors du scope d'un hook peut embrouiller Drupal et son ordre de chargement lors du bootstrap, polluant les caches avant que ceux-ci soit récupérés ou bien contournant la logique de chargement, bref, abstenez-vous. Si en plus vous couplez cela avec une faute de frappe, ça peut faire très mal.

Favoriser le cache bloating

Le cache bloating en français c'est "Trop de cache tue le cache". N'oubliez pas que dans Drupal, un cache_get() c'est une requête, donc centralisez vos données au maximum et rationaliser les appels pour éviter des requêtes inutiles. La fonction drupal_static() est la pour centraliser du cache PHP static entre les fonctions, mais utilisez-là à bon escient.

L'exemple type que vous trouverez sur api.drupal.org est de ce genre :

function _ma_premiere_fonction_appelee_souvent() {
  $mycachedvariable = &drupal_static(__FUNCTION__);
}

Mais rien ne vous empêche de faire :

define('MY_UNIQUE_KEY', 'mon_cache_statique');

function _ma_premiere_fonction_appelee_souvent() {
  $mycachedvariable = &drupal_static(MY_UNIQUE_KEY);
}

function _ma_deuxieme_fonction_appelee_souvent() {
  $mycachedvariable = &drupal_static(MY_UNIQUE_KEY);
}

Ici MY_UNIQUE_KEY est une constante de chaîne de caractères unique et permet de partager le cache PHP statique.

Charger des objets dans une boucle

C'est un classique, mais une piqûre de rappel de fait pas de mal, toujours utiliser les fonctions suffixées par *_load_multiple, c'était une grande avancée de Drupal 7, profitez-en. Ainsi :

function mafonction() {
  $nids = db_select('node', 'n')->fields('n', ['nid'])->range(0, 10)->execute()->fetchCol();
  foreach($nids as $nid) {
    $node = node_load($nid);
    // Faire qqch avec ce noeud
  }
}

doit devenir :

function mafonction() {
  $nids = db_select('node', 'n')->fields('n', ['nid'])->range(0, 10)->execute()->fetchCol();
  foreach(node_load_multiple($nids) as $node) {
    // Faire qqch avec ce noeud
  }
}

Placer du code métier dans les templates

Je suis sûr que vous allez sourire et passer à la prochaine section, mais oui, il y a encore des gens qui chargent des objets et travaillent dessus dans les templates. Si vous vous sentez obligé, mettez au moins un commentaire au dessus de votre code pour vous excuser… Sinon, il y a les preprocess et autres altérations.

On ne doit jamais voir ceci dans un template node.tpl.php par exemple :

<div>
  <?php
  $account = user_load($node->field_ma_user_ref['und'][0]['value']);
  echo format_username($account);
  ?>
</div>

Il faut manipuler les données en amont dans un preprocess :

# Dans template.php
function montheme_preprocess_node(&$vars) {
  $node = $vars['node'];
  $vars['user_ref'] = user_load($node->field_ma_user_ref['und'][0]['value']);
}

# Dans node.tpl.php
<div>
  <?php echo format_username($user_ref); ?>
</div>

Si vous voulez éviter ce genre de personnes, utilisez Twig en tant que theme engine dans Drupal 7, ils n'auront pas la possibilité d'écrire ce genre de bêtises.

Ne pas utiliser le principe de suggestions de templates

Avouez, ceci n'est pas franchement joli dans un template :

<div>
  <?php if($moncul == 'du poulet'): ?>
    <p>Je place un paragraphe</p>
  <?php elseif($view_mode == 'teaser'): ?>
    <span>Oh et puis, non, je prefere un span</span>
  <?php else: ?>
    <em>Et ça c'est si je suis indécis</em>
  <?php endif; ?>
</div>

Et encore, ici il n'y a qu'un seul élément dans chaque partie… donc vu que c'est déjà pas beau avec PHPTemplate, alors si en plus vous mettez des dizaines de lignes HTML dedans… Sachez que grâce aux preprocess, vous pouvez déclarer autant de suggestions de template que bon vous semble !

Pour cela, peuplez le tableau $variables['template_suggestions'] de cette manière :

function montheme_preprocess_node(&$vars) {
  if ($vars['moncul'] == 'du poulet') {
    $vars['template_suggestions'][] = 'montemplate__dupoulet';
  }
  elseif ($vars['view_mode'] == 'teaser') {
    $vars['template_suggestions'][] = 'montemplate__' . $vars['view_mode'];
  }
  else {
    $vars['template_suggestions'][] = 'montemplate__default';
  }
}

Ça mange pas de pain, ni de ressources, et vous pouvez ensuite mettre joyeusement votre HTML dans montemplate--dupoulet.tpl.php.

Appeler les fonctions de thème au lieu de déclarer des render array

Demandez à un thémeur ce qu'il l'énerve le plus : "ne pas avoir accès aux données brutes", alors soyez sympa, donnez-lui ces fameuses données. Cela revient à ne pas utiliser la fonction theme() du tout, et utiliser des render arrays qui conservent les données jusqu'au rendu.

Un exemple type qui réside dans le cœur de Drupal sont les blocs : leur contenu est une chaîne HTML quasi impossible à altérer dans le thème, n'hésitez donc pas à retourner un render array avec votre propre fonction de thème :

function monmodule_block_view($delta = '') {
  return array(
    'subject' => t('My subject'),
    'content' => array(
      '#theme' => 'mymodule_sometheming',
      '#mydata' => $data,
    ),
  );
}

Utiliser unset() au lieu d'#access false

Tiens en parlant de render arrays, ils ont tout plein de propriétés sympathiques comme #cache, #weight mais surtout #access. Du coup, ne supprimez jamais des données d'un formulaire ou d'une structure de données avec unset($form['component']); mais utilisez plutôt $form['component']['#access'] = FALSE; comme ça vous pourrez toujours le réutiliser plus tard si besoin.

En plus : $form['component']['#access'] = user_access('ma permission'); est quand même plus propre qu'un if(user_access('ma permission')), non ?

Utiliser le hook_form_alter

Réfléchissez à la lisibilité de votre code dès le début, utiliser hook_form_FORM_ID_alter() car vous ne saurez pas à l'avance combien de cas vous aurez dans votre switch($form_id) {}

Tout coder dans le fichier .module

Un de nos signaux qu'un module est mal développé est le nombre de lignes dans son fichier .module, au dessus de quelques milliers, on abandonne : c'est mal rangé ! Utilisez donc les fichiers .inc pour ranger vos page callbacks avec par exemple la propriété 'file' d'un route dans le hook_menu().

Vous pouvez même déclarez un hook_hook_info pour charger dynamiquement des hooks, c'est bon pour votre RAM.

Pour aller plus loin

Si cet article vous a plu, n'hésitez pas à consulter notre plan formation de développeur Drupal.

Formations associées

Formations Drupal

Formation Drupal Développeur

Toulouse Du 26 au 28 novembre 2024

Voir la formation

Actualités en lien

Image
Encart D7 vers Drupal 11
04/04/2024

Migration d'un site Drupal 7 en Drupal 11

Trucs, astuces et "bouts" de code pour migrer votre site web de Drupal 7 à Drupal 11. Compte-rendu d'une conférence donnée au Drupalcamp Rennes 2024.

Voir l'article
Image
Varnish & drupal
20/02/2018

Varnish et Drupal : gérer un cache anonyme étendu

Le rôle d'un Reverse Proxy Cache Varnish dans une architecture Web (type Drupal).

Voir l'article
02/08/2013

Résolution de problèmes Drupal : construction de site (2/4)

Dans cette série d'articles, nous tentons de vous aider à vous sortir seuls de situations courantes en Drupal. Aujourd'hui, des problèmes rencontrés lors de la construction du site.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus