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

闲话少叙,直接入题。首先,为什么要打印 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-laravel-queries

本帖由 Summer 于 1年前 加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 18

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

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

file

1年前
zhangbao

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

use DB;

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

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

1年前

@zhangbao ,执行了2条SQL语句

select * from cs_article where send_time <= ? ;

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

1年前
zhangbao

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

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

1年前

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

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

file

1年前
zhangbao

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

1年前
zhangbao

@悲剧不上演 你把代码贴出来。

1年前

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

//后台发布人关联
public function manager(){
    return $this->belongsTo("App\Http\Models\AdminUser",'uid');
}
1年前
zhangbao
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 属性,就可以得到你最终打印出的结果了。

应该是这样的。

1年前

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

1年前
zhangbao
1年前

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

1年前
zhangbao

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

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

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

1年前

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

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

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

11个月前
zhangbao

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

11个月前

@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

11个月前
zhangbao

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

11个月前

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

11个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!