快捷打印 Laravel 中的数据库查询(SQL)语句
闲话少叙,直接入题。首先,为什么要打印 Laravel 中 Query Builder 构建的 SQL 语句?
答案很简单,就是我要知道到底执行了什么 SQL 语句,这样我就能合适地写 Query Builder、在适合用『热加载』的场景不会误用『懒加载』。
热加载
$user = User::where('name', 'Eric')->with('articles')->first();
上面就是热加载的例子――获取第一个名为『Eric』的用户信息同时,把他的文章也全都取到。
这样的使用场景下,Query Builder 只使用了类似下面的两条 SQL 语句。
select * from users where name = 'Eric' limit 1;
select * from articles where user_id in (22);
就是说当你用 $user->articles
遍历用户文章时,不会再请求数据库了。
懒加载
懒加载和热加载是相对的。下面就是懒加载的例子。
$user = User::where('name', 'Eric')->first();
// 在 Blade 模版里遍历打印出用户文章
@\foreach($user->articles as $article)
// ...
@endforeach
这种用法能达到效果,但是效率会变低――每次遍历、处理的一篇文章信息,都是向数据库执行一次 SQL 得到的。
如果用户有 N 篇文章,就要执行 N 次 SQL 查询,再加上之前请求用户信息的 1 次 SQL,这就是所谓的『N+1』问题。
明明能 2 次完成的事,就不要再花 N+1 次了。
打印 SQL
像上面的状况,如果我们知道底层是怎样执行 SQL 语句的,也许就不会发生了。这就为我们找出了一个有必要打印 SQL 语句来看的理由。
既然文章题目是『快捷打印 SQL』,自然配置起来也很简单。配置到最后的结果的是:
当你的 APP_ENV
设置为 local
、请求 URL 后面紧跟 ?sql-debug=1
时,就会打印出请求处理逻辑中涉及到的所有数据库查询语句。
配置
AppServiceProvider
的 boot
方法内写入
use DB;
use Event;
if ( env('APP_ENV') === 'local' ) {
DB::connection()->enableQueryLog();
Event::listen('kernel.handled', function ( $request, $response ) {
if ( $request->has('sql-debug') ) {
$queries = DB::getQueryLog();
if (!empty($queries)) {
foreach ($queries as &$query) {
$query['full_query'] = vsprintf(str_replace('?', '%s', $query['query']), $query['bindings']);
}
}
dd($queries);
}
});
}
注意:路由有两种形式——基于闭包(Closure)和基于控制器动作(Controller Action)的。上面的配置只对基于控制器动作的路由有效。
参考链接
https://laravel-news.com/quickly-dumping-l...
本作品采用《CC 协议》,转载必须注明作者和本文链接
Article::where('send_time','<=',time())->with('manager')->get();
请教下,Laravel是如何组装成我们想要的这种类型?(查询出多个新闻,新闻有多个编辑名字)
@悲剧不上演 在你请求数据的语句下方,写上
重新请求一下 URL,就能看到了。你试试。
@zhangbao ,执行了2条SQL语句
select * from cs_article where send_time <= ? ;
select * from cs_admin where cs_admins.id in (?);
@悲剧不上演 就是这样的啊,
cs_article
表里应该外键(类似admin_id
)对应cs_admins
表里的id
。第一句 SQL 就是找到所有的 article ,每个 article 都有
admin_id
,所以第二句 SQL 就是用in
+ 这些 articleadmin_id
的方式就找到 article admin 信息的。@zhangbao 恩恩,我想知道的是Laravel如何给我们组装上我上面图片的那种数据类型,像上面的两条SQL语句
查出所有的
cs_article
中数据,然后查出 与之关联的admin用户表中数据,我想请教一下,Laravel 是如何匹配到对应数据的,(下图数据)例如 头条 是如何匹配到 admin 的?
@悲剧不上演 你的 Article Model 中的
manager
关联是怎么定义的?@悲剧不上演 你把代码贴出来。
@zhangbao 记错了,是一对一 :cold_sweat:
manager
关联设定: Article Model 的uid
字段是匹配 User Model 的id
字段。你使用的热加载执行了两条 SQL 语句。select * from cs_article where send_time <= ? ;
select * from cs_admin where cs_admins.id in (?);
第一条 SQL 拿到了
$articles
,第二条 SQL 拿到了写这些 article 的$admins
,数据现在都拿到了。接下来,用$article
的uid
属性匹配$admin
的id
属性,就可以得到你最终打印出的结果了。应该是这样的。
@zhangbao 恩恩,应该就是循环
$article
,根据uid
属性来填充来把数据填充其上。谢谢哈@悲剧不上演 :)
试了下,没生效,laravel 5.4. 路由是基于控制器的
@linzi007 应该是没有问题的,我试过。你查看下:
?sql-debug=1
参数。APP_ENV
是否为local
?或者你可以参考 这篇文章 查看程序输出的 SQL 语句。
我用的5.5,打印sql这个功能不生效,不能触发这个事件,
不明白手动注册事件为什么
listen
里面的事件名可以这样写:kernel.handled
,大概知道是监听App\Http\Kernel.php
的handled
方法。但不知道为啥不触发后面的匿名函数。我试着写到
App\ProvidersEventServiceProvider.php
的boot
方法里还是不行@wenfan 你查看 SQL 的那个路由应该写在控制器,写在
web.php
中的路由是看不到 SQL 输出的。你看看是不是这个问题。@zhangbao
我是再
web.php
里定义的路由,在控制器里运行的数据库查询web.php
:Route::get('/test', 'HomeController@test');
App\Http\Controllers\HomeController
@wenfan 还有一个地方:
APP_ENV
是否为local
?如果还不可行的话,建议按照 这篇文章 打印 SQL :)@zhangbao
是
local
,还是不行,第二种方式可以