asseco-voice / laravel-json-query-builder
Laravel JSON 查询构建器
Requires
- php: ^8.1
- laravel/framework: ^10.0
Requires (Dev)
- fakerphp/faker: ^1.9.1
- mockery/mockery: ^1.4.4
- orchestra/testbench: ^8.5
- phpunit/phpunit: ^10.0
- dev-master
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.0
- v2.0.0
- v1.3.0
- v1.2.0
- v1.1.1
- v1.1.0
- v1.0.0
- v0.5.0
- v0.4.1
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.0
- dev-VSLIV30-3966_quick_search_and_relations_search
- dev-VSLIV30-3515-Creation-Time-Filter-Misbehaviour
- dev-vsliv_enable_raw_arguments
- dev-dependabot/composer/guzzlehttp/psr7-2.5.0
This package is auto-updated.
Last update: 2024-09-19 13:12:27 UTC
README
Laravel JSON 查询构建器
此软件包允许根据以下特殊逻辑从 JSON 对象构建查询。
安装
通过 composer 安装软件包。它将自动注册为 Laravel 服务提供者。
composer require asseco-voice/laravel-json-query-builder
使用方法
为了使用此软件包,您需要实例化 JsonQuery()
并向其提供两个依赖项。一个是 Illuminate\Database\Eloquent\Builder
实例,另一个是 JSON/数组输入。
实例化后,您需要运行 search()
方法,查询将在提供的构建器对象上构建。
$jsonQuery = new JsonQuery($builder, $input);
$jsonQuery->search();
此软件包的命名约定
- 参数 是顶级 JSON 键名(见下文选项 以下)
- 参数 是参数值。顶层 JSON 中的所有内容。
- 参数 是单个键值对。
- 单个参数进一步分解为 列 / 操作符 / 值
{
"search": { <-- parameter
"first_name": "=foo", <-- argument
"last_name": " = bar " <-- argument
ˆˆˆˆˆˆˆˆˆ ˆ ˆˆˆ
column operator value
}
}
参数分解
参数遵循特殊逻辑以查询数据库。可以使用以下 JSON 参数(键)
search
- 将执行查询逻辑(详见下文 以下)returns
- 将仅返回提供的列。order_by
- 将根据提供的值对结果进行排序。group_by
- 将根据提供的值对结果进行分组。relations
- 将加载给定模型的关联。limit
- 将限制返回的结果。offset
- 将从给定点开始返回结果子集。此参数 必须 与limit
参数一起使用。count
- 将返回记录数。soft_deleted
- 将包括软删除模型在搜索结果中。doesnt_have_relations
- 将仅返回没有指定任何关联的条目。
搜索
逻辑以 "column": "operator values"
的形式执行,其中我们假设以下内容
column
代表数据库中的列。多个键可以分开,作为新的 JSON 键值对。- 可以使用
.
作为分隔符通过相关模型进行搜索,例如"relation.column": "operator value"
。 注意 这将执行WHERE EXISTS
,如果包含在关联中,则不会过滤结果关联。要执行WHERE NOT EXISTS
,可以使用!relation.column
。 operator
是可用的主要查询操作符之一(下文列出 以下)values
是分号 (;
) 分隔的值列表(例如"column": "=value;value2;value3"
),它们也可以有微操作符(例如"column": "=value;!value2;%value3%"
)。
主要操作符
=
- 等于!=
- 不等于<
- 小于(需要正好一个值)>
- 大于(需要正好一个值)<=
- 小于等于(需要正好一个值)>=
- 大于等于(需要正好一个值)<>
- 介于(需要正好两个值)!<>
- 不在范围内(需要恰好两个值)
示例
{
"search": {
"first_name": "=foo1;foo2",
"last_name": "!=bar1;bar2"
}
}
将执行一个 SELECT * FROM some_table WHERE first_name IN ('foo1', 'foo2') AND last_name NOT IN ('bar1', 'bar2')
。
如果您为字符串类型的列传递单个值,将使用 LIKE 操作符而不是 IN。对于 Postgres 数据库,使用 ILIKE 而不是 LIKE 以支持不区分大小写的搜索。
{
"search": {
"first_name": "=foo",
"last_name": "!=bar"
}
}
将执行一个 SELECT * FROM some_table WHERE first_name LIKE 'foo' AND last_name NOT LIKE 'bar'
。
微操作符
!
- 取消值。仅在值的开始处工作(即!value
)。%
- 执行 LIKE 查询。仅在值的开始、结束或两端工作(即%value
、value%
或%value%
)。- 逻辑运算符用于使用 多个运算符(您不能对单个列执行
=1||2
,但可以执行=1||=2
)对于单个列(顺序很重要!)&&
使您能够使用 AND 连接值||
使您能够使用 OR 连接值
{
"search": {
"first_name": "=!foo",
"last_name": "=bar%"
}
}
将执行一个 SELECT * FROM some_table WHERE first_name NOT LIKE 'foo' AND last_name LIKE 'bar%'
。
注意,在这里 !value
的行为与 !=
主运算符相同。区别在于 !=
主运算符取消整个值列表,而 !value
仅取消该特定值。即 !=value1;value2
与 =!value1;!value2
的语义相同。
逻辑运算符示例
{
"search": {
"first_name": "=foo||=bar",
}
}
将执行 SELECT * FROM some_table WHERE first_name IN ('foo') OR first_name IN ('bar')
。
请注意,逻辑运算符使用标准的布尔逻辑优先级,因此 x AND y OR z AND q
与 (x AND y) OR (z AND q)
相同。
嵌套关系搜索
如果使用的键是关系名称,则可以嵌套另一个搜索对象,这将执行一个 whereHas()
查询构建方法。
即
{
"search": {
"some_relation": {
"search": { ... }
},
}
}
返回
使用 returns
键将有效地仅返回其中指定的字段。此运算符接受值数组或单个值。
示例
返回单个值
{
"returns": "first_name",
}
将执行一个 SELECT first_name FROM ...
返回多个值
{
"returns": ["first_name", "last_name"]
}
将执行一个 SELECT first_name, last_name FROM ...
按顺序排列
使用 order_by
键根据给定键执行 'order by'。键的顺序很重要!
参数假定以 "column": "direction"
的形式提供,其中 direction
必须是 asc
(升序)或 desc
(降序)。如果只提供列,则方向将被假定为升序。
示例
{
"order_by": {
"first_name": "asc",
"last_name": "desc"
}
}
将执行一个 SELECT ... ORDER BY first_name asc, last_name desc
分组
使用 group_by
键根据给定键执行 'group by'。键的顺序很重要!
参数假定是单个属性或属性数组。
由于分组的行为类似于普通 SQL 查询,请确保选择正确的字段和聚合函数。
示例
{
"group_by": ["last_name", "first_name"]
}
将执行一个 SELECT ... GROUP BY last_name, first_name
关系
还可以通过使用 relations
参数加载对象关系。此运算符接受值数组或单个值。
简单
示例
解析单个关系
{
"relations": "containers",
}
解析多个关系
{
"relations": ["containers", "addresses"]
}
如果定义正确并遵循 Laravel 习惯,关系应该是可预测的
- 1:1 & M:N - 关系名称为复数(例如,Contact 有许多 Addresses,因此关系名称为 'addresses')
- M:1 - 关系名称为单数(例如,Comment 属于一个 Post,因此关系名称为 'post')
- 重要:由于 Laravel 以 snake_case 形式返回 API 响应,因此可以提供 snake_case 关系(尽管 camelCase 也可以工作),用于多词关系。即执行
"relations": "workspace_items"
等同于调用"relations": "workspaceItems"
,但建议使用 snake_case 方法。
可以使用点符号递归加载关系
{
"relations": "media.type"
}
这将同时加载媒体关系并立即解析媒体类型。如果您需要解析多个二级关系,可以提供一个包含这些关系的数组
{
"relations": ["media.type", "media.category"]
}
这将加载媒体关系,同时为每个媒体对象加载解析的类型和类别。
还可以使用点符号无限制地堆叠关系。但请注意,这可能会对性能造成严重影响!
{
"relations": "media.type.contact.title"
}
复杂
关系也可以是一个包含嵌套搜索属性的对象,以进一步过滤给定的结果集。
示例
"relations": [
{
"media": {
"search": {
"media_type_id": "=1"
}
}
}
]
将在对象上加载一个 media
关系,但只返回 media_type_id
等于 1
的对象。
限制
您可以通过以下方式限制获取的结果数量
{
"limit": 10
}
这将执行 SELECT * FROM table LIMIT 10
。
偏移量
您可以使用偏移量进一步限制返回的结果,但需要与限制一起使用。
{
"limit": 10,
"offset": 5
}
这将执行 SELECT * FROM table LIMIT 10 OFFSET 5
。
计数
您可以通过添加计数键来获取记录的计数,而不是具体的记录
{
"count": true
}
这将执行 SELECT count(*) FROM table
。
软删除
默认情况下,软删除的记录会被排除在搜索之外。这可以通过 soft_deleted
来覆盖。
{
"soft_deleted": true
}
没有关系
如果您只想找到没有指定关系的条目,可以使用 doesnt_have_relations
键来做到这一点。
{
"doesnt_have_relations": "containers"
}
如果您想指定多个关系,可以按照以下方式操作
{
"doesnt_have_relations": ["containers", "addresses"]
}
顶级逻辑运算符
此外,还可以通过顶级逻辑运算符分组搜索子句。
可用运算符
&&
AND||
OR
不使用顶级运算符将假定 AND 运算符。
示例
这些运算符接受单个对象,或对象的数组,有一些值得注意的差异。单个对象将在给定的属性上应用运算符
{
"search": {
"&&": {
"id": "=1",
"name": "=foo"
}
}
}
结果为 id=1 AND name=foo
。而对象数组将在数组对象之间应用运算符,而不是在对象内部
{
"search": {
"||": [
{
"id": "=1",
"name": "=foo"
},
{
"id": "=2",
"name": "=bar"
}
]
}
}
结果为 (id=1 AND name=foo) OR (id=2 AND name=bar)
。这是故意为之的,默认运算符是 AND,因此它将在对象内部应用。
如果您想将内部属性更改为 OR,可以递归地进行操作
{
"search": {
"||": [
{
"||": {
"id": "=1",
"name": "=foo"
}
},
{
"id": "=2",
"name": "=bar"
}
]
}
}
结果为 (id=1 OR name=foo) OR (id=2 AND name=bar)
。
荒谬示例
由于逻辑是递归的,您可以像您想要的那样荒谬和深入,但在这个阶段,可能更明智的是重新审视您实际上想从生活和宇宙中得到什么
{
"search": {
"||": {
"&&": [
{
"||": [
{
"id": "=2||=3",
"name": "=foo"
},
{
"id": "=1",
"name": "=foo%&&=%bar"
}
]
},
{
"we": "=cool"
}
],
"love": "<3",
"recursion": "=rrr"
}
}
}
分解
- 步骤 1
{
"id": "=2||=3",
"name": "=foo"
},
结果:(id=2 OR id=3) AND name=foo
- 步骤 2
{
"id": "=1",
"name": "=foo%&&=%bar"
}
结果:id=1 AND (name LIKE foo% AND name LIKE %bar)
- 步骤 3(合并)
"||": [
{...},
{...}
]
结果:(步骤1) OR (步骤2)
- 步骤 4
we=cool
{
"we": "=cool"
}
- 步骤 5(合并)
"&&": [
{
"||": [...]
},
{
"we": "=cool"
}
],
结果:(步骤3) AND (步骤4)
- 步骤 6(最终合并)
"||": {
"&&": [...],
"love": "<3",
"recursion": "=rrr"
}
结果:(步骤5) OR love<3 OR recursion=rrr
最终的查询(用火消灭它)
(((id=2 OR id=3) AND name=foo) OR (id=1 AND (name LIKE foo% AND name LIKE %bar))) AND we=cool OR love<3 OR recursion=rrr
配置
除了标准的查询字符串搜索之外,还可以提供额外的包配置。
通过运行 php artisan vendor:publish --provider="Asseco\JsonQueryBuilder\JsonQueryServiceProvider"
来发布配置。
配置文件中的所有键都详细说明了每个键的上方。
包扩展
配置发布后,您将看到几个可以扩展自定义代码的键。
- 请求参数在
request_parameters
配置键下注册。您可以通过添加自己的自定义参数来扩展此功能。为了使其工作,需要扩展Asseco\JsonQueryBuilder\RequestParameters\AbstractParameter
。 - 运算符在
operators
配置键下注册。可以通过添加一个扩展Asseco\JsonQueryBuilder\SearchCallbacks\AbstractCallback
的类来扩展这些。 - 类型在
types
配置键下注册。可以通过添加一个扩展Asseco\JsonQueryBuilder\Types\AbstractType
的类来扩展这些。