N+1 问题捕杀神器 ——beyondcode/laravel-query-detector

安装

1
composer require beyondcode/laravel-query-detector --dev

将配置文件发布出来:

1
php artisan vendor:publish --provider=BeyondCode\\QueryDetector\\QueryDetectorServiceProvider

提示方式:

修改一下配置 config/querydetector.php

1
2
3
4
5
6
7
'output' => [
\BeyondCode\QueryDetector\Outputs\Alert::class,
\BeyondCode\QueryDetector\Outputs\Log::class,
\BeyondCode\QueryDetector\Outputs\Console::class,
\BeyondCode\QueryDetector\Outputs\Clockwork::class,
\BeyondCode\QueryDetector\Outputs\Debugbar::class,
]
  • \BeyondCode\QueryDetector\Outputs\Alert::class —— 浏览器 Alert 提示;
  • \BeyondCode\QueryDetector\Outputs\Console::class —— 浏览器 console 提示;
  • \BeyondCode\QueryDetector\Outputs\Clockwork::class —— 浏览器 clockwork 插件提示,该扩展包的安装建 课程
  • \BeyondCode\QueryDetector\Outputs\Debugbar::class —— Laravel-Debugbar 提示;
  • \BeyondCode\QueryDetector\Outputs\Json::class —— json 响应提示,由于 LaraBBS 中使用了 Dingo 扩展包完成接口数据的返回,该配置对现有接口没有作用。任何继承了 Illuminate\Http\JsonResponse 的响应都会自动添加提示;
  • \BeyondCode\QueryDetector\Outputs\Log::class —— 日志提示;

白名单功能

扩展包还提供了白名单的功能,也就当某个模型的的某个关系出现了 N + 1 问题的时候,不进行报错。相当于手动取消了对某个模型预加载的监控,或许你会在某个特殊的场合遇到这样的白名单使用场景,我们只是来测试一下功能。调整一下白名单的配置:config/querydetector.php

1
2
3
4
5
6
7
8
9
10
11
12
.
.
.
'except' => [
App\Models\Topic::class => [
App\Models\Category::class,
'category',
]
],
.
.
.

哈希数据 ID —— vinkla/hashids

普通项目中,我们使用的数据 ID 通常为数据库的自增 ID,自增 ID 有一些问题:

  • 数据的 ID 是暴露的;
  • 有可能被别人恶意采集;
  • 容易根据 ID 猜测数项目中的数据量;
  • 随着数据的增长,ID 会越来越大,无法统一长度。

vinkla/hashidshashids 在 Laravel 中的封装,hashids 是一个可以通过数字生成简短,唯一,无序字符串的开源库,它不仅能将数字转换为字符串,还能将转换后的结果转换回数字,利用这一点,我们可以很好的解决上述问题。

安装

1
composer require vinkla/hashids

安装成功后需要将 vinkla/hashids 的配置文件发布出来:

1
php artisan vendor:publish --provider="Vinkla\Hashids\HashidsServiceProvider"

vinkla/hashids 的思路是将数字转换为字符串,同时又可以将字符串再转换回数字。为了保证字符串不被轻易的破解,我们需要配置盐(salt),也就是给目标数字额外增加一些变量,保证他人在没有相同盐(salt)的情况下,无法将符串再转换回数字,增加了安全性。同时我们还可以指定转换后字符串的最小长度,这样能保证 ID 尽量统一美观。

在配置文件中修改 saltlength 信息:config/hashids.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.
.
.
'default' => 'main',
'connections' => [

'main' => [
'salt' => env('APP_KEY'),
'length' => '9',
],

'alternative' => [
'salt' => env('APP_KEY'),
'length' => '6',
],
]
.
.
.

vinkla/hashids 提供了多套配置信息,默认使用的是 main 中的配置,方便我们根据不同场景做不同的转换。我们现在的需求使用默认的即可,所以修改 main 中的 saltlarabbs, 修改 lentgh9

使用 -> 测试

我们先使用 Tinker 来验证一下相关的功能是否好用,打开 Tinker:

1
php artisan tinker

输入如下内容:

1
2
$hashId = Hashids::encode(10);
Hashids::decode($hashId);

可以看到对 10 encode 的结果为 9 位,将转换后的字符串进行 decode 可以正确得到数字 10,不过这里要注意,decode 后的结果为数组。转换后的结果为什么是数组呢?因为 hashids 不仅支持数字和字符串,还支持将数组进行 encode

封装的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 将所有的id字段加密处理
*/
if (!function_exists('encryptHash')) {
function encryptHash($id)
{
if (empty($id)){
return $id;
}
if (is_array($id)){
$arr = [];
foreach ($id as $v){
$arr[] = Hashids::encode($v);
}
return $arr;
}
return Hashids::encode($id);
}
}



/**
* 将所有的id字段解密处理
*/
if (!function_exists('decryptHash')) {
function decryptHash($code)
{
if (empty($code)){
return $code;
}
if (is_array($code)){
$arr = [];
foreach ($code as $v){
if (!Hashids::decode($v)){
throw new \Hashids\HashidsException('解密失败');
}
$arr[] = Hashids::decode($v)[0];
}
return $arr;
}
if (!Hashids::decode($code)){
throw new \Hashids\HashidsException('解密失败');
}
return Hashids::decode($code)[0];
}
}

解决跨域问题( CORS )——barryvdh/laravel-cors

同源策略

首先需要了解一下浏览器 同源策略,它是浏览器最核心也最基本的安全功能,同源的意思是,域名,协议,端口都相同。如果两个地址不同源,那么:

  1. Cookie、LocalStorage 和 IndexDB 无法相互读取;
  2. DOM 无法相互获得;
  3. AJAX 请求不能相互发送。

举个例子,larabbs.testlaravel-china.org 是无法读取对方的 Cookie 等数据,无法获取对方的 DOM,无法相互发送 AJAX 请求的。

这是出于安全的考虑,但是有的时候我们又确实需要跨域,比如要实现前后端分离,前端的域名是 www.larabbs.test 服务器接口的域名是 api.larabbs.test ,因为子域不同,依然会被同源策略限制,所以前端的 JS 就无法请求到后端接口的数据,我们需要解决这样的跨域问题。

CORS

CORS 是一个 W3C 标准,全称是 “跨域资源共享”(Cross-origin resource sharing),就是其中一种用来解决浏览器跨域的问题方法。这种方法的关键是在于服务器,只要服务器端返回了合适的头信息(Header),前端 JS 发送 AJAX 的时候就能访问跨域的资源你,代码和同源时一致,无需做任何修改,用户也不会有感知。

对于 CORS 请求时,又会分为两种,简单请求和非简单请求。

只要同时满足以下两大条件,就属于 简单请求

  1. 请求方法是以下三种方法之一:
    • HEAD
    • GET
    • POST
  2. HTTP 的头信息不超出以下几种字段:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限于三个值 application/x-www-form-urlencodedmultipart/form-datatext/plain

安装

1
composer require barryvdh/laravel-cors

将配置文件发布出来:

1
php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"

使用

扩展包的使用是非常简单的,在需要的地方增加中间件即可。

如果需要全局使用,可以在 app/Http/Kernel.php$middleware 中增加 \Barryvdh\Cors\HandleCors::class,但是 LaraBBS 中只有接口部分设计到 CORS 问题,我们只添加到 API 相关的路由中。

由于 LaraBBS 中使用了 DingoApi,路由部分被接管了,所以需要去 routes/api.php 中单独设置中间件。

首先需要设置一下该中间件的别名,方便使用:app/Http/Kernel.php

1
2
3
4
5
6
7
protected $routeMiddleware = [
.
.
.
// CORS
'cors' => \Barryvdh\Cors\HandleCors::class,
];

在接口中增加 cors 中间件:routes/api.php

1
2
3
4
$api->version('v1', [
'namespace' => 'App\Http\Controllers\Api',
'middleware' => ['serializer:array', 'bindings', 'change-locale', 'cors']
], function ($api) {

配置

可以看到只要使用了中间件,所有的请求都是可以通过的,也就是说,barryvdh/laravel-cors 的默认配置都是允许所有请求,当然我们可以修改这些配置:config/cors.php

1
2
3
4
5
6
7
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'],
'exposedHeaders' => [],
'maxAge' => 0,

分别对应如下配置:

配置 对应的 Header 说明
supportsCredentials Access-Control-Allow-Credentials 是否携带 Cookie
allowedOrigins Access-Control-Allow-Origin 允许的域名
allowedOriginsPatterns Access-Control-Allow-Origin 通过正则匹配允许的域名
allowedHeaders Access-Control-Allow-Headers 允许的 Header
allowedMethods Access-Control-Allow-Methods 允许的 HTTP 方法
exposedHeaders Access-Control-Expose-Headers 除了 6 个基本的头字段,额外允许的字段
maxAge Access-Control-Max-Age 预检请求的有效期

根据实际需要修改配置即可。

以上扩展教程出自laravel社区的 LiyuSummer 所作,我只是针对我项目所需扩展做一总结,非常感谢两位大佬提供的教程,让我少走好多弯路。
如有侵权,请联系博主,立马删除。