faridlab / laravel-grammatical-query
搜索查询 - Laravel 包,用于基于 URL 查询字符串参数过滤查询
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();
可用方法
过滤器:
- fields:
array ― 可选
- search:
string ― 可选
- page:
integer default(1) ― 可选
- limit:
integer default(25) ― 可选
- relationship:
array ― 可选
- count:
array ― 可选
- withtrashed:
boolean default(false) ― 可选
- orderby:
array ― 可选
- fieldname[where]:
string|array ― 可选
- fieldname[orwhere]:
string|array ― 可选
- fieldname[eq]:
string|integer ― 可选
- 字段名[notEq]:
字符串|整数 ― 可选
- 字段名[gt]:
字符串|整数 ― 可选
- 字段名[gtEq]:
字符串|整数 ― 可选
- 字段名[lt]:
字符串|整数 ― 可选
- 字段名[ltEq]:
字符串|整数 ― 可选
- 字段名[like]:
字符串 ― 可选
- 字段名[notlike]:
字符串 ― 可选
- 字段名[contain]:
字符串 ― 可选
- 字段名[notcontain]:
字符串 ― 可选
- 字段名[startwith]:
字符串 ― 可选
- 字段名[endwith]:
字符串 ― 可选
- 字段名[in]:
数组 ― 可选
- 字段名[notin]:
数组 ― 可选
- 字段名[between]:
数组 ― 可选
- 字段名[notbetween]:
数组 ― 可选
- 字段名[isnull]:
字符串 ― 可选
- 字段名[isnotnull]:
字符串 ― 可选
为了解释每个方法,假设我们在 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: 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: 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 子句(默认过滤器)
通常情况下,如果您的查询字符串参数不是上述可用方法之一,它将通过默认过滤器进行过滤,该默认过滤器是 SQL 语句中的 where
。当您需要直接过滤您表中的一列时,这是一个合适的过滤器。
约定
?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
输出
请注意,无效值的 default
过滤器参数将被查询忽略,并且对结果没有影响。
自定义过滤器
通过自定义过滤器,您可以定义自己的方法作为过滤器。这有助于 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
输出
小贴士
为了防止您的模型变得混乱或被过滤方法填充,您可以为此创建一个特质,并将有关过滤器的一切都放在特质中。
条件过滤器
模型中的 $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();
上面的查询将只查询用户名(不是电子邮件),因为只包含用户名作为条件。
注意,必须先传递过滤器数组,然后再传递条件。