小白折腾服务器(十一):postman 调试 API 查看当前接口全部 sql

laravel-debugbar、Clockwork、Telescope.......
等等等等,telescope是laravel作者开发的,功能强大界面美观没得说,但是laravel版本劝退(5.7.7+)
Clockwork和laravel-debugbar都必须是浏览器中调试,postman中就只能打log了....
有时候的sql又真的复杂,开发时查看orm组装的sql还是很有必要的,
打log有许多方法,但是我就是想看这一个接口的全部sql,我还不想去翻日志

想了想就折腾了这段代码

思路是这样的,

1.记日志(当有指定参数时,往指定的文件里写sql日志)

给全局加一个参数,dumpSql,当有dumpSql参数时,app/Providers/AppServiceProvider.php中的boot方法里加DB::listen往指定的文件里写sql日志

2.记日志之前清空该指定文件(保证每次记录的都是当前请求接口的sql日志)

这样就保证每次请求的api中的sql信息都是新的

3.响应中读取该指定文件,并将读取的内容组成数组,将该数组放入响应中

这里我用的是后置中间件,可以很方便的格式化响应数据,可以看这里:小白折腾服务器(七):自定义接口错误响应格式

思路很简单,看代码吧~
app/Providers/AppServiceProvider.php中添加代码:

  public function boot()
    {
        $filename = storage_path('/logs/sql.log');
        if (file_exists($filename)) {
            unlink($filename);
        }
        // 只在本地开发环境启用 SQL 日志
        if (app()->environment(['local', 'dev']) && request('dumpSql', 0)) {
            DB::listen(function ($query) use ($filename) {
//                $sql = vsprintf(str_replace("?", "'%s'", $query->sql), $query->bindings);
                $sql = Str::replaceArray('?', $query->bindings, $query->sql);

                $sqlArr = [
                    'date' => (string)now(),
                    'sql'  => $sql,
                    'time' => $query->time,
                ];

                $logPath = storage_path('/logs/');
                if (!file_exists($logPath)) {
                    mkdir($logPath, 0777, true);
                }

                file_put_contents($filename, json_encode($sqlArr, 320) . PHP_EOL, FILE_APPEND);
            });
        }
    }

在后置中间件中加一个私有方法:
这个方法是按行读取指定文件,并将内容装进数组

   protected function readFileByLine($filename)
    {
        $fh = fopen($filename, 'r');

        $sql = [];
        while (!feof($fh)) {
            $sql[] = json_decode(fgets($fh), true);
        }

        fclose($fh);

        if (is_null(end($sql))) {
            array_pop($sql);
        }

        return $sql;
    }

修改后置中间件:
后置中间件格式化响应的时候,加上这3行代码,
当有dumpSql参数 && 存在指定sql日志文件时,拼接sql响应参数。

       $sqlFilename = storage_path('/logs/sql.log');
        if (request('dumpSql', 0) && file_exists($sqlFilename)) {
            $data['sql'] = $this->readFileByLine($sqlFilename);
        }

可以看下效果:

小白折腾服务器(十一):给响应中加上sql信息,postman调试接口可以直接看sql啦

全部的后置中间件代码如下:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class After
{
    /**
     * 处理成功返回自定义格式
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure                 $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        if (is_array($response)) {
            return $response;
        }

        // 如果是导出Excel类型直接返回
        if ($response instanceof BinaryFileResponse) {
            return $response;
        }

        // 执行动作
        $oriData = $response->getOriginalContent();
        $content = json_decode($response->getContent(), true) ?? $oriData;
        $content = is_array($oriData) ? $oriData : $content;

        if ($content['code'] ?? 0) {
            return $response;
        }

        $data['data'] = isset($content['data']) ? $content['data'] : $content;

        if ($content['meta'] ?? []) {
            $data['meta'] = [
                'total' => $content['meta']['total'],
                'page'  => $content['meta']['page'] ?? $content['meta']['current_page'] ?? 0,
                'size'  => $content['meta']['size'] ?? $content['meta']['per_page'] ?? 0,
            ];
        }

        if ($oriData instanceof LengthAwarePaginator) {
            $data['meta'] = [
                'total' => $content['total'],
                'page'  => $content['current_page'],
                'size'  => (int)$content['per_page'],
            ];
        }

        if ($data['data']['data'] ?? []) {
            $data['data'] = $data['data']['data'];
        }

        $sqlFilename = storage_path('/logs/sql.log');
        if (request('dumpSql', 0) && file_exists($sqlFilename)) {
            $data['sql'] = $this->readFileByLine($sqlFilename);
        }

        $message  = ['code' => 0, 'message' => 'success', 'data' => []];
        $temp     = ($content) ? array_merge($message, $data) : $message;
        $response = $response instanceof JsonResponse ? $response->setData($temp) : $response->setContent($temp);

        return $response;
    }

    /**
     * @param $filename
     *
     * @return array
     */
    protected function readFileByLine($filename)
    {
        $fh = fopen($filename, 'r');

        $sql = [];
        while (!feof($fh)) {
            $sql[] = json_decode(fgets($fh), true);
        }

        fclose($fh);

        if (is_null(end($sql))) {
            array_pop($sql);
        }

        return $sql;
    }
}

自己写的小方法,最大的好处是自己熟悉,少依赖,如果用别人的包,可能一更新还得等作者更新,
当然最大的好处是比较方便啦^_^

一定一定要加 app()->environment(['local', 'dev']) 这个判断呦

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
php @ abc
文章
20
粉丝
94
喜欢
197
收藏
231
排名:107
访问:8.9 万
私信
所有博文
社区赞助商