Introducción: Cuando Query Builder calla y necesitas ver el SQL
\n\n¿Alguna vez has escrito una consulta compleja con Query Builder, la has ejecutado y has recibido un error o datos incorrectos? Te mueres por ver qué está generando realmente tu cadena de métodos, pero el framework devuelve un objeto, no una cadena de texto. ¿Te suena familiar?
\n\nEsta es una de las preguntas más populares en Stack Overflow (decenas de miles de visitas). Y con razón. Saber cómo "extraer" el SQL puro de Query Builder es un superpoder que te ahorrará horas de depuración. Especialmente cuando trabajas con ORM como Doctrine o Eloquent en Laravel, donde las consultas se construyen mediante cadenas de métodos y el SQL real permanece oculto.
\n\nEn este artículo, veremos cómo obtener la cadena SQL raw de Query Builder en diferentes frameworks PHP, mostraremos ejemplos de código y explicaremos por qué es importante para todo desarrollador. ¡Vamos allá!
\n\n1. ¿Por qué podrías necesitar el SQL raw?
\n\nAntes de sumergirnos en el código, entendamos por qué es útil ver el SQL "crudo" si Query Builder ya hace todo por nosotros.
\n\n- \n
- Depuración de errores. Cuando una consulta falla, solo ves un objeto. El SQL es la verdad. Podrás ver qué tablas se unen, qué condiciones WHERE se aplican y dónde está el error de sintaxis. \n
- Optimización del rendimiento. Query Builder puede generar consultas ineficientes. Al verlas, detectarás JOINs innecesarios o falta de índices. \n
- Aprendizaje. Si eres principiante, ver el SQL generado te ayuda a entender cómo el ORM traduce tus métodos PHP en comandos SQL reales. \n
- Exportación y registro. A veces necesitas guardar la consulta en un log o enviarla a otro servicio. Sin la cadena, es imposible. \n
Ahora pasemos a lo más interesante: el código.
\n\n2. Laravel Eloquent: toSql() y getBindings()
\n\nLaravel es el framework PHP más popular, y su Query Builder (parte de Eloquent) ofrece métodos integrados para obtener el SQL. Así funciona.
\n\nEjemplo básico: toSql()
\n\nuse Illuminate\\Support\\Facades\\DB;\n\n$query = DB::table('users')\n ->where('age', '>', 18)\n ->where('status', 'active')\n ->orderBy('name', 'asc');\n\n$sql = $query->toSql();\n\necho $sql;\n// Salida: select * from `users` where `age` > ? and `status` = ? order by `name` asc\n\n¡Perfecto! Pero notaste los signos de interrogación. Son marcadores de posición para consultas preparadas. Para ver los valores reales, necesitas usar getBindings().
SQL completo con valores sustituidos
\n\n$bindings = $query->getBindings();\n\n$fullSql = vsprintf(str_replace('?', '%s', $sql), array_map(function ($value) {\n return is_numeric($value) ? $value : "'" . $value . "'";\n}, $bindings));\n\necho $fullSql;\n// Salida: select * from `users` where `age` > 18 and `status` = 'active' order by `name` asc\n\nEste truco reemplaza los marcadores por valores reales. Pero ten cuidado: no uses este método para ejecutar la consulta — no escapa los datos, solo es para depuración.
\n\nMétodo dd() para depuración rápida
\n\nEn Laravel existe el método dd() (dump and die), que muestra el SQL y los bindings:
DB::table('users')->where('age', '>', 18)->dd();\n// Detiene la ejecución y muestra:\n// sql: select * from `users` where `age` > ?\n// bindings: [18]\n\nConsejo: Usa dd() solo en entornos de desarrollo. En producción, matará la consulta.
3. Doctrine ORM: getSQL() y parámetros
\n\nDoctrine es un ORM potente, usado en Symfony y otros proyectos. Su Query Builder es diferente, pero el principio es el mismo.
\n\nEjemplo básico
\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\n// Supongamos que $entityManager se obtiene mediante DI\n$queryBuilder = $entityManager->createQueryBuil