Makina Blog
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
Actualités en lien
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.
Varnish et Drupal : gérer un cache anonyme étendu
Le rôle d'un Reverse Proxy Cache Varnish dans une architecture Web (type Drupal).
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.