Étant donné le code suivant:
DB::table('users')->get();
Je souhaite obtenir la chaîne de requête SQL brute générée par le générateur de requête de base de données ci-dessus. Dans cet exemple, ce serait SELECT * FROM users
.
Comment puis-je faire cela?
Pour afficher à l'écran les dernières requêtes exécutées, vous pouvez utiliser ceci
dd(DB::getQueryLog());
Je crois que les requêtes les plus récentes seront au bas du tableau.
Vous aurez quelque chose comme ça:
array(1) {
[0]=>
array(3) {
["query"]=>
string(21) "select * from "users""
["bindings"]=>
array(0) {
}
["time"]=>
string(4) "0.92"
}
}
Selon le commentaire de Joshua ci-dessous, il est maintenant désactivé par défaut. Pour l'utiliser, vous devez l'activer manuellement à l'aide de:
DB::enableQueryLog();
Utilisez la méthode toSql()
sur une instance QueryBuilder
.
DB::table('users')->toSql()
renverrait:
sélectionnez * de `utilisateurs`
C'est plus facile que de connecter un écouteur d'événement et vous permet également de vérifier l'aspect réel de la requête à tout moment de sa création.
Vous pouvez écouter l'événement 'illuminate.query'. Avant la requête, ajoutez l'écouteur d'événement suivant:
Event::listen('illuminate.query', function($query, $params, $time, $conn)
{
dd(array($query, $params, $time, $conn));
});
DB::table('users')->get();
Cela affichera quelque chose comme:
array(4) {
[0]=>
string(21) "select * from "users""
[1]=>
array(0) {
}
[2]=>
string(4) "0.94"
[3]=>
string(6) "sqlite"
}
Si vous essayez d’obtenir le journal en utilisant Illuminate sans utiliser Laravel:
\Illuminate\Database\Capsule\Manager::getQueryLog();
Vous pouvez également créer une fonction rapide comme ceci:
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;
}
MODIFIER
la journalisation des requêtes est désactivée par défaut pour les versions mises à jour (le tableau ci-dessus renvoie un tableau vide). Pour le réactiver, lors de l'initialisation du gestionnaire Capsule, saisissez une instance de la connexion et appelez la méthode enableQueryLog
.
$capsule::connection()->enableQueryLog();
MODIFIER ENCORE
En prenant en compte la question, vous pouvez réellement procéder comme suit pour convertir la requête unique en cours au lieu de toutes les requêtes précédentes:
$sql = $query->toSql();
$bindings = $query->getBindings();
DB::QueryLog()
fonctionne uniquement après avoir exécuté la requête $builder->get()
. Si vous souhaitez obtenir la requête avant de l'exécuter, vous pouvez utiliser la méthode $builder->toSql()
. Voici l'exemple pour obtenir le SQL et le lier:
$query = str_replace(array('?'), array('\'%s\''), $builder->toSql());
$query = vsprintf($query, $builder->getBindings());
dump($query);
$result = $builder->get();
Il existe une méthode éloquente pour obtenir une chaîne de requête.
toSql ()
dans notre cas,
DB::table('users')->toSql();
revenir
select * from users
est la solution exacte qui renvoie la chaîne de requête SQL .. Espérons que cette ...
$data = User::toSql();
echo $data; //this will retrun select * from users. //here User is model
Si vous utilisez laravel 5.1 et MySQL, vous pouvez utiliser cette fonction faite par moi:
/*
* 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;
}
En tant que paramètre d’entrée, vous pouvez utiliser l’un ou l’autre de ces paramètres.
Illuminate\Database\Eloquent\Builder
Illuminate\Database\Eloquent\Relations\HasMany
Illuminate\Database\Query\Builder
À partir de laravel 5.2
et des suivantes. vous pouvez utiliser DB::listen
pour obtenir des requêtes exécutées.
DB::listen(function ($query) {
// $query->sql
// $query->bindings
// $query->time
});
Ou si vous souhaitez déboguer une seule instance Builder
, vous pouvez utiliser la méthode toSql
.
DB::table('posts')->toSql();
DB::enableQueryLog();
$queries = DB::getQueryLog();
C'est la fonction que j'ai placée dans ma classe de modèle de base. Passez simplement l'objet générateur de requêtes dans celui-ci et la chaîne SQL sera renvoyée.
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;
}
C'est la meilleure solution que je puisse suggérer à n'importe qui pour le débogage d'une dernière requête éloquente ou d'une requête finale, bien que cela ait été discuté également:
// query builder
$query = DB::table('table_name')->where('id', 1);
// binding replaced
$sql = str_replace_array('?',$query->getBindings(), $query->toSql());
// print
dd($sql);
Un 'macroable' replacement pour obtenir la requête SQL avec les liaisons.
Ajouter la fonction macro ci-dessous dans la méthodeAppServiceProvider
boot()
.
\Illuminate\Database\Query\Builder::macro('toRawSql', function(){
return array_reduce($this->getBindings(), function($sql, $binding){
return preg_replace('/\?/', is_numeric($binding) ? $binding : "'".$binding."'" , $sql, 1);
}, $this->toSql());
});
Ajoutez un alias pour le constructeur Eloquent. ( Laravel 5.4+ )
\Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){
return ($this->getQuery()->toRawSql());
});
Puis déboguez comme d'habitude. ( Laravel 5.4+ )
Par exemple. Générateur de requêtes
\Log::debug(\DB::table('users')->limit(1)->toRawSql())
Par exemple. Eloquent Builder
\Log::debug(\App\User::limit(1)->toRawSql());
Remarque: de Laravel 5.1 à 5.3, étant donné qu'Eloquent Builder n'utilise pas le trait
Macroable
, vous ne pouvez pas ajoutertoRawSql
un alias à Eloquent Builder à la volée. Suivez l'exemple ci-dessous pour obtenir le même résultat.
Par exemple. Eloquent Builder ( Laravel 5.1 - 5.3 )
\Log::debug(\App\User::limit(1)->getQuery()->toRawSql());
Pour laravel 5.5.X
Si vous souhaitez recevoir chaque requête SQL exécutée par votre application, vous pouvez utiliser la méthode listen. Cette méthode est utile pour la journalisation des requêtes ou le débogage. Vous pouvez enregistrer votre écouteur de requête dans un fournisseur de services:
<?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()
{
//
}
}
Vous devez d'abord activer le journal de requête en appelant:
DB::enableQueryLog();
après des requêtes en utilisant la façade DB, vous pouvez écrire:
dd(DB::getQueryLog());
la sortie sera comme ci-dessous:
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
]
]
Le moyen le plus simple est de faire une erreur délibérée . Par exemple, je souhaite voir la requête SQL complète de la relation suivante:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
Je veux juste faire une colonne pour être introuvable, ici je choisis created_at
et je l'ai changée en created_ats
en ajoutant la fin de s
à:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_ats','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
Donc, le débogueur va retourner l'erreur suivante:
(4/4) ErrorException SQLSTATE [42S22]: Colonne introuvable: 1054 inconnue column 'eqtype_jobs.created_ats' in 'liste de champs' (SQL: sélectionnez
jobs
. *,eqtype_jobs
.set_id
commepivot_set_id
,eqtype_jobs
.job_id
commepivot_job_id
,eqtype_jobs
.created_ats
Commepivot_created_ats
,eqtype_jobs
.updated_at
commepivot_updated_at
,eqtype_jobs
.id
commepivot_id
dejobs
intérieure Rejoindreeqtype_jobs
surjobs
.id
=eqtype_jobs
.job_id
oùeqtype_jobs
.set_id
= 56 ordre parpivot_created_at
limite max. 20 Décalage 0) (Affichage: /Home/said/www/factory/ressources/vues/set/show. blade.php)
Le message d'erreur ci-dessus renvoie la requête SQL complète avec l'erreur
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
Maintenant, il suffit de supprimer le supplément s
de created_at et de tester ce SQL à votre guise dans n’importe quel éditeur SQL tel que l’éditeur SQL de phpMyAdmin!
Remarquer:
La solution a été testée avec Laravel 5.4 .
Si vous n'utilisez pas Laravel mais le paquet Eloquent, alors:
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);
J'ai créé des fonctions simples pour obtenir le SQL et les liaisons de certaines requêtes.
/**
* 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;
}
Usage:
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 ],
// ]
vous pouvez utiliser Clockwork
Clockwork est une extension Chrome pour le développement de PHP, étendant les outils de développement avec un nouveau panneau fournissant toutes sortes d'informations utiles pour le débogage et le profilage de vos applications PHP, y compris des informations sur les requêtes, les en-têtes, les données d'extraction et de publication cookies, données de session, requêtes de base de données, itinéraires, visualisation de l'exécution de l'application, etc.
mais fonctionne aussi dans firefox
Vous pouvez utiliser ce package pour obtenir toutes les requêtes en cours d’exécution lorsque vous chargez votre page.
https://github.com/barryvdh/laravel-debugbar
Autant que j'aime ce cadre, je déteste quand il agit comme de la merde.
DB::enableQueryLog()
est totalement inutile. DB::listen
est également inutile. Il y avait une partie de la requête quand j'ai dit $query->count()
, mais si je fais $query->get()
, ça n'a rien à dire.
La seule solution qui semble fonctionner de manière cohérente consiste à insérer intentionnellement une syntaxe ou une autre erreur dans les paramètres ORM, comme un nom de colonne/table inexistant, à exécuter votre code sur la ligne de commande en mode débogage, et à générer l'erreur SQL avec la requête complète frickin 'enfin. Sinon, l’erreur apparaît dans le fichier journal si elle est exécutée à partir du serveur Web.
Imprimer la dernière requête
DB::enableQueryLog();
$query = DB::getQueryLog();
$lastQuery = end($query);
print_r($lastQuery);
Voici la solution que j'utilise:
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]);
});
S'il vous plaît, lisez les commentaires dans le code. Je sais, ce n'est pas parfait, mais pour mon débogage quotidien, c'est OK. Il essaie de construire la requête liée avec plus ou moins de fiabilité. Cependant, ne vous y fiez pas entièrement, les moteurs de base de données échappent aux valeurs que cette fonction courte ne met pas en œuvre. Alors, prenez le résultat avec précaution.
Utilisation:
$data = DB::select('select * from users where id = :id', ['id' => 1]);
print_r($data);
La sortie sera comme ci-dessous:
Array ( [0] => stdClass Object ( [id] => 1 [name] => parisa [last] => naderi [username] => png [password] => 2132 [role] => 0 ) )
Si vous utilisez bricoleur et que vous souhaitez enregistrer la requête SQL formée, vous pouvez le faire.
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.5 — cli) by Justin Hileman
>>> DB::listen(function ($query) { dump($query->sql); dump($query->bindings); dump($query->time); });
=> null
>>> App\User::find(1)
"select * from `users` where `users`.`id` = ? limit 1"
array:1 [
0 => 1
]
6.99
=> App\User {#3131
id: 1,
name: "admin",
email: "[email protected]",
created_at: "2019-01-11 19:06:23",
updated_at: "2019-01-11 19:06:23",
}
>>>
Vous devez ajouter une liaison dans votre sortie SQL afin de la trouver lisible. Vous pouvez utiliser le code suivant pour imprimer des requêtes SQL brutes:
$users = User::where('status', 1);
$users_query = str_replace(array('?'), array('\'%s\''), $users->toSql());
$users_query = vsprintf($query, $users->getBindings());
dump($users_query);
$all_users = $users->get();