huangbule/laravel-eloquent-filter

轻松过滤Eloquent模型

v1.1.2 2024-06-14 06:37 UTC

This package is auto-updated.

Last update: 2024-09-14 07:09:54 UTC


README

轻松过滤Eloquent模型。

依赖

  1. PHP7.4+
  2. 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中等于情况,那么我们为何不把它们封装起来呢,为什么要重复写呢?

设计思路

  1. 参考laravel validate写法,简化语句

  2. 引入操作符比如like、>=、between等等,而且大家可以自己拓展

  3. 引入前置处理器在最终执行where语句之前,我们可能需要对$input用户传过来的数据做下处理,比如上面的created_at[2024-03-14, 2024-03-20]其实20号需要加一天,因为created_at是datetime类型。而且必须可拓展

  4. 引入别名比如标题我们数据库用title,但是由于特殊原因不能用这个字段,比如传order_title

  5. 确定搜索规则格式:搜索字段:前置处理器(0-n个)|操作符(可能没有默认)为了区分前置处理器操作符操作符做个特殊表示用$开头比如$like、$eq

  6. 订单列表返回了用户信息,这时候我们想搜索用户标题怎么办?必须支持关联关系查询,设定关联关系用#开头比如['title:#user'],然后必须在order表里面定义user()关联方法,默认就查询user表里面的title了,原理用whereHas

  7. 引入配置文件,因为大部分字段搜索的规则都差不多的,我总不可能每个里面都写吧,比如['title:$like'],我在每个控制器里User、Order面都写一遍,那岂不是太麻烦了

  8. 如果你既想在配置直接定义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 业务逻辑
       
    }
}

贡献

你可以通过以下三种方式之一进行贡献

  1. 使用问题跟踪器提交错误报告。
  2. 问题跟踪器上回答问题或修复错误。
  3. 贡献新功能或更新wiki。

代码贡献过程不是很正式。你只需要确保遵循PSR-0、PSR-1和PSR-2编码指南。任何新的代码贡献都必须伴随适用的单元测试。

许可证

MIT