Cómo extraer SQL crudo de Query Builder en PHP: 3 métodos prácticos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

«Doctor, mi Query Builder está generando algo extraño…»

\n

¿Situación familiar? Escribes una elegante consulta encadenada con Query Builder, estás seguro de que todo funciona perfectamente, pero el resultado es un error o datos incorrectos. Lo primero que se le viene a la mente a cualquier desarrollador PHP: «¡Muéstrame lo que realmente estás generando!».

\n

La pregunta «How do I get the query builder to output its raw SQL query as a string?» es una de las más populares en Stack Overflow (miles de visitas y decenas de respuestas). Y no es casualidad. Saber cómo obtener SQL limpio rápidamente es un superpoder que ahorra horas de depuración.

\n

En este artículo analizaremos 3 métodos prácticos para frameworks PHP (Laravel, Doctrine, CakePHP), mostraremos ejemplos de código reales y explicaremos cuándo usar cada método. ¡Allá vamos!

\n\n

¿Por qué es necesario? (Breve y al grano)

\n
    \n
  • Depuración — entender por qué la consulta devuelve datos incorrectos.
  • \n
  • Optimización — ver qué JOINs y WHEREs se ejecutan realmente.
  • \n
  • Aprendizaje — los principiantes a menudo no entienden cómo Query Builder traduce los métodos a SQL.
  • \n
  • Registro — guardar la consulta cruda para auditoría o reproducción.
  • \n
\n

¿Listo? ¡Entonces pasemos al código!

\n\n

Método 1: Laravel Eloquent / Query Builder — el clásico

\n

En Laravel hay varias formas, pero la más fiable es usar toSql() junto con getBindings(). ¿Por qué juntos? Porque toSql() muestra la consulta con marcadores de posición ?, y sin sustituir los valores es inútil.

\n\n

Ejemplo simple: SELECT con WHERE

\n
use App\\Models\\User;\n\n$query = User::where('email', 'like', '%@gmail.com')\n             ->where('active', 1)\n             ->orderBy('created_at', 'desc');\n\n// Obtenemos SQL con marcadores de posición\n$sql = $query->toSql();\n$bindings = $query->getBindings();\n\n// Unimos todo en un formato legible\n$fullSql = vsprintf(str_replace('?', '%s', $sql), $bindings);\n\necho $fullSql;\n// Resultado:\n// SELECT * FROM users WHERE email LIKE '%@gmail.com' AND active = 1 ORDER BY created_at DESC
\n

Importante: vsprintf no es una solución universal. Para tipos de datos complejos (por ejemplo, NULL o arrays) es mejor usar Str::replaceArray() o el paquete laravel-query-builder-debug.

\n\n

Variante avanzada: con join y whereIn

\n
$query = DB::table('orders')\n    ->join('users', 'orders.user_id', '=', 'users.id')\n    ->whereIn('orders.status', ['pending', 'processing'])\n    ->where('orders.total', '>', 100)\n    ->select('orders.*', 'users.name');\n\n$bindings = $query->getBindings();\n$sql = $query->toSql();\n\n// Formateamos de manera elegante\n$fullSql = preg_replace_array('/\\?/', $bindings, $sql);\necho $fullSql;\n// SELECT orders.*, users.name FROM orders INNER JOIN users ON orders.user_id = users.id WHERE orders.status IN ('pending', 'processing') AND orders.total > 100
\n

Consejo: Use preg_replace_array() — es más seguro que vsprintf, especialmente cuando los datos contienen el carácter %.

\n\n

Método 2: Doctrine DBAL — para los que aman el rigor

\n

Doctrine Query Builder (usado a menudo en Symfony) es una bestia más compleja. Aquí no hay un toSql() listo, pero existe getSQL() y getParameters(). Además, hay que tener en cuenta los tipos de parámetros.

\n\n

Ejemplo básico

\n
use Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Query\\QueryBuilder;\n\n/** @var Connection $connection */\n$queryBuilder = $connection->createQueryBuilder();\n$queryBuilder\n    ->select('p.*', 'c.name as category_name')\n    ->from('products', 'p')\n    ->join('p', 'categories', 'c', 'p.category_id = c.id')\n    ->where('p.price > :minPrice')\n    ->andWhere('p.sto                

Blogs

Book Recommendations