faridlab/laravel-grammatical-query

搜索查询 - Laravel 包,用于基于 URL 查询字符串参数过滤查询

v9.0.5 2022-11-24 15:34 UTC

This package is auto-updated.

Last update: 2024-09-24 20:01:45 UTC


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 原则中的开放/关闭原则,因为当你有一个新参数时,你需要进入现有的代码并添加新的逻辑(这可能会破坏现有的实现)。

因此,我们必须设计一种方法来使我们的过滤器逻辑相互分离,并将它们应用到最终的查询中,这正是这个包背后的整个想法。

使用方法

  1. 首先,你需要安装这个包

$ composer require faridlab/laravel-grammatical-query

  1. 然后,你应该在你的模型中使用 FilterQueryString 特性,并定义 $filters 属性,它可以包含 可用过滤器 或你的 自定义过滤器
use SearchQuery\FilterQueryString\FilterQueryString;

class User extends Model
{
    use FilterQueryString;

    protected $filters = [];

    ...
}
  1. 你需要在你的 Eloquent 查询中使用 filter() 方法。例如
User::select('name')->filter()->get();

可用方法

过滤器:

为了解释每个方法,假设我们在 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

假设我们想过滤 nameusernameage 数据库列,在 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();

上面的查询将只查询用户名(不是电子邮件),因为只包含用户名作为条件。

注意,必须先传递过滤器数组,然后再传递条件。