Skip to main content

Depurar consultas SQL

Monday 24 de November de 2014

Cuando se trabaja con EntityFieldQuery, db_select y otras formas de abstracción del SQL a veces se quiere acceder a la consulta efectiva SQL que se realiza, normalmente cuando uno está depurando. Los objetos de tipo SelectQuery, InsertQuery, UpdateQuery, etc, implementan el método mágico de PHP __toString(), de forma que basta hacer un casting a string para ver el SQL de la consulta:

$query_object = db_select('users', 'u')->fields('u');
$query_text = (string) $query_object;

El valor de $query_text será:

SELECT u.* FROM {users} u 

Sin embargo, EntityFieldQuery no implementa el método __toString(), por lo que hay que usar un truco. EntityFieldQuery, internamente, usa un objeto SelectQuery que sí dispone del método __toString(). Basta con engancharse en el punto adecuado y mostrar la consulta, una vez es ya un objeto SelectQuery. Para ello habrá que hacer dos cosas: marcar la consulta para ser 'depurada' mediante una etiqueta a la query e implementar el hook_query_alter().

Por ejemplo, supongamos que tenemos el siguiente EntityFieldQuery:

$efq = new EntityFieldQuery;
$result = $efq->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', 'page')
    ->execute();

Para depurarlo habría que añadirle una etiqueta, por ejemplo 'efq_debug':

$efq = new EntityFieldQuery;
$result = $efq->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', 'page')
    ->addTag('efq_debug')
    ->execute();

E implementar el correspondiente hook:

function mymodule_query_alter($query) {
  if ($query->hasTag('efq_debug')) {
    dpm((string) $query, 'Consulta');
    dpm($query->arguments(), 'Argumentos');
  }
}

Como resultado obtenemos para el primer dpm:

Consulta => SELECT node.nid AS entity_id, node.vid AS revision_id, node.type AS bundle, :entity_type AS entity_type
FROM 
{node} node
WHERE  (node.type = :db_condition_placeholder_0) 

Como puede verse la consulta viene con placeholders. Esta es la razón por la que se incluye el segundo dpm(), con los argumentos de la consulta:

:db_condition_placeholder_0 (String, 4 characters ) page
:entity_type (String, 4 characters ) node

Obviamente esto solo debería estar activado en desarrollo. Me hubiese gustado haber pensado esta forma de depurar el EntityFieldQuery pero antes de hacerlo busqué y encontré una respuesta de Clive, el Chuck Norris de Drupal Answers.

Actualización

Como comentan en la propia pregunta ahora el módulo devel incluye este truco: basta con añadir la etiqueta debug:

$q = new EntityFieldQuery;
$q->entityCondition('entity_type', 'node')
  ->addTag('debug')
  ->execute();
SQL
Debug
EntityFieldQuery