Как вытащить SQL-запрос из Query Builder в виде строки: полный гайд

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Вступление: Когда Query Builder молчит, а SQL нужно увидеть

Вы когда-нибудь писали сложный запрос через Query Builder, запускали его — и получали ошибку или не те данные? Руки чешутся увидеть, что же на самом деле генерирует ваша цепочка методов, но фреймворк упорно возвращает объект, а не строку. Знакомая ситуация?

Этот вопрос — один из самых популярных на Stack Overflow (десятки тысяч просмотров). И не зря. Умение "вытащить" сырой SQL из Query Builder — это суперсила, которая экономит часы отладки. Особенно когда вы работаете с ORM вроде Doctrine или Eloquent в Laravel, где запросы строятся через цепочку методов, а реальный SQL скрыт внутри.

В этой статье мы разберём, как получить raw SQL строку из Query Builder в разных PHP-фреймворках, покажем примеры кода и объясним, почему это важно для каждого разработчика. Поехали!

1. Почему вам может понадобиться raw SQL?

Прежде чем нырнуть в код, давайте поймём, зачем вообще смотреть на "сырой" SQL, если Query Builder и так всё делает за нас.

  • Отладка ошибок. Когда запрос не работает, вы видите только объект. А SQL — это истина. Вы увидите, какие таблицы джойнятся, какие условия WHERE применяются, и где ошибка в синтаксисе.
  • Оптимизация производительности. Query Builder может сгенерировать неэффективный запрос. Посмотрев на него, вы заметите лишние JOIN'ы или отсутствие индексов.
  • Обучение. Если вы новичок, просмотр сгенерированного SQL помогает понять, как ORM транслирует ваши PHP-методы в реальные SQL-команды.
  • Экспорт и логирование. Иногда нужно записать запрос в лог или отправить его в другой сервис. Без строки это невозможно.

Теперь перейдём к самому интересному — коду.

2. Laravel Eloquent: toSql() и getBindings()

Laravel — самый популярный PHP-фреймворк, и его Query Builder (часть Eloquent) предоставляет встроенные методы для получения SQL. Вот как это работает.

Простой пример: toSql()

use Illuminate\Support\Facades\DB;

$query = DB::table('users')
    ->where('age', '>', 18)
    ->where('status', 'active')
    ->orderBy('name', 'asc');

$sql = $query->toSql();

echo $sql;
// Вывод: select * from `users` where `age` > ? and `status` = ? order by `name` asc

Отлично! Но вы заметили знаки вопроса? Это плейсхолдеры для подготовленных запросов. Чтобы увидеть реальные значения, нужно использовать getBindings().

Полный SQL с подстановкой значений

$bindings = $query->getBindings();

$fullSql = vsprintf(str_replace('?', '%s', $sql), array_map(function ($value) {
    return is_numeric($value) ? $value : "'" . $value . "'";
}, $bindings));

echo $fullSql;
// Вывод: select * from `users` where `age` > 18 and `status` = 'active' order by `name` asc

Этот трюк заменяет плейсхолдеры на реальные значения. Но будьте осторожны: не используйте этот метод для выполнения запроса — он не экранирует данные, это только для отладки.

Метод dd() для быстрой отладки

В Laravel есть удобный метод dd() (dump and die), который выводит SQL и биндинги:

DB::table('users')->where('age', '>', 18)->dd();
// Остановит выполнение и покажет:
// sql: select * from `users` where `age` > ?
// bindings: [18]

Совет: Используйте dd() только в development-среде. В production он убьёт запрос.

3. Doctrine ORM: getSQL() и параметры

Doctrine — мощный ORM, используемый в Symfony и других проектах. Его Query Builder немного отличается, но принцип тот же.

Базовый пример

use Doctrine\ORM\EntityManagerInterface;

// Предположим, $entityManager уже получен через DI
$queryBuilder = $entityManager->createQueryBuilder();

$queryBuilder
    ->select('u')
    ->from('App\Entity\User', 'u')
    ->where('u.age > :age')
    ->setParameter('age', 18)
    ->orderBy('u.name', 'ASC');

$query = $queryBuilder->getQuery();
$sql = $query->getSQL();

echo $sql;
// Вывод: SELECT u0_.id AS id_0, u0_.name AS name_1, u0_.age AS age_2 FROM users u0_ WHERE u0_.age > ? ORDER BY u0_.name ASC

Опять знаки вопроса. Чтобы получить параметры, используйте getParameters():

$parameters = $query->getParameters();
foreach ($parameters as $param) {
    echo $param->getName() . ' => ' . $param->getValue() . "\n";
}
// Вывод: age => 18

Doctrine не даёт готового метода для подстановки, но вы можете написать свой хелпер. Главное — помните: это для отладки, не для выполнения.

4. Yii2 Query Builder: createCommand()->rawSql

Yii2 — ещё один популярный фреймворк. Его Query Builder использует класс yii\db\Query.

use yii\db\Query;

$query = new Query();
$query->select('*')
    ->from('users')
    ->where(['>', 'age', 18])
    ->andWhere(['status' => 'active']);

// Получаем команду и raw SQL
$command = $query->createCommand();
$sql = $command->rawSql;

echo $sql;
// Вывод: SELECT * FROM `users` WHERE `age` > 18 AND `status` = 'active'

Обратите внимание: rawSql уже подставляет значения! Это удобно для быстрой отладки.

5. CakePHP Query Builder: sql() и valueBinder()

CakePHP 4+ использует свой Query Builder. Вот как получить SQL:

use Cake\Datasource\ConnectionManager;

$connection = ConnectionManager::get('default');
$query = $connection->newQuery()
    ->select('*')
    ->from('users')
    ->where(['age >' => 18]);

$sql = $query->sql();
$bindings = $query->valueBinder()->bindings();

echo $sql;
// Вывод: SELECT * FROM users WHERE age > :c0

Для подстановки значений:

$fullSql = $query->sql($query->valueBinder());
echo $fullSql;
// Вывод: SELECT * FROM users WHERE age > 18

CakePHP тоже делает подстановку автоматически, если передать valueBinder в sql().

6. Написание универсального логгера SQL

Если вы работаете с разными проектами или хотите логировать все запросы, напишите простой класс-логгер. Вот пример для Laravel:

class SqlLogger
{
    public static function toRawSql($query)
    {
        $sql                

Блоги

Рекомендаций по книгам