faridlab / laravel-search-query
搜索查询 - 根据URL查询字符串参数过滤查询的Laravel包
Requires
- php: >=7.1
- laravel/framework: >=6.20.14
Requires (Dev)
- orchestra/testbench: ^4.0
README
轻松根据URL查询字符串参数过滤查询。
兼容Laravel 5.x 6.x 7.x 8.x.
目录
描述问题
你可能遇到过需要根据URL查询字符串中给定的参数过滤查询的情况。在开发逻辑后,你可能有了这样的代码
$users = User::latest(); if(request('username')) { $users->where('username', request('username')); } if(request('age')) { $users->where('age', '>', request('age')); } if(request('email')) { $users->where('email', request('email')); } return $users->get();
这可以工作,但这不是一种好的做法。
当参数的数量开始增加时,这种类型的if
语句的数量也会增加,你的代码会变得庞大且难以维护。
这也违反了SOLID原则中的开放/封闭原则,因为当你有一个新的参数时,你需要进入现有的代码并添加新的逻辑(这可能会破坏现有的实现)。
因此,我们需要设计一种方法来使我们的过滤器逻辑彼此分离,并将它们应用到最终的查询中,这正是这个包背后的整个理念。
使用方法
- 首先,你需要安装这个包
$ composer require faridlab/laravel-grammatical-query
use SearchQuery\FilterQueryString\FilterQueryString; class User extends Model { use FilterQueryString; protected $filters = []; ... }
- 你需要在你的Eloquent查询中使用
filter()
方法。例如
User::select('name')->filter()->get();
可用方法
过滤器:
- 字段:
数组 - 可选
- 搜索:
字符串 - 可选
- 页面:
整数 default(1) - 可选
- 限制:
整数 default(25) - 可选
- 关系:
数组 - 可选
- 计数:
数组 - 可选
- 包含已删除的:
布尔值 default(false) - 可选
- 排序:
数组 - 可选
- 字段名[where]:
字符串|数组 - 可选
- 字段名[orwhere]:
字符串|数组 - 可选
- fieldname[eq]:
string|integer ― 可选
- fieldname[notEq]:
string|integer ― 可选
- fieldname[gt]:
string|integer ― 可选
- fieldname[gtEq]:
string|integer ― 可选
- fieldname[lt]:
string|integer ― 可选
- fieldname[ltEq]:
string|integer ― 可选
- fieldname[like]:
string ― 可选
- fieldname[notlike]:
string ― 可选
- fieldname[contain]:
string ― 可选
- fieldname[notcontain]:
string ― 可选
- fieldname[startwith]:
string ― 可选
- fieldname[endwith]:
string ― 可选
- fieldname[in]:
array ― 可选
- fieldname[notin]:
array ― 可选
- fieldname[between]:
array ― 可选
- fieldname[notbetween]:
array ― 可选
- fieldname[isnull]:
string ― 可选
- fieldname[isnotnull]:
string ― 可选
为了解释每个方法,假设我们有一个这样的数据在 users
表中
假设我们的查询是这样的
User::filter()->get();
字段
fields: array|string ― optional
约定
> GET /api/v1/users?fields={fieldname}
> GET /api/v1/users?fields[]={fieldname1}
> GET /api/v1/users?fields[]={fieldname2}
> GET /api/v1/users?fields=name
> GET /api/v1/users?fields[]=name&fields[]=email
在 Users.php 中
protected $filters = ['fields'];
示例: https://startapp.id/api/v1/users?fields[]=name&fields[]=email
搜索
search: string ― optional
约定
> GET /api/v1/users?search={fieldname}
> GET /api/v1/users?search=faridlab
在 Users.php 中
protected $filters = ['search'];
示例: https://startapp.id/api/v1/users?search=faridlab
页面
page: integer default(1) ― optional
约定
> GET /api/v1/users?page={page}
> GET /api/v1/users?page=1
在 Users.php 中
protected $filters = ['page'];
示例: https://startapp.id/api/v1/users?page=1
限制
limit: integer default(25) ― optional
约定
> GET /api/v1/users?limit={limit}
> GET /api/v1/users?limit=25
在 Users.php 中
protected $filters = ['limit'];
示例: https://startapp.id/api/v1/users?limit=25
关系
relationship: array|string ― optional
约定
> GET /api/v1/users?relationship={relation}
> GET /api/v1/users?relationship[]={relation1}
> GET /api/v1/users?relationship[]={relation2}
> GET /api/v1/users?relationship=role
> GET /api/v1/users?relationship[]=role
> GET /api/v1/users?relationship[]=permissions
在 Users.php 中
protected $filters = ['relationship'];
示例: https://startapp.id/api/v1/users?relationship=role
https://startapp.id/api/v1/users?relationship[]=role&relationship[]=permissions
计数
count: array|string ― optional
约定
> GET /api/v1/users?count={relation}
> GET /api/v1/users?count[]={relation1}
> GET /api/v1/users?count[]={relation2}
> GET /api/v1/users?count=addresses
> GET /api/v1/users?count[]=photos
> GET /api/v1/users?count[]=accounts
在 Users.php 中
protected $filters = ['count'];
示例: https://startapp.id/api/v1/users?count=addresses
https://startapp.id/api/v1/users?count[]=photos&count[]=accounts
Withtrashed
withtrashed: boolean default(false) ― optional
约定
> GET /api/v1/users?withtrashed=true
> GET /api/v1/users?withtrashed=true
在 Users.php 中
protected $filters = ['withtrashed'];
示例: https://startapp.id/api/v1/users?withtrashed=true
Orderby
orderby: array|string ― optional
Orderby 等同于 SQL 中的 order by
语句,可以在 FilterQueryString
中灵活使用
约定
> GET /api/v1/users?orderby={fieldname} > GET /api/v1/users?orderby[{fieldname}]={asc|desc}&orderby[{fieldname}]={asc|desc} > GET /api/v1/users?orderby=name > GET /api/v1/users?orderby[id]=desc&orderby[name]=asc > GET /api/v1/users?orderby[id]=desc&orderby[name]=asc&orderby[email]=asc
在 Users.php 中
protected $filters = ['orderby'];
单个 sort
:
https://startapp.id/api/v1/users?orderby=created_at
输出
- 注意 当你传递参数不是数组而是字符串时,它将用作字段名,并默认按 'asc' 排序。
多个 sort
:
https://startapp.id/api/v1/users?orderby[name]=asc&orderby[email]=desc
输出
请注意 任何无效的 orderby
参数都将被查询忽略并不会影响结果。
WHERE...
fieldname[where]: string|array ― optional
约定
> GET /api/v1/users?{fieldname}={searchtext}
> GET /api/v1/users?{fieldname}[]={searchtext}
> GET /api/v1/users?{fieldname}[where]={searchtext}
> GET /api/v1/users?username=faridlab
> GET /api/v1/users?username[]=faridlab
> GET /api/v1/users?username[where]=faridlab
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username=faridlab
https://startapp.id/api/v1/users?username[]=faridlab
https://startapp.id/api/v1/users?username[where]=faridlab
或者
fieldname[orwhere]: string|array ― optional
约定
> GET /api/v1/users?{fieldname}[orwhere]={searchtext}
> GET /api/v1/users?{fieldname}[orwhere][]={searchtext}
> GET /api/v1/users?{fieldname}[orwhere][]={searchtext}
> GET /api/v1/users?username[where]=faridlab
> GET /api/v1/users?username[where][]=faridlab
> GET /api/v1/users?username[where][]=mehrad123
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[where]=faridlab
https://startapp.id/api/v1/users?username[where][]=faridlab
https://startapp.id/api/v1/users?username[where][]=mehrad123
等于
fieldname[eq]: string|integer ― optional
约定
> GET /api/v1/users?{fieldname}[eq]={searchtext}
> GET /api/v1/users?{fieldname2}[eq]={searchtext}
> GET /api/v1/users?username[eq]=faridlab&email[eq]=farid@startapp.id
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[eq]=faridlab&email[eq]=farid@startapp.id
不等于
fieldname[notEq]: string|integer ― optional
约定
> GET /api/v1/users?{fieldname}[notEq]={searchtext}
> GET /api/v1/users?{fieldname2}[notEq]={searchtext}
> GET /api/v1/users?username[notEq]=faridlab&email[notEq]=farid@startapp.id
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[notEq]=faridlab&email[notEq]=farid@startapp.id
大于
fieldname[gt]: string|integer ― optional
约定
> GET /api/v1/users?{fieldname}[gt]={searchtext}
> GET /api/v1/users?id[gt]=10
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?id[gt]=10
大于等于
fieldname[gtEq]: string|integer ― optional
约定
> GET /api/v1/users?{fieldname}[gtEq]={searchtext}
> GET /api/v1/users?id[gtEq]=10
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?id[gtEq]=10
小于
fieldname[lt]: string|integer ― optional
约定
> GET /api/v1/users?{fieldname}[lt]={searchtext}
> GET /api/v1/users?id[lt]=10
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?id[lt]=10
小于等于
fieldname[ltEq]: string|integer ― optional
约定
> GET /api/v1/users?{fieldname}[ltEq]={searchtext}
> GET /api/v1/users?id[ltEq]=10
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?id[ltEq]=10
类似...
fieldname[like]: string ― optional
约定
> GET /api/v1/users?{fieldname}[like]={searchtext}
> GET /api/v1/users?username[like]=faridlab
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[like]=faridlab
不类似
fieldname[notlike]: string ― optional
约定
> GET /api/v1/users?{fieldname}[notlike]={searchtext}
> GET /api/v1/users?username[notlike]=faridlab
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[notlike]=faridlab
包含
fieldname[contain]: string ― optional
约定
> GET /api/v1/users?{fieldname}[contain]={searchtext}
> GET /api/v1/users?username[contain]=farid
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[contain]=farid
不包含
fieldname[notcontain]: string ― optional
约定
> GET /api/v1/users?{fieldname}[notcontain]={searchtext}
> GET /api/v1/users?username[notcontain]=farid
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[notcontain]=farid
以...开始
fieldname[startwith]: string ― optional
约定
> GET /api/v1/users?{fieldname}[startwith]={searchtext}
> GET /api/v1/users?username[startwith]=farid
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[startwith]=farid
以...结束
fieldname[endwith]: string ― optional
约定
> GET /api/v1/users?{fieldname}[endwith]={searchtext}
> GET /api/v1/users?username[endwith]=lab
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[endwith]=lab
在...
fieldname[in]: array ― optional
约定
> GET /api/v1/users?{fieldname}[in][]={searchtext}&{fieldname}[in][]={searchtext2}
> GET /api/v1/users?username[in][]=faridlab&username[in][]=farid
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[in][]=faridlab&username[in][]=farid
不在...
fieldname[notin]: array ― optional
约定
> GET /api/v1/users?{fieldname}[notin][]={searchtext}&{fieldname}[notin][]={searchtext2}
> GET /api/v1/users?username[notin][]=faridlab&username[notin][]=farid
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?username[notin][]=faridlab&username[notin][]=farid
在...
fieldname[between]: array ― optional
约定
> GET /api/v1/users?{fieldname}[between][]={searchtext}&{fieldname}[between][]={searchtext2}
> GET /api/v1/users?id[between][]=1&id[between][]=10
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?id[between][]=1&id[between][]=10
不在...
fieldname[notbetween]: array ― optional
约定
> GET /api/v1/users?{fieldname}[notbetween][]={searchtext}&{fieldname}[notbetween][]={searchtext2}
> GET /api/v1/users?id[notbetween][]=1&id[notbetween][]=10
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?id[notbetween][]=1&id[notbetween][]=10
为空
fieldname[isnull]: string ― optional
约定
> GET /api/v1/users?{fieldname}[isnull]={null|''}
> GET /api/v1/users?deleted_at[isnull]=null
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?deleted_at[isnull]=null
https://startapp.id/api/v1/users?deleted_at[isnull]
不为空
fieldname[isnotnull]: string ― optional
约定
> GET /api/v1/users?{fieldname}[isnotnull]={null|''}
> GET /api/v1/users?deleted_at[isnotnull]=null
在 Users.php 中
protected $filters = [];
示例: https://startapp.id/api/v1/users?deleted_at[isnotnull]=null
https://startapp.id/api/v1/users?deleted_at[isnotnull]
不同的
fieldname[distinct]: boolean ― optional
约定
> GET /api/v1/users?{fieldname}[distinct]={true|1}
> GET /api/v1/users?first_name[distinct]=true
在 Users.php 中
protected $filters = ['distinct'];
示例: https://startapp.id/api/v1/users?first_name[distinct]=true
https://startapp.id/api/v1/users?first_name[distinct]=1
https://startapp.id/api/v1/users?first_name[distinct]
Where 子句(默认过滤器)
通常,当查询字符串参数不是之前提供的可用方法之一时,它将通过默认过滤器进行过滤,该过滤器是 where
sql 语句。当您需要直接过滤表中的一个列时,它是合适的过滤器。
约定
?field=value
?field1=value&field2=value
?field1[0]=value1&field1[1]=value2
?field1[0]=value1&field1[1]=value2&field2[0]=value1&field2[1]=value2
假设我们想要过滤 name
, username
和 age
数据库列,在 Users.php
protected $filters = ['name', 'username', 'age'];
示例:
https://startapp.id?name=mehrad
输出
示例:
https://startapp.id?age=22&username=dariush123
输出
示例:
https://startapp.id?name[0]=mehrad&name[1]=dariush
输出
示例:
https://startapp.id?name[0]=mehrad&name[1]=dariush&username[0]=mehrad123&username[1]=reza1234
输出
请注意,默认过滤器参数中的无效值将被查询忽略,并对结果没有影响。
自定义过滤器
通过自定义过滤器,您可以定义自己的方法作为过滤器。这有助于 SOLID 原则中的开放/关闭,因此每次需要新的过滤器时,您不必编辑以前的过滤器,而可以为其编写单独的方法。
让我们创建一个自定义过滤器。假设您想创建一个名为 all_except
的过滤器,它检索除指定的用户之外的所有用户
在 Users.php 中
protected $filters = ['all_except']; public function all_except($query, $value) { return $query->where('name', '!=', $value); }
为了测试我们新添加的过滤器
https://startapp.id?all_except=mehrad
输出
注意,您自定义定义的过滤器具有最高优先级,这意味着您可以覆盖现有的过滤器。
例如,让我们将 in
过滤器更改为只能接受 3 个值的方式
在 Users.php 中
protected $filters = ['in']; public function in($query, $value) { $exploded = explode(',', $value); if(count($exploded) != 4) { // throwing an exception or whatever you like to do } $field = array_shift($exploded); return $query->whereIn($field, $exploded); }
自定义过滤器的另一个好例子是当您不想暴露数据库表的列名时。例如,假设我们不希望在 users
表中暴露名为 username
的列
在 Users.php 中
protected $filters = ['by']; public function by($query, $value) { return $query->where('username', $value); }
https://startapp.id?by=dariush123
输出
小贴士
为了防止您的模型变得混乱或填充了太多的过滤器方法,您可以为它创建一个 trait,并将有关过滤器的一切都放在 trait 中。
条件过滤器
模型中的 $filters
属性对模型来说有点像全局属性。这意味着当您在 eloquent 查询上使用 filter()
方法时,它将始终执行所有的 $filters
过滤器。
可能会有一些情况,基于条件,您需要指定哪些过滤器确切地需要被过滤。
为了实现这一点,您可以在 filter()
方法中指定您希望的过滤器作为参数。
示例
在您的查询中
User::filter('in')->get();
in=name,mehrad,reza&like=name,mehrad
输出
如果未指定 in
参数,则查询的结果将只有一条记录(mehrad
)。
另一个示例
在您的查询中
User::filter('like', 'name')->get();
like=name,mehrad,reza,dariush,hossein&name[0]=mehrad&name[1]=hossein&username=mehrad
输出
手动传递过滤器数组(Livewire)
当使用 Livewire 过滤数据时,后续的查询字符串更改不会触发新的请求。我们可以通过手动传递一个过滤器数组来解决这个问题。
示例
User::filter(['username' => 'mehrad123'])->get();
另一个示例
User::filter([ 'username' => [ 'contain' => 'medhrad', ], 'email' => [ 'contain' => 'startapp.id', ] ])->get();
您还可以结合条件过滤器
User::filter([ 'username' => 'mehrad123', 'email' => [ 'contains' => 'startapp.id' ] ], 'username')->get();
上述查询只会查询用户名(而不是电子邮件),因为只包含了用户名作为条件。
请注意,过滤器数组必须在条件之前传递。