Introdução: Quando o Query Builder fica em silêncio e você precisa ver o SQL
\n\nVocê já escreveu uma consulta complexa usando o Query Builder, executou — e recebeu um erro ou dados inesperados? Dá aquela coceira de ver o que realmente está sendo gerado pela sua cadeia de métodos, mas o framework teima em retornar um objeto, não uma string. Situação familiar?
\n\nEssa é uma das perguntas mais populares no Stack Overflow (dezenas de milhares de visualizações). E não é à toa. Saber "extrair" o SQL bruto do Query Builder é um superpoder que economiza horas de depuração. Especialmente quando você trabalha com ORMs como Doctrine ou Eloquent no Laravel, onde as consultas são construídas através de uma cadeia de métodos e o SQL real fica escondido.
\n\nNeste artigo, vamos mostrar como obter a string SQL bruta do Query Builder em diferentes frameworks PHP, com exemplos de código e explicações sobre por que isso é importante para todo desenvolvedor. Vamos nessa!
\n\n1. Por que você pode precisar do SQL bruto?
\n\nAntes de mergulharmos no código, vamos entender por que vale a pena dar uma olhada no SQL "cru", se o Query Builder já faz tudo por nós.
\n\n- \n
- Depuração de erros. Quando a consulta não funciona, você vê apenas um objeto. Já o SQL é a verdade. Você enxerga quais tabelas estão sendo unidas (JOINs), quais condições WHERE estão sendo aplicadas e onde está o erro de sintaxe. \n
- Otimização de performance. O Query Builder pode gerar uma consulta ineficiente. Ao examiná-la, você perceberá JOINs desnecessários ou a falta de índices. \n
- Aprendizado. Se você é iniciante, ver o SQL gerado ajuda a entender como o ORM traduz seus métodos PHP em comandos SQL reais. \n
- Exportação e logging. Às vezes, é preciso registrar a consulta em um log ou enviá-la para outro serviço. Sem a string, isso é impossível. \n
Agora, vamos ao que interessa — o código.
\n\n2. Laravel Eloquent: toSql() e getBindings()
\n\nLaravel é o framework PHP mais popular, e seu Query Builder (parte do Eloquent) oferece métodos nativos para obter o SQL. Veja como funciona.
\n\nExemplo simples: 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// Saída: select * from `users` where `age` > ? and `status` = ? order by `name` asc\n\nPerfeito! Mas você reparou nos pontos de interrogação? São placeholders para consultas preparadas. Para ver os valores reais, use getBindings().
SQL completo com substituição de valores
\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// Saída: select * from `users` where `age` > 18 and `status` = 'active' order by `name` asc\n\nEsse truque substitui os placeholders pelos valores reais. Mas cuidado: não use este método para executar a consulta — ele não escapa os dados, serve apenas para depuração.
\n\nMétodo dd() para depuração rápida
\n\nNo Laravel, existe o método prático dd() (dump and die), que exibe o SQL e os bindings:
DB::table('users')->where('age', '>', 18)->dd();\n// Interrompe a execução e mostra:\n// sql: select * from `users` where `age` > ?\n// bindings: [18]\n\nDica: Use dd() apenas em ambiente de desenvolvimento. Em produção, ele interrompe a consulta.
3. Doctrine ORM: getSQL() e parâmetros
\n\nDoctrine é um ORM poderoso, usado no Symfony e outros projetos. O Query Builder dele é um pouco diferente, mas o princípio é o mesmo.
\n\nExemplo básico
\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\n// Suponha que $entityManager já foi obtido via DI\n$queryBuilder = $entityManager->createQueryBuil