Débugger une requête dynamique

Avec Drupal 7 a été introduit une toute nouvelle couche d'accès aux bases de données. Et parmi les nombreuses possibilités offertes par cette Database API, nous trouvons la manipulation dynamiquement des requêtes SQL pour ajouter une jointure, un tri, une condition, etc.

Mais cette fantastique possibilité induit aussi de véritables maux de crâne lorsqu'arrive l'inévitable heure du debuggage.

Requêtes dynamiques avec Drupal 6

Avec Drupal 6, lorsque l'on devait créer une requête à géométrie variable, cela donnait un code de ce genre :

if (!is_null($uid)) {
  $bout = "n.uid=$uid"; // oui je sais, ce n'est pas safe, c'est pour l'exemple ;-)
} else {
  $bout = '';
}

$query = "
  SELECT n.nid, n.title
  WHERE
    $bout
    n.type='mon_type' AND
    n.status=1
  ORDER BY n.created DESC"
;

$result = db_query($query);
while ($row = db_fetch_object($result)) {
  ...
}

Alors pas de doute, c'est moche. Cela peut être rendu moins moche avec les placeholders, mais bon, pas terrible tout de même...

Drupal 7 et db_select

Maintenant, écrivons le même code en utilisant les requêtes dynamiques de Drupal 7 :

// Création d'une requête en définissant le "from"
$query = db_select('node', 'n');

// Ici une clause "where" conditionnée par la présence d'un user ID valide
if (!is_null($uid)) {
  $query->condition('n.uid', $uid);
}

// Clause Where : n.type='mon_type'
$query->condition('n.type', 'mon_type');

// Clause Where : n.status=1
$query->condition('n.status', 1);

// On trie le tout
$query->orderBy('n.created', 'DESC');

// Et on itère sur le résultat, tout simplement...
foreach ($query->execute() as $row) {
  ...
}

Comme vous pouvez le constater, cela n'a pas la même gueule... Le sujet de ce papier n'étant pas d'expliquer en détail le fonctionnement de cette API, je vous conseille d'aller bouquiner l'excellent documentation disponible ici.

Et quant ça coince ?

Seul souci dans cette histoire, comment debugger une requête qui plante. Et justement, la requête donnée plus haut plante salement (c'est même fait exprès ;-). Avant, avec Drupal 6, un simple echo $query aurait suffit. Et avec Drupal 7 ce n'est guère plus compliqué. $$echo (string)$query; SELECT FROM {node} n WHERE (n.type = :db_condition_placeholder_0) AND (n.status = :db_condition_placeholder_1) ORDER BY n.created DESC

En effet, $query est maintenant un objet dont la classe a le bon goût d'implémenter la méthode magique __toString() et de fournir en résultat une requête SQL correctement formée, ou presque. Presque car nous avons encore ces "placeholder" qui nous bouchent la vue. Qu'à cela ne tienne, il suffit d'aller piocher les valeurs de ces placeholders, qui sont les paramètres de notre requêtes, grâce à la méthode $query->getArguments() qui nous renvoie un tableau utilisable par la fonction PHP strtr : $$echo strtr((string)$query, $query->getArguments()); SELECT FROM {node} n WHERE (n.type = mon_type) AND (n.status = 1) ORDER BY n.created DESC

Voilà qui est bien mieux. On voit immédiatement qu'il manque quelque chose d'assez fondamental entre le SELECT et le FROM, les champs à remonter. Cela se corrige facilement :

// Création d'une requête en définissant le "from"
$query = db_select('node', 'n');

// Ajout des champs n.nid et n.title
$query->fields('n', array('nid', 'title'));

Voilà, c'est réglé. Et nous pouvons donc ajouter à nos fonctions pratiques celle qui permet de debugger une requête dynamique :

function db_debug($query) {
  return strtr((string)$query, $query->getArguments()+array('{'=>'', '}'=>''));
}

Cette fonction n'est rien de plus qu'une formalisation de la technique énoncée plus haut, à ceci près que je vire au passage les accolades permettant de tester la requête facilement dans une console mysql ou postgresql.

Happy hunting :)

Vus : 1847
Publié par arNuméral : 54