Как заставить построитель запросов выводить необработанный SQL-запрос в виде строки?
учитывая следующий код:
DB::table('users')->get();
Я хочу получить строку запроса raw 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()
вернутся:
выбрать * из `пользователи`
это проще, чем подключить прослушиватель событий, а также позволяет проверить, как запрос будет выглядеть в любой момент при его создании.
Вы можете слушать ' illuminate.событие запроса. Перед запросом добавьте следующий прослушиватель событий:
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;
}
редактировать
обновленные версии, похоже, по умолчанию отключили ведение журнала запросов (выше возвращается пустой массив). Чтобы снова включить, при инициализации диспетчера капсул, возьмите экземпляр соединения и вызовите 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();
возвращение
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;
}
в качестве входного параметра можно использовать любой из этих
Осветить\Database\Eloquent\Builder
Осветить\База Данных\Красноречивый\Отношения\HasMany
Осветить\Database\Query\Builder
Первый способ:
просто вы можете сделать следующие вещи, используя toSql()
метод
$query = DB::table('users')->get();
echo $query->toSql();
если он не работает, вы можете настроить вещь из документация фреймворк Laravel.
второй вариант:
другой способ сделать это
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: выбрать
jobs
.*,eqtype_jobs
.set_id
aspivot_set_id
,eqtype_jobs
.job_id
aspivot_job_id
,eqtype_jobs
.created_ats
aspivot_created_ats
,eqtype_jobs
.updated_at
aspivot_updated_at
,eqtype_jobs
.id
aspivot_id
Сjobs
внутренний присоединяйтесьeqtype_jobs
onjobs
.id
=eqtype_jobs
.job_id
гдеeqtype_jobs
.set_id
= 56 приказаpivot_created_at
предел 20 desc смещение 0) (вид: / главная / Саид / www / фабрика / ресурсы/просмотры/набор / шоу.лезвие.в 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 с полным гребаным запросом. В противном случае, надеюсь, ошибка появится в файле журнала при запуске с веб-сервера.