Как заставить построитель запросов выводить свой необработанный SQL-запрос в виде строки?
учитывая следующий код:
DB::table('users')->get();
Я хочу получить необработанную строку запроса SQL, которую будет генерировать построитель запросов выше, поэтому в этом примере это будет SELECT * FROM users
.
Как мне это сделать?
23 ответа:
для вывода на экран последних запросов вы можете использовать этот
dd(DB::getQueryLog());
Я считаю, что самые последние запросы будут в нижней части массива.
у вас будет что-то вроде этого:
array(1) { [0]=> array(3) { ["query"]=> string(21) "select * from "users"" ["bindings"]=> array(0) { } ["time"]=> string(4) "0.92" } }
по состоянию на Джошуа комментарий ниже, Теперь это отключено по умолчанию. Чтобы использовать, вам нужно будет включить его вручную с помощью:
DB::enableQueryLog();
использовать
toSql()
методQueryBuilder
экземпляра.
DB::table('users')->toSql()
вернутся:выбрать * из `пользователи`
Это проще, чем подключить прослушиватель событий, а также позволяет проверить, что запрос будет на самом деле выглядеть в любой момент, пока вы его строите.
Вы можете слушать 'гореть.запрос ' событие. Перед запросом добавьте следующий прослушиватель событий:
Event::listen('illuminate.query', function($query, $params, $time, $conn) { dd(array($query, $params, $time, $conn)); }); DB::table('users')->get();
это выведет что-то вроде:
array(4) { [0]=> string(21) "select * from "users"" [1]=> array(0) { } [2]=> string(4) "0.94" [3]=> string(6) "sqlite" }
если вы пытаетесь получить журнал с помощью Illuminate без использования Laravel:
\Illuminate\Database\Capsule\Manager::getQueryLog();
вы также можете запустить быструю функцию, например:
function logger() { $queries = \Illuminate\Database\Capsule\Manager::getQueryLog(); $formattedQueries = []; foreach( $queries as $query ) : $prep = $query['query']; foreach( $query['bindings'] as $binding ) : $prep = preg_replace("#\?#", is_numeric($binding) ? $binding : "'" . $binding . "'", $prep, 1); endforeach; $formattedQueries[] = $prep; endforeach; return $formattedQueries; }
EDIT
в обновленных версиях, по-видимому, по умолчанию отключено ведение журнала запросов (выше возвращается пустой массив). Чтобы снова включить его, при инициализации Capsule Manager возьмите экземпляр соединения и вызовите
enableQueryLog
метод$capsule::connection()->enableQueryLog();
изменить Опять
принимая во внимание фактический вопрос, вы можете сделать следующее, чтобы преобразовать текущий одиночный запрос вместо всех предыдущих запросов:
$sql = $query->toSql(); $bindings = $query->getBindings();
DB::QueryLog()
работает только после выполнения запроса$builder->get()
. если вы хотите получить запрос перед выполнением запроса, вы можете использовать$builder->toSql()
метод. это пример того, как получить sql и привязать его:$query = str_replace(array('?'), array('\'%s\''), $builder->toSql()); $query = vsprintf($query, $builder->getBindings()); dump($query); $result = $builder->get();
существует метод в eloquent для получения строки запроса.
toSql()
в нашем случае,
DB::table('users')->toSql();
return
select * from users
является точным решением, которое возвращает строку запроса SQL..Надеюсь, это полезно...
Если вы используете laravel 5.1 и MySQL, вы можете использовать эту функцию, сделанную мной:
/* * returns SQL with values in it */ function getSql($model) { $replace = function ($sql, $bindings) { $needle = '?'; foreach ($bindings as $replace){ $pos = strpos($sql, $needle); if ($pos !== false) { if (gettype($replace) === "string") { $replace = ' "'.addslashes($replace).'" '; } $sql = substr_replace($sql, $replace, $pos, strlen($needle)); } } return $sql; }; $sql = $replace($model->toSql(), $model->getBindings()); return $sql; }
в качестве входного параметра можно использовать любой из этих
Illuminate\Database\Eloquent\Builder
Осветить\Базы Данных\Красноречивый\Отношений\Помощью Методов Hasmany
Illuminate\Database\Query\Builder
Первый способ:
просто вы можете делать следующие вещи с помощью
toSql()
метод$query = DB::table('users')->get(); echo $query->toSql();
если это не работает, вы можете установить, что от laravel documentation.
второй вариант:
другой способ сделать это
DB::getQueryLog()
но если она возвращает пустой массив, то по умолчанию он отключен визит это,
просто включить с
DB::enableQueryLog()
и оно будет работать :)для получения дополнительной информации посетите GitHub вопрос чтобы узнать больше об этом.
надеюсь, что это помогает :)
С фреймворк Laravel
5.2
и далее. вы можете использоватьDB::listen
для получения выполняемых запросов.DB::listen(function ($query) { // $query->sql // $query->bindings // $query->time });
или если вы хотите отладить один
Builder
экземпляр, то вы можете использоватьtoSql
метод.DB::table('posts')->toSql();
это функция, которую я поместил в свой базовый класс модели. Просто передайте в него объект построителя запросов, и строка SQL будет возвращена.
function getSQL($builder) { $sql = $builder->toSql(); foreach ( $builder->getBindings() as $binding ) { $value = is_numeric($binding) ? $binding : "'".$binding."'"; $sql = preg_replace('/\?/', $value, $sql, 1); } return $sql; }
для laravel 5.5.X
Если вы хотите получать каждый SQL-запрос, выполняемый вашим приложением, вы можете использовать метод listen. Этот метод полезен для ведения журнала запросов или отладки. Вы можете зарегистрировать свой прослушиватель запросов в поставщике услуг:
<?php namespace App\Providers; use Illuminate\Support\Facades\DB; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { DB::listen(function ($query) { // $query->sql // $query->bindings // $query->time }); } /** * Register the service provider. * * @return void */ public function register() { // } }
для просмотра выполненного запроса Laravel используйте журнал запросов laravel
DB::enableQueryLog(); $queries = DB::getQueryLog();
сначала вам нужно будет включить журнал запросов, позвонив:
DB::enableQueryLog();
после запросов с использованием фасада БД вы можете написать:
dd(DB::getQueryLog());
выход, как показано ниже:
array:1 [▼ 0 => array:3 [▼ "query" => "select * from `users` left join `website_user` on `users`.`id` = `website_user`.`user_id` left join `region_user` on `users`.`id` = `region_user`.`user_id` left ▶" "bindings" => array:5 [▶] "time" => 3.79 ] ]
Blockquote
вы можете использовать этот пакет для получения всех запросов, которые выполняются при загрузке вашей страницы
https://github.com/barryvdh/laravel-debugbar
Если вы не используете фреймворк Laravel, но используя красноречивые пакет тогда:
use \Illuminate\Database\Capsule\Manager as Capsule; use \Illuminate\Events\Dispatcher; use \Illuminate\Container\Container; $capsule = new Capsule; $capsule->addConnection([ // connection details ]); // Set the event dispatcher used by Eloquent models... (optional) $capsule->setEventDispatcher(new Dispatcher(new Container)); // Make this Capsule instance available globally via static methods... (optional) $capsule->setAsGlobal(); // Setup the Eloquent ORM...(optional unless you've used setEventDispatcher()) $capsule->bootEloquent(); // Listen for Query Events for Debug $events = new Dispatcher; $events->listen('illuminate.query', function($query, $bindings, $time, $name) { // Format binding data for sql insertion foreach ($bindings as $i => $binding) { if ($binding instanceof \DateTime) { $bindings[$i] = $binding->format('\'Y-m-d H:i:s\''); } else if (is_string($binding)) { $bindings[$i] = "'$binding'";`enter code here` } } // Insert bindings into query $query = str_replace(array('%', '?'), array('%%', '%s'), $query); $query = vsprintf($query, $bindings); // Debug SQL queries echo 'SQL: [' . $query . ']'; }); $capsule->setEventDispatcher($events);
можно использовать часам
Clockwork-это расширение Chrome для разработки PHP, расширяющее инструменты разработчика с помощью новой панели, предоставляющей все виды информации, полезной для отладки и профилирования ваших приложений PHP, включая информацию о запросе, заголовках, данных get и post, файлах cookie, данных сеанса, запросах базы данных, маршрутах, визуализации времени выполнения приложений и многое другое.
но работает и в firefox
Я создал несколько простых функций, чтобы получить SQL и привязки из некоторых запросов.
/** * getSql * * Usage: * getSql( DB::table("users") ) * * Get the current SQL and bindings * * @param mixed $query Relation / Eloquent Builder / Query Builder * @return array Array with sql and bindings or else false */ function getSql($query) { if( $query instanceof Illuminate\Database\Eloquent\Relations\Relation ) { $query = $query->getBaseQuery(); } if( $query instanceof Illuminate\Database\Eloquent\Builder ) { $query = $query->getQuery(); } if( $query instanceof Illuminate\Database\Query\Builder ) { return [ 'query' => $query->toSql(), 'bindings' => $query->getBindings() ]; } return false; } /** * logQuery * * Get the SQL from a query in a closure * * Usage: * logQueries(function() { * return User::first()->applications; * }); * * @param closure $callback function to call some queries in * @return Illuminate\Support\Collection Collection of queries */ function logQueries(closure $callback) { // check if query logging is enabled $logging = DB::logging(); // Get number of queries $numberOfQueries = count(DB::getQueryLog()); // if logging not enabled, temporarily enable it if( !$logging ) DB::enableQueryLog(); $query = $callback(); $lastQuery = getSql($query); // Get querylog $queries = new Illuminate\Support\Collection( DB::getQueryLog() ); // calculate the number of queries done in callback $queryCount = $queries->count() - $numberOfQueries; // Get last queries $lastQueries = $queries->take(-$queryCount); // disable query logging if( !$logging ) DB::disableQueryLog(); // if callback returns a builder object, return the sql and bindings of it if( $lastQuery ) { $lastQueries->push($lastQuery); } return $lastQueries; }
использование:
getSql( DB::table('users') ); // returns // [ // "sql" => "select * from `users`", // "bindings" => [], // ] getSql( $project->rooms() ); // returns // [ // "sql" => "select * from `rooms` where `rooms`.`project_id` = ? and `rooms`.`project_id` is not null", // "bindings" => [ 7 ], // ]
самый простой способ сделать преднамеренная ошибка. Например, я хочу видеть полный SQL-запрос следующего отношения:
public function jobs() { return $this->belongsToMany(Job::class, 'eqtype_jobs') ->withPivot(['created_at','updated_at','id']) ->orderBy('pivot_created_at','desc'); }
я просто сделать столбец, чтобы не найти, здесь я выбираю
created_at
и я изменил его наcreated_ats
добавляя трейлингs
будет:public function jobs() { return $this->belongsToMany(Job::class, 'eqtype_jobs') ->withPivot(['created_ats','updated_at','id']) ->orderBy('pivot_created_at','desc'); }
Итак, отладчик вернет следующую ошибку:
(4/4) ErrorException SQLSTATE[42S22]: столбец не найден: 1054 неизвестно колонна 'eqtype_jobs.created_ats ' в ' списке полей '(SQL: select
jobs
.* ,eqtype_jobs
.set_id
какpivot_set_id
,eqtype_jobs
.job_id
какpivot_job_id
,eqtype_jobs
.created_ats
какpivot_created_ats
,eqtype_jobs
.updated_at
какpivot_updated_at
,eqtype_jobs
.id
какpivot_id
Сjobs
внутренний присоединяйтесьeqtype_jobs
onjobs
.id
=eqtype_jobs
.job_id
гдеeqtype_jobs
.set_id
= 56 order bypivot_created_at
предел 20 desc смещение 0) (вид: /главная/сказал/ВСП/завод/ресурсов/вид/установленный/показать.лезвие.php)выше сообщение об ошибке возвращает полный SQL-запрос с ошибкой
SQL: select jobs.*, eqtype_jobs.set_id as pivot_set_id, eqtype_jobs.job_id as pivot_job_id, eqtype_jobs.created_ats as pivot_created_ats, eqtype_jobs.updated_at as pivot_updated_at, eqtype_jobs.id as pivot_id from jobs inner join eqtype_jobs on jobs.id = eqtype_jobs.job_id where eqtype_jobs.set_id = 56 order by pivot_created_at desc limit 20 offset 0
теперь, просто удалите лишние
s
от created_at и проверить этот SQL, как вам нравится в любом редакторе SQL, таких как phpMyAdmin SQL editor!обратите внимание:
решение было протестировано с Laravel 5.4.
вот решение, которое я использую:
DB::listen(function ($sql, $bindings, $time) { $bound = preg_replace_callback("/\?/", function($matches) use ($bindings) { static $localBindings; if (!isset($localBindings)) { $localBindings = $bindings; } $val = array_shift($localBindings); switch (gettype($val)) { case "boolean": $val = ($val === TRUE) ? 1 : 0; // mysql doesn't support BOOL data types, ints are widely used // $val = ($val === TRUE) ? "'t'" : "'f'"; // todo: use this line instead of the above for postgres and others break; case "NULL": $val = "NULL"; break; case "string": case "object": $val = "'". addslashes($val). "'"; // correct escaping would depend on the RDBMS break; } return $val; }, $sql); array_map(function($x) { (new \Illuminate\Support\Debug\Dumper)->dump($x); }, [$sql, $bindings, $bound]); });
пожалуйста, прочитайте комментарии в коде. Я знаю, это не идеально, но для моей повседневной отладки это нормально. Он пытается построить связанный запрос с большей или меньшей надежностью. Однако не доверяйте ему полностью, ядра СУБД избегают значений по-разному, которые эта короткая функция не реализует. Итак, внимательно отнеситесь к результату.
печать последнего запроса
DB::enableQueryLog(); $query = DB::getQueryLog(); $lastQuery = end($query); print_r($lastQuery);
как бы я ни любил эту структуру, я ненавижу, когда она действует как дерьмо.
DB::enableQueryLog()
Это совершенно бесполезно.DB::listen
одинаково бесполезны. Он показал часть запроса, когда я сказал$query->count()
, но если я это сделаю$query->get()
, ему нечего сказать.единственное решение, которое, как представляется, работает последовательно, - это намеренно поместить некоторую синтаксическую или другую ошибку в параметры ORM, например, несуществующее имя столбца / таблицы, запустить код в командной строке в режиме отладки, и он будет плевать наконец-то вышла ошибка SQL с полным гребаным запросом. В противном случае, надеюсь, ошибка появится в файле журнала при запуске с веб-сервера.