在寫後臺介面的時候,我們會使用到大量的篩選,比如狀態篩選、時間篩選、關鍵篩選等等等。
起初在控制器中我是習慣:
..
..
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 協議》,轉載必須註明作者和本文連結