bjerke/api-query-builder

为Laravel提供的查询构建器,它解析请求并使用Eloquent ORM查询数据库

v1.8.0 2023-07-16 11:16 UTC

This package is auto-updated.

Last update: 2024-09-16 13:46:41 UTC


README

一个库,允许客户端以非常动态的方式查询模型,模仿Eloquent ORM。

安装

composer require bjerke/api-query-builder

配置

配置项不多,但可以配置查询本地化排序子句时使用哪种校对。默认配置为为svsv-SE地区使用utf8mb4_swedish_ci。如果您不需要其他地区的特殊校对,则无需发布此配置。但是,如果您想为特定地区添加其他校对,则需要从库中发布配置文件,以便在您自己的应用程序中修改它。为此,请运行以下Artisan命令:

php artisan vendor:publish --provider="Bjerke\ApiQueryBuilder\QueryBuilderServiceProvider"

现在您将在/config目录下有一个querybuilder.php配置文件,您可以在其中添加额外的地区 => 校对组合

用法

要使用查询构建器,您将使用此库的2个主要组件:特性QueryBuilderModelTrait和构建器本身QueryBuilder

该特性旨在帮助构建器验证请求的字段、关系、可追加属性和计数。以及一些辅助方法。有关如何使用关系、可追加属性和计数的更多信息,请参阅下文。此特性需要包含在您希望使用查询构建器的模型中。

在您的控制器方法中,您可以使用查询构建器根据请求编译Eloquent构建器类,如下所示:

public function index(Request $request)
{
    // Setup the builder
    $queryBuilder = new QueryBuilder(new MyModel, $request);

    // Parse the request and return an Eloquent Builder instance
    $query = $queryBuilder->build();

    // The instance can be extended/modified freely just as any other Eloquent Builder instance
    // For example, maybe we want to enable the option to turn pagination on/off?
    if (($pagination = $request->input('paginate')) !== null &&
        ($pagination === false || $pagination === 'false' || $pagination === '0')
    ) {
        return $query->get();
    }

    $perPage = $request->input('per_page');

    return $query->paginate($perPage)->appends($request->except('page'));
}

可用查询方法

大多数方法都包含一个or对应方法,允许您在查询中创建OR语句。就像Eloquent一样。例如whereorWhere

where / orWhere

执行一个where语句。它可以以多种方式定义。以下将执行一个精确匹配,其中first_name等于test

?where[first_name]=test

您还可以通过定义操作符(=!=like><)进行更复杂的匹配。定义操作符时,您还需要定义一个value参数。以下将执行一个匹配%test%like查询。

?where[first_name][value]=%25test%25&where[first_name][operator]=like

这些方法都是递归的。这意味着您可以在父级"where"中包装多个语句,以匹配其中的所有语句。

whereIn / orWhereIn

类似于where / orWhere,但匹配一组值。值可以定义为逗号分隔的字符串或实际数组。

?whereIn[id]=1,2,3

or

?whereIn[id][]=1&whereIn[id][]=2&whereIn[id][]=3

whereNotIn / orWhereNotIn

whereNotIn / orWhereNotIn 相同,但匹配提供的值的缺失。

whereBetween / orWhereBetween

匹配的列值在提供的两个值之间。值可以定义为逗号分隔的字符串或实际数组。

?whereBetween[date]=2017-01-01,2018-01-01

or

?whereBetween[date][]=2017-01-01&whereBetween[date][]2018-01-01

whereNotBetween / orWhereNotBetween

whereBetween / orWhereBetween 相同,但匹配的值应在提供的范围之外。

whereNull / orWhereNull

?whereNull[]=updated_at

whereNotNull / orWhereNotNull

whereNull / orWhereNull 相同,但匹配的值不应为 null。

whereHas / orWhereHas

查询存在的关系。这需要你的关系被添加到模型上的 allowedApiRelations 数组。否则,它将忽略此查询。

简单的存在检查,只会返回与之相关的任何预订的结果。

?whereHas[]=bookings

根据列值过滤存在检查。只会返回与具有 id 1 的预订相关的结果。

?whereHas[][bookings][id]=1

高级查询。将接受大多数查询方法。

?whereHas[][bookings][whereIn][id]=1,2,3

whereDoesntHave / orWhereDoesntHave

whereHas / orWhereHas 相同,但匹配关系的缺失。

whereDate

按日期查询。此方法的缩写形式为:whereDate / whereMonth / whereDay / whereYear / whereTime。

?whereDate[created_at]=2016-12-31

您还可以通过定义一个运算符(=!=><)来进行更高级的匹配。在定义运算符时,您还需要定义一个 value 参数。

?whereDate[created_at][value]=2016-12-31&where[created_at][operator]=<

search

这是一个方法,使在多个列上执行搜索查询变得稍微容易一些,而不是执行高级的 where-查询。

?search[value]=Jesper&search[columns]=first_name,last_name,phone&search[split]=true

参数

- value: Search query
- columns: Comma separated string or array of column names to search in
- split: Boolean. Defaults to false 
    Optionally set to true  to treat spaces as delimiters for keywords,
    i.e "Jesper Bjerke" will result a query for all "Jesper" and all "Bjerke"
    Without split, it will treat it as a single keyword and match on full "Jesper Bjerke"
- json: Boolean. Defaults to false.
    If the search column is json and you want the search to be case insensitive, set this to true.

select

将数据集限制为仅拉取特定的列。

?select=id,first_name,last_name

or

?select[]=id&select[]=first_name&select[]=last_name

如果您使用 with 加载,还可以选择关系属性。

?select[]=user.first_name

orderBy

根据一个或多个列对结果进行排序。

?orderBy=first_name,desc

或多个列

?orderBy[first_name]=desc&orderBy[created_at]=desc

使用 descasc 定义排序。还有一个特殊的排序 localizedDesclocalizedAsc,它将根据当前区域设置运行预配置的排序。有关更多信息,请参阅 配置

如果您使用 with 加载,也可以根据关系属性进行排序。

?orderBy=user.first_name,desc

groupBy

按列对结果进行分组。

?groupBy=first_name

limit

通过数字限制可能返回的总结果数量。

?limit=2

最多只返回 2 个结果。

with

预加载关系。这需要你的关系被添加到模型上的 allowedApiRelations 数组(阅读更多)。否则,它将忽略此查询。

注意加载大量关系时的性能。只有在您知道只会得到有限的结果集时才这样做。

?with=user,booking

or

?with[]=user&with[]=booking

appends

附加属性。这需要您的属性被添加到模型上的 allowedApiAppends 数组(阅读更多)。否则,它将忽略此查询。

注意加载大量可附加属性的性能。这些属性在每个模型上在查询结果之后进行处理。只有在您知道只会得到有限的结果集且附加属性不会过度压榨数据库等时才这样做。

?appends=full_name,generated_name

or

?appends[]=full_name&appends[]=generated_name

counts

关系计数。这将返回一个整数属性,表示此模型所拥有的关系的数量。

这需要你在模型的 allowedApiCounts 数组中添加你的属性(了解更多)。否则它将忽略这个查询。

注意大量关系计数可能会影响性能。它们会产生额外的数据库访问。

?counts=users,bookings

or

?counts[]=users&counts[]=bookings

分页 / 每页数量

分页不是由查询构建器专门处理的,但这里有一个示例说明你可以如何实现分页

public function index(Request $request)
{
    // Setup the builder
    $queryBuilder = new QueryBuilder(new MyModel, $request);

    // Parse the request and return an Eloquent Builder instance
    $query = $queryBuilder->build();

    // The instance can be extended/modified freely just as any other Eloquent Builder instance
    // For example, maybe we want to enable the option to turn pagination on/off?
    if (($pagination = $request->input('paginate')) !== null &&
        ($pagination === false || $pagination === 'false' || $pagination === '0')
    ) {
        return $query->get();
    }

    $perPage = $request->input('per_page');

    return $query->paginate($perPage)->appends($request->except('page'));
}

现在分页默认为开启状态,要控制查询的分页,你可以现在传递额外的URL参数。

要完全关闭分页

?pagination=false

要调整每页结果数量

?per_page=25

定义允许的字段、关系、可附加属性和数量

为了避免在你的模型上暴露所有内容,你必须定义你想要可查询的每个关系、附加属性或数量。验证在它们之间基本是相同的。唯一的例外是 allowedApiFields,其中默认允许所有标准字段。在你的模型中包含 QueryBuilderModelTrait 后,你可以添加以下方法到其中

// ...
public function allowedApiFields(): array
{
    // Default is ['*']
    return [
        'firstname',
        'lastname'
    ];
}
// ...
// ...
public function allowedApiRelations(): array
{
    return [
        'user' // Must be a relation on your model
    ];
}
// ...
// ...
public function allowedApiCounts(): array
{
    return [
        'user' // Must be a relation on your model
    ];
}
// ...
// ...
public function allowedApiAppends(): array
{
    return [
        'full_name' // Must be an appendable attribute on your model
    ];
}
// ...