getmaple/laravel-query-builder

轻松从API请求构建Eloquent查询

1.17.2 2019-04-10 14:17 UTC

README

Latest Version on Packagist Build Status StyleCI Quality Score Total Downloads

此包允许您根据请求过滤、排序并包含Eloquent关系。在此包中使用的QueryBuilder扩展了Laravel的默认Eloquent构建器。这意味着您仍然可以使用所有喜爱的方法和宏。查询参数名称尽可能遵循JSON API规范

基本用法

过滤API请求: /users?filter[name]=John

use Spatie\QueryBuilder\QueryBuilder;

// ...

$users = QueryBuilder::for(User::class)
    ->allowedFilters('name')
    ->get();
// all `User`s that contain the string "John" in their name

从API请求请求关系: /users?include=posts

$users = QueryBuilder::for(User::class)
    ->allowedIncludes('posts')
    ->get();
// all `User`s with their `posts` loaded

与现有查询很好地协同工作

$query = User::where('active', true);

$user = QueryBuilder::for($query)
    ->allowedIncludes('posts', 'permissions')
    ->where('score', '>', 42) // chain on any of Laravel's query builder methods
    ->first();

对API请求进行排序: /users?sort=name

$users = QueryBuilder::for(User::class)->get();
// all `User`s sorted by name

请查看下面的用法部分以获取高级示例和功能。

安装

您可以通过composer安装此包

composer require spatie/laravel-query-builder

您可以选择使用以下命令发布配置文件:

php artisan vendor:publish --provider="Spatie\QueryBuilder\QueryBuilderServiceProvider" --tag="config"

这是发布配置文件的内容

return [

    /*
     * By default the package will use the `include`, `filter`, `sort` and `fields` query parameters.
     *
     * Here you can customize those names.
     */
    'parameters' => [
        'include' => 'include',

        'filter' => 'filter',

        'sort' => 'sort',

        'fields' => 'fields',
    ],

];

用法

包括关系

include查询参数将加载结果集合上的任何Eloquent关系。默认情况下,不允许任何包含。所有包含都必须使用allowedIncludes()指定。

// GET /users?include=posts
$users = QueryBuilder::for(User::class)
    ->allowedIncludes('posts')
    ->get();

// $users will contain all users with their posts loaded

您可以通过逗号分隔来加载多个关系

// GET /users?include=posts,permissions
$users = QueryBuilder::for(User::class)
    ->allowedIncludes('posts', 'permissions')
    ->get();

// $users will contain all users with their posts and permissions loaded

您还可以将包含数组传递给allowedIncludes()方法。

// GET /users?include=posts,permissions
$users = QueryBuilder::for(User::class)
    ->allowedIncludes(['posts', 'permissions'])
    ->get();

// $users will contain all users with their posts and permissions loaded

您可以使用点号加载嵌套关系

// GET /users?include=posts.comments,permissions
$users = QueryBuilder::for(User::class)
    ->allowedIncludes('posts.comments', 'permissions')
    ->get();

// $users will contain all users with their posts, comments on their posts and permissions loaded

当尝试使用allowedIncludes()不允许的关系时,将抛出InvalidIncludeQuery异常。

关系/包含名称将在查找模型上对应关系时转换为camelCase。这意味着/users?include=blog-posts将尝试加载User模型上的blogPosts()关系。

一旦关系被加载到结果集合上,您就可以使用Eloquent API资源和条件关系将它们包含在您的响应中。

过滤

您可以使用filter查询参数通过部分属性值、精确属性值或属性值是否存在于给定值数组中过滤结果。您还可以指定更复杂查询的定制过滤器。

默认情况下,不允许任何过滤器。所有过滤器都必须使用allowedFilters()指定。当尝试对使用allowedFilters()不允许的属性进行过滤时,将抛出InvalidFilterQuery异常。

// GET /users?filter[name]=john&filter[email]=gmail
$users = QueryBuilder::for(User::class)
    ->allowedFilters('name', 'email')
    ->get();
// $users will contain all users with "john" in their name AND "gmail" in their email address

您还可以将过滤器数组传递给allowedFilters()方法。

// GET /users?filter[name]=john&filter[email]=gmail
$users = QueryBuilder::for(User::class)
    ->allowedFilters(['name', 'email'])
    ->get();
// $users will contain all users with "john" in their name AND "gmail" in their email address

您可以通过传递逗号分隔的值列表来指定多个匹配的过滤器值

// GET /users?filter[name]=seb,freek
$users = QueryBuilder::for(User::class)
    ->allowedFilters('name')
    ->get();
// $users will contain all users that contain "seb" OR "freek" in their name

属性列别名

有时您可能希望公开用于过滤的属性,而这些属性与数据库列的名称不完全相同。如果您想允许过滤那些可能在数据库中带有前缀的列,可以使用以下符号。

use Spatie\QueryBuilder\Filter;

// GET /users?filter[name]=John
$users = QueryBuilder::for(User::class)
    ->allowedFilters(Filter::exact('name', 'user_name')) // filter by the column 'user_name'
    ->get();

精确过滤器

当根据模型ID、布尔值或字面字符串过滤模型时,您将想要使用精确过滤器。这样,/users?filter[id]=1不会匹配ID中包含数字1的所有用户。

您可以使用Spatie\QueryBuilder\Filter::exact('property_name')allowedFilters()方法中添加精确过滤器。

use Spatie\QueryBuilder\Filter;

// GET /users?filter[name]=John%20Doe
$users = QueryBuilder::for(User::class)
    ->allowedFilters(Filter::exact('name'))
    ->get();
// all users with the exact name "John Doe"

查询构建器将自动将'true''false'映射为布尔值,并将逗号分隔的值列表映射为数组

use Spatie\QueryBuilder\Filter;

// GET /users?filter[id]=1,2,3,4,5&filter[admin]=true
$users = QueryBuilder::for(User::class)
    ->allowedFilters(Filter::exact('id'), Filter::exact('admin'))
    ->get();
// $users will contain all admin users with id 1, 2, 3, 4 or 5

范围过滤器

有时您可能需要构建更复杂的过滤查询。这时范围过滤和自定义过滤就非常有用了。

范围过滤允许您通过在URL中添加过滤器轻松地将本地作用域添加到查询。

考虑以下模型上的作用域

public function scopeStartsBefore(Builder $query, $date): Builder
{
    return $query->where('starts_at', '<=', Carbon::parse($date));
}

要基于startsBefore作用域进行过滤,只需将其添加到查询构建器的allowedFilters中。

QueryBuilder::for(Event::class)
    ->allowedFilters([
        Filter::scope('starts_before'),
    ])
    ->get();

以下过滤器现在会将startsBefore作用域添加到底层查询中。

GET /events?filter[starts_before]=2018-01-01

您甚至可以通过将逗号分隔的列表传递给过滤器来传递多个参数给作用域。

GET /events?filter[starts_between]=2018-01-01,2018-12-31

自定义过滤器

您可以使用Filter::custom()方法指定自定义过滤器。自定义过滤器是简单的可调用类,它实现了\Spatie\QueryBuilder\Filters\Filter接口。这样,您就可以创建任何您想要的查询。

例如

use Spatie\QueryBuilder\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;

class FiltersUserPermission implements Filter
{
    public function __invoke(Builder $query, $value, string $property) : Builder
    {
        return $query->whereHas('permissions', function (Builder $query) use ($value) {
            $query->where('name', $value);
        });
    }
}

use Spatie\QueryBuilder\Filter;

// GET /users?filter[permission]=createPosts
$users = QueryBuilder::for(User::class)
    ->allowedFilters(Filter::custom('permission', FiltersUserPermission::class))
    ->get();
// $users will contain all users that have the `createPosts` permission

过滤器忽略值

您可以指定每个过滤器的一组忽略值。这允许在这些值提交时不应用过滤器。

QueryBuilder::for(User::class)
    ->allowedFilters(Filter::exact('name')->ignore(null))
    ->get();

ignore()方法接受一个或多个值,其中每个值可以是一个忽略值的数组。以下调用都是有效的

  • ignore('should_be_ignored')
  • ignore(null, '-1')
  • ignore([null, 'ignore_me'], ['also_ignored'])

给定一个要过滤的值的数组,只有非忽略值子集被传递给过滤器。如果所有值都被忽略,则过滤器不会应用。

// GET /user?filter[name]=forbidden,John Doe
QueryBuilder::for(User::class)
    ->allowedFilters(Filter::exact('name')->ignore('forbidden'))
    ->get();

// Only users where name matches 'John Doe'

// GET /user?filter[name]=ignored,ignored_too
QueryBuilder::for(User::class)
    ->allowedFilters(Filter::exact('name')->ignore(['ignored', 'ignored_too']))
    ->get();

// Filter does not get applied

排序

用于确定结果集合将按哪个属性排序的查询参数是sort。默认情况下,排序是升序的。在属性名称的开头添加一个连字符(-)将反转结果集合。

// GET /users?sort=-name
$users = QueryBuilder::for(User::class)->get();

// $users will be sorted by name and descending (Z -> A)

默认情况下,所有模型属性都可以用于排序结果。但是,您可以使用allowedSorts方法来限制在请求中允许使用的属性。

当尝试按未在allowedSorts()中指定的属性排序时,将抛出InvalidSortQuery异常。

// GET /users?sort=password
$users = QueryBuilder::for(User::class)
    ->allowedSorts('name')
    ->get();

// Will throw an `InvalidSortQuery` exception as `password` is not an allowed sorting property

要定义一个默认排序参数,该参数应在未显式添加到请求的情况下应用,您可以使用defaultSort方法。

// GET /users
$users = QueryBuilder::for(User::class)
    ->defaultSort('name')
    ->allowedSorts('name', 'street')
    ->get();

// Will retrieve the users sorted by name

如果您想按降序排序默认顺序,可以使用-

// GET /users
$users = QueryBuilder::for(User::class)
    ->defaultSort('-name')
    ->allowedSorts('name', 'street')
    ->get();

// Will retrieve the users sorted descendingly by name

您还可以将排序数组传递给allowedSorts()方法。

// GET /users?sort=name
$users = QueryBuilder::for(User::class)
    ->allowedSorts(['name', 'street'])
    ->get();

// Will retrieve the users sorted by name

您可以通过用逗号分隔它们来按多个属性排序。

// GET /users?sort=name,-street
$users = QueryBuilder::for(User::class)
    ->allowedSorts('name', 'street')
    ->get();

// $users will be sorted by name in ascending order with a secondary sort on street in descending order.

使用排序别名

有时不适宜将列名暴露给用户。

类似于使用过滤时的别名,您也可以这样对排序做。

列名可以作为可选参数传递,默认为属性字符串。

// GET /users?sort=-street
$users = QueryBuilder::for(User::class)
   ->allowedSorts(Sort::field('street', 'actual_column_street')
   ->get();

选择特定列

有时您只想获取几个字段以减少SQL查询的整体大小。这可以通过使用fields查询参数来完成。以下只获取用户idname

GET /users?fields[users]=id,name

SQL查询将看起来像这样

SELECT "id", "name" FROM "users"

使用allowedFields方法,您可以限制在请求中允许查询的字段(列)。

当尝试选择未在allowedFields()中指定的列时,将抛出InvalidFieldQuery异常。

$users = QueryBuilder::for(User::class)
    ->allowedFields('name')
    ->get();

// GET /users?fields[users]=email will throw an `InvalidFieldQuery` exception as `email` is not an allowed field.

选择包含模型的字段的工作方式相同。这在您只需要几个列时包含整个关系时特别有用。考虑以下示例

GET /posts?include=author&fields[author]=name

将获取所有帖子,包括作者的名字。

附加属性

有时您可能想要将一些自定义属性附加到来自模型的查询结果中。这可以通过使用append参数来完成。

class User extends Model
{
    public function getFullnameAttribute()
    {
        return $this->firstname.' '.$this->lastname;
    }
}
// GET /users?append=fullname

$users = QueryBuilder::for(User::class)
    ->allowedAppends('fullname')
    ->get();

当然,您可以传递一个要附加的属性列表。

// GET /users?append=fullname,ranking

其他查询方法

由于QueryBuilder扩展了Laravel的默认Eloquent查询构建器,您可以使用您喜欢的任何方法或宏。您还可以指定一个基础查询,而不是模型完全限定名(FQCN)。

QueryBuilder::for(User::where('id', 42)) // base query instead of model
    ->allowedIncludes('posts')
    ->where('activated', true) // chain on any of Laravel's query methods
    ->first(); // we only need one specific user

分页

此包不提供任何帮助您分页响应的方法。然而,如上所述,您可以使用Laravel的默认paginate()方法。

如果您想完全遵循JSON API规范,您也可以使用我们自己的spatie/json-api-paginate

在前端构建查询

如果您使用Vue,您可能会对vue-api-query包感兴趣,该包由Robson Tenório提供。

测试

composer test

变更日志

有关最近更改的更多信息,请参阅变更日志

贡献

有关详细信息,请参阅贡献指南

安全

如果您发现任何与安全相关的问题,请通过电子邮件freek@spatie.be告知我们,而不是使用问题跟踪器。

明信片软件

您可以自由使用此包,但如果它进入了您的生产环境,我们非常希望您能从您的家乡给我们寄一张明信片,说明您正在使用我们哪个包。

我们的地址是:Spatie,Samberstraat 69D,2060 安特卫普,比利时。

我们将所有收到的明信片发布在我们的公司网站上

鸣谢

支持我们

Spatie是一家位于比利时安特卫普的网络设计公司。您可以在我们的网站上找到我们所有开源项目的概述在这里

您的业务依赖于我们的贡献吗?请与我们联系,并在Patreon上支持我们。所有承诺都将专门用于分配人力以维护和新奇事物。

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件