faridlab/laravel-search-query

搜索查询 - 根据URL查询字符串参数过滤查询的Laravel包

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

This package is auto-updated.

Last update: 2024-09-24 19:20:44 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

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

假设我们想要过滤 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

输出

请注意,默认过滤器参数中的无效值将被查询忽略,并对结果没有影响。

自定义过滤器

通过自定义过滤器,您可以定义自己的方法作为过滤器。这有助于 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();

上述查询只会查询用户名(而不是电子邮件),因为只包含了用户名作为条件。

请注意,过滤器数组必须在条件之前传递。