asseco-voice/laravel-json-query-builder

Laravel JSON 查询构建器

v2.2.5 2024-09-19 13:06 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 查询。仅在值的开始、结束或两端工作(即 %valuevalue%%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 的类来扩展这些。