快捷打印 Laravel 中的数据库查询(SQL)语句

闲话少叙,直接入题。首先,为什么要打印 Laravel 中 Query Builder 构建的 SQL 语句?

答案很简单,就是我要知道到底执行了什么 SQL 语句,这样我就能合适地写 Query Builder、在适合用『热加载』的场景不会误用『懒加载』。

热加载

$user = User::where('name', 'Eric')->with('articles')->first();

上面就是热加载的例子――获取第一个名为『Eric』的用户信息同时,把他的文章也全都取到。

这样的使用场景下,Query Builder 只使用了类似下面的两条 SQL 语句。

  1. select * from users where name = 'Eric' limit 1;
  2. 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 时,就会打印出请求处理逻辑中涉及到的所有数据库查询语句。

配置

AppServiceProviderboot 方法内写入

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 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 18

Article::where('send_time','<=',time())->with('manager')->get();

请教下,Laravel是如何组装成我们想要的这种类型?(查询出多个新闻,新闻有多个编辑名字)

file

6年前 评论

@悲剧不上演 在你请求数据的语句下方,写上

use DB;

DB::connection()->enableQueryLog();
dd( DB::getQueryLog());

重新请求一下 URL,就能看到了。你试试。

6年前 评论

@zhangbao ,执行了2条SQL语句

select * from cs_article where send_time <= ? ;

select * from cs_admin where cs_admins.id in (?);

6年前 评论

@悲剧不上演 就是这样的啊,cs_article 表里应该外键(类似 admin_id)对应 cs_admins 表里的 id

第一句 SQL 就是找到所有的 article ,每个 article 都有 admin_id,所以第二句 SQL 就是用 in + 这些 article admin_id 的方式就找到 article admin 信息的。

6年前 评论

@zhangbao 恩恩,我想知道的是Laravel如何给我们组装上我上面图片的那种数据类型,像上面的两条SQL语句
查出所有的cs_article中数据,然后查出 与之关联的admin用户表中数据,

我想请教一下,Laravel 是如何匹配到对应数据的,(下图数据)例如 头条 是如何匹配到 admin 的?

file

6年前 评论

@悲剧不上演 你的 Article Model 中的 manager 关联是怎么定义的?

6年前 评论

@zhangbao 记错了,是一对一 :cold_sweat:

//后台发布人关联
public function manager(){
    return $this->belongsTo("App\Http\Models\AdminUser",'uid');
}
6年前 评论
public function manager(){
    return $this->belongsTo("App\Http\Models\AdminUser",'uid');
}

// 等同于 
public function manager(){
    return $this->belongsTo("App\Http\Models\AdminUser",'uid', 'id');
}

manager 关联设定: Article Model 的 uid 字段是匹配 User Model 的 id 字段。你使用的热加载执行了两条 SQL 语句。

  1. select * from cs_article where send_time <= ? ;
  2. select * from cs_admin where cs_admins.id in (?);

第一条 SQL 拿到了 $articles,第二条 SQL 拿到了写这些 article 的 $admins,数据现在都拿到了。接下来,用 $articleuid 属性匹配 $adminid 属性,就可以得到你最终打印出的结果了。

应该是这样的。

6年前 评论

@zhangbao 恩恩,应该就是循环$article,根据 uid属性来填充来把数据填充其上。谢谢哈

6年前 评论

试了下,没生效,laravel 5.4. 路由是基于控制器的

6年前 评论

@linzi007 应该是没有问题的,我试过。你查看下:

  1. 请求 URL 后面是否跟上 ?sql-debug=1 参数。
  2. APP_ENV 是否为 local
  3. 您的数据能否能正常取到?

或者你可以参考 这篇文章 查看程序输出的 SQL 语句。

6年前 评论

我用的5.5,打印sql这个功能不生效,不能触发这个事件,

 Event::listen('kernel.handled',function(){.......})

不明白手动注册事件为什么listen里面的事件名可以这样写:kernel.handled,大概知道是监听App\Http\Kernel.phphandled方法。但不知道为啥不触发后面的匿名函数。
我试着写到App\ProvidersEventServiceProvider.phpboot方法里还是不行

6年前 评论

@wenfan 你查看 SQL 的那个路由应该写在控制器,写在 web.php 中的路由是看不到 SQL 输出的。你看看是不是这个问题。

6年前 评论

@zhangbao
我是再web.php里定义的路由,在控制器里运行的数据库查询

  1. web.php: Route::get('/test', 'HomeController@test');
  2. App\Http\Controllers\HomeController
    public function test()
    {
        $data = User::get();
        dd($data);
    }
  3. 访问url:

file

6年前 评论

@wenfan 还有一个地方:APP_ENV 是否为 local?如果还不可行的话,建议按照 这篇文章 打印 SQL :)

6年前 评论

@zhangbao
local,还是不行,第二种方式可以

6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!