简化我们做后台时对列表的筛选代码

在写后台接口的时候,我们会使用到大量的筛选,比如状态筛选、时间筛选、关键筛选等等等。
起初在控制器中我是习惯:

..
..

public function index(Request $request)
{
    $user = User::query();

    if($status = $request->input('status'))
    {

    }

    if($isAdmin = $request->input('xxx'))
    {

    }

    ....

    return $user->paginate(xxx);
}

后面随着需要筛选的字段越来越多,又或者我的后台比较简单,基本都是只有关键字筛选,这样我们就是一直在写同样的代码。

下面我们利用 局部作用域 来改善一下。

首先我们定义一个 trait:

<?php

namespace xxx;

use App\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;


/**
 * Trait FilterScopeTrait
 *
 * @package App\Models\Traits
 */
trait FilterScopeTrait
{
    /**
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \App\Filters\Filter                    $filter
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 10:15
     */
    public function scopeFilter(Builder $builder, Filter $filter)
    {
        return $filter->filter($builder);
    }
}

然后我们将这个 trait 在模型中引入。我这边图方便就直接定义一个基类 Model

<?php

namespace App\Models;

use xxx\FilterScopeTrait;
use Illuminate\Database\Eloquent\Model as BaseModel;

/**
 * @method static \Illuminate\Database\Eloquent\Builder|static filter(\App\Filters\Filter $filter)
 *
 * @package App\Models
 */
class Model extends BaseModel
{
    use FilterScopeTrait;
}

让我们的 User 继承新的 Model

<?php

namespace App\Models;

/**
 * @method static Builder|User filter(\App\Filters\Filter $filter)
 */
class User extends Model
{
    ...
    ...
    ...
}

定义 Filter

<?php

namespace App\Filters;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;

/**
 * Class Filter
 *
 * @package App\Filters
 */
abstract class Filter
{
    /**
     * @var \Illuminate\Database\Eloquent\Builder
     */
    protected $builder;

    /**
     * @var \Illuminate\Http\Request
     */
    protected $request;

    /**
     * @var array
     */
    protected $filters = [];


    /**
     * Filter constructor.
     *
     * @param  \Illuminate\Http\Request  $request
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }


    /**
     * 筛选项
     *
     * @return array
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 9:53
     */
    protected function getFilters(): array
    {
        return $this->request->only($this->filters);
    }


    /**
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 10:02
     */
    public function filter(Builder $builder)
    {
        $this->builder = $builder;

        foreach ($this->getFilters() as $filter => $value) {
            if (method_exists($this, $filter)) {
                $this->$filter($value);
            }
        }

        return $this->builder;
    }


    /**
     * 转布尔值
     *
     * @param  string  $val  要转的值
     *
     * @return bool
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/25 16:04
     */
    protected function str2Bool(string $val)
    {
        return filter_var($val, FILTER_VALIDATE_BOOLEAN);
    }

}

接下来就是定义 UserFilter 来专门处理 User 可能的筛选

<?php

namespace App\Filters;

/**
 * Class AdminOperatingLogFilter
 *
 * @package App\Filters
 */
class UserFilter extends Filter
{

    /**
     * @var string[]
     */
    protected $filters = [
        'keyword', 'regisiterTime', 'type', 'isActive',
    ];


    /**
     * 关键字筛选
     *
     * @param  string  $keyword
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 10:35
     */
    public function keyword(string $keyword)
    {
        return $this->builder->where('nickname', 'like', "%{$keyword}%")->orWhere('username', 'like', "%{$keyword}%");
    }


    /**
     * @param  array  $rangeTime
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 10:35
     */
    public function regisiterTime(array $regisiterTime)
    {
        return $this->builder->whereBetween('created_at', $regisiterTime);
    }


    /**
     * @param $isActive
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 10:35
     */
    public function isActive($isActive)
    {
        return $this->builder->where('is_active', $this->str2Bool($isActive));
    }


    /**
     * @param  string  $type
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/24 10:35
     */
    public function type(string $type)
    {
        return $this->builder->where('type', $type);
    }


}

然后我们在控制器中使用:

<?php

namespace xxx;

use App\Filters\UserFilter;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

/**
 * Class UserController
 *
 * @package App\Http\Controllers\Admin
 */
class UserController extends Controller
{


    /**
     * @param  \Illuminate\Http\Request    $request
     * @param  \App\Filters\UserFilter     $filter
     *
     * @return \Illuminate\Http\JsonResponse
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/08/29 9:58
     */
    public function index(Request $request, UserFilter $filter)
    {
        $users = User::filter($filter)->latest()->paginate($request->input("limit"));

        return \response()->json($users);
    }

如此一来我们的代码就很简洁了。把我们的所有筛选操作转移到了 UserFilter

还有一种情况就是基本只做个关键字筛选,我们定义一个 KeywordFilter:

<?php

namespace App\Filters;

use Illuminate\Database\Eloquent\Builder;

/**
 * Class KeywordFilter
 *
 * @package App\Filters
 */
class KeywordFilter extends Filter
{

    /**
     * @var string[]
     */
    protected $filters = [
        'keyword',
    ];

    /**
     * @var array
     */
    protected $fields = [];


    /**
     * 设置想要搜索的字段
     *
     * @param  string|array  $field
     *
     * @return $this
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/26 14:29
     */
    public function field($field)
    {
        $field = is_array($field) ? $field : func_get_args();

        $this->fields = $field;

        return $this;
    }


    /**
     * 关键字筛选
     *
     * @param  string|null  $keyword  搜索关键字
     *
     * @return \Illuminate\Database\Eloquent\Builder
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/11/26 14:32
     */
    protected function keyword(?string $keyword) : Builder
    {
        if (empty($this->fields)) {
            return $this->builder;
        }

        return $this->builder->where(function (Builder $builder) use ($keyword) {
            $fields = $this->fields;
            $first  = array_shift($fields);
            $builder->where($first, "like", "%{$keyword}%");
            if (!empty($fields)) {
                foreach ($fields as $field) {
                    $builder->orWhere($field, "like", "%{$keyword}%");
                }
            }
        });
    }


}

使用:

<?php

namespace App\Http\Controllers\Admin;

use App\Filters\KeywordFilter;
use App\Http\Controllers\Controller;
use App\Http\Requests\AdminUserRequest;
use App\Models\AdminUser;
use Illuminate\Http\Request;

/**
 * Class AdminUserController
 *
 * @package App\Http\Controllers\Admin
 */
class AdminUserController extends Controller
{


    /**
     * @param  \Illuminate\Http\Request    $request
     * @param  \App\Filters\KeywordFilter  $filter
     *
     * @return \Illuminate\Http\JsonResponse
     *
     * @author   : stringer <10******56@qq.com>
     * @datetime : 2020/08/29 9:58
     */
    public function index(Request $request, KeywordFilter $filter)
    {
        $adminUsers = AdminUser::filter($filter->field('username', 'nickname'))
                        ->latest()
                        ->paginate($request->input("limit"));

         return \response()->json($adminUsers);
    }

我们在 field 方法里面传入可以进行搜索的匹配的字段即可。

如此一来,如果说有很多张表需要筛选。我们就新增一个 XxxxFilter
如果一张表里面有很多个字段需要筛选,我们就在该 xxxFilter 下新增筛选方法即可。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1
3年前 评论
琳琅天上 (楼主) 3年前
Imuyu (作者) 3年前
mouyong 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!