huangbule / laravel-eloquent-filter
轻松过滤Eloquent模型
Requires
- php: ^7.4|^8.0
README
轻松过滤Eloquent模型。
依赖
- PHP7.4+
- laravel 8
安装
$ composer require huangbule/laravel-eloquent-filter
发布配置文件:
php artisan vendor:publish --tag=filter
设计初衷
为了简化搜索而编写大量重复代码,看看平时大家会写的简化代码,安全方面暂时先不考虑
$input = \request()->input(); //模拟接受用户数据
OrdersModel::query()->when(!empty($input['title']), function ($q) use($input) {
//订单标题,模糊搜索,我想你们不止订单要给表有`title`字段吧,每个model你们都写一遍
$q->where('title', 'like', '%' . $input['title'] . '%');
})->when(!empty($input['status']), function ($q) use($input) {
//模拟in类型,订单状态,如果搜索多个状态你们可能会这么写,可能传数组或者逗号隔开
if (!is_array($input['status'])) {
$input['status'] = explode(",", $input['status']);
}
$q->whereIn('status', $input['status']);
})->when(!empty($input['created_at']), function ($q) use($input) {
//搜索订单创建时间,可能传数组或者逗号隔开
if (is_array($input['created_at'])) {
$start_at = $input['created_at'][0];
$end_at = date('Y-m-d', strtotime('+1 day', strtotime($input['created_at'][1])));
} else {
$start_at = $input['created_at'];
$end_at = date('Y-m-d', strtotime('+1 day', strtotime($input['created_at'])));
}
return $q->where('created_at', '>=', $start_at)->where('created_at', '<', $end_at);
})->get();
这里只简单列举了一个orderModel,你会发现项目中这样代码重复遍地可见,所以需要优化加速开发
或者你可能用scope封装起来,比如:
public function scopeWxUserId($query, $param)
{
if (! empty($param['wx_user_id'])) {
return $query->where('wx_user_id', $param['wx_user_id']);
}
}
public function scopeUserId($query, $param)
{
if (! empty($param['user_id'])) {
return $query->where('user_id', $param['user_id']);
}
}
你看上面都是重复代码,都属于where中等于情况,那么我们为何不把它们封装起来呢,为什么要重复写呢?
设计思路
-
参考laravel validate写法,简化语句
-
引入
操作符比如like、>=、between等等,而且大家可以自己拓展 -
引入
前置处理器在最终执行where语句之前,我们可能需要对$input用户传过来的数据做下处理,比如上面的created_at[2024-03-14, 2024-03-20]其实20号需要加一天,因为created_at是datetime类型。而且必须可拓展 -
引入
别名比如标题我们数据库用title,但是由于特殊原因不能用这个字段,比如传order_title -
确定搜索规则格式:
搜索字段:前置处理器(0-n个)|操作符(可能没有默认)为了区分前置处理器和操作符对操作符做个特殊表示用$开头比如$like、$eq -
订单列表返回了用户信息,这时候我们想搜索用户标题怎么办?必须支持关联关系查询,设定关联关系用
#开头比如['title:#user'],然后必须在order表里面定义user()关联方法,默认就查询user表里面的title了,原理用whereHas -
引入配置文件,因为大部分字段搜索的规则都差不多的,我总不可能每个里面都写吧,比如['title:$like'],我在每个控制器里User、Order面都写一遍,那岂不是太麻烦了
-
如果你既想在配置直接定义title,又想定义它所属关联关系。可以在关联关系前面加#title,代码会在$input['#title']去掉#变成$input['title']获取值,demo见下面的#department_name
配置文件
<?php
return [
'default' => '$like', //定义默认操作符, [title'] 等同于 ['title:$like']
'rule' => [ //定义通用字段类型
'id' => '$eq',
'title' => '$like', //可以不写 因为默认是like
'order_title' => '@title', //@开头是别名,指定表里面字段是title
'department_name' => '$like',
'#department_name' => '#department|$like', //department是关联关系
'created_at' => '#department|HalfOpenDate|$halfOpen' //不分顺序。 属于department表,同时进行预处理函数,最后执行左开右闭处理
]
];
用法
将FilterTrait特质引入模型
namespace App\Models;
use Huangbule\LaravelEloquentFilter\Traits\FilterTrait;
use Illuminate\Database\Eloquent\Model;
class OrderModel extends Model
{
use FilterTrait;
}
然后在你的控制器中,调用eloquent查询对象的filter方法,第一个参数是用户传递的数据,第二个参数是定义规则
use App\Models\Order;
class OrderController extends Controller
{
public function index(Request $request)
{
return User::filter($request->input(), ['title','created_at','搜索字段:预处理器|#操作符|@别名'])->get();
}
}
标识符枚举:
操作符枚举:
前置处理器枚举
如何拓展自定义前置处理器
namespace App\Providers;
use App\Preprocess\UuidPreprocess;
use Huangbule\LaravelEloquentFilter\Traits\FilterTrait;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(Request $request)
{
$this->app->singleton('uuid', UuidPreprocess::class);
}
}
自定义预处理器必须实现Huangbule\LaravelEloquentFilter\Contracts\Ipreprocess接口
namespace App\Preprocess;
use Huangbule\LaravelEloquentFilter\Contracts\Ipreprocess;
class UuidPreprocess implements Ipreprocess {
public function handle($column, &$param) {
//@todo 业务逻辑
if (! empty($param[$column])) {
$param[$column] = \Hashids::decode($param[$column]);
}
}
}
如何拓展操作符
namespace App\Providers;
use App\Preprocess\UuidPreprocess;
use Huangbule\LaravelEloquentFilter\Traits\FilterTrait;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(Request $request)
{
$this->app->singleton('leftLike', LeftLikeFilter::class);
}
}
自定义过滤器必须实现Huangbule\LaravelEloquentFilter\Contracts\Ifilter接口
namespace App\Preprocess;
use Huangbule\LaravelEloquentFilter\Contracts\Ifilter;
class LeftLikeFilter implements Ifilter {
public function handle($qr, $column, $param) {
//@todo 业务逻辑
}
}
贡献
你可以通过以下三种方式之一进行贡献
代码贡献过程不是很正式。你只需要确保遵循PSR-0、PSR-1和PSR-2编码指南。任何新的代码贡献都必须伴随适用的单元测试。
许可证
MIT