laminas-api-tools/api-tools-doctrine-querybuilder

Laminas API Tools Doctrine QueryBuilder模块

1.9.1 2021-06-11 16:42 UTC

README

Build Status

🇷🇺 俄罗斯公民

我们,Laminas的参与者,来自不同的国家。我们中许多人都有在俄罗斯和乌克兰的朋友、亲戚和同事。我们中的一些人出生在俄罗斯。我们中的一些人现在住在俄罗斯。我们中的一些祖父母在二战中与法西斯作战。在这里,没有人支持法西斯主义。

我们中的一位有乌克兰亲戚,她和儿子一起从家中逃出。火车因前方道路上的轰炸而延误。我们有朋友躲在防空洞里。我们在空袭后焦急地等待他们的消息,空袭无差别地轰炸医院、学校、幼儿园和房屋。我们不是从任何媒体上得到这个消息的。这是我们的亲身经历。

您足够信任我们使用我们的程序,我们请求您再次信任我们。我们需要您的帮助。走出家门,抗议这场无谓的战争。停止杀戮。说“不,战争!”

🇺🇸 致俄罗斯公民

我们Laminas来自世界各地。我们中许多人都有在俄罗斯和乌克兰的朋友、家人和同事。我们中的一些人出生在俄罗斯。我们中的一些人现在住在俄罗斯。我们中的一些祖父母在二战中与纳粹作战。在这里,没有人支持法西斯主义。

一位团队成员有一位乌克兰亲戚,她和儿子一起逃离了家园。火车因前方道路上的轰炸而延误。我们有朋友躲在防空洞里。我们在空袭后焦急地等待他们的消息,空袭无差别地轰炸医院、学校、幼儿园和房屋。我们不是从任何媒体上得到这个消息的。这是我们的亲身经历。

您足够信任我们使用我们的软件。我们请求您在这个问题上信任我们。我们需要您的帮助。走出家门,抗议这场不必要的战争。停止杀戮。说“停止战争!”

这个库提供了从数组参数中查询构建器指令。这个库旨在将HTTP请求中的过滤器应用于API,以提供流畅的过滤和排序方言。

哲学

鉴于开发者已识别A和B:A == B与过滤和排序实体数据的能力和愿望有关。

共享的Doctrine实体包含

id integer,
name string,
startAt datetime,
endAt datetime,

开发者A或B编写API。资源是一个单一的Doctrine实体,数据是通过Doctrine QueryBuilder $objectManager->createQueryBuilder()查询的。此模块向其他开发者提供与Doctrine查询构建器相同的过滤和排序能力,但通过请求参数访问,如同API作者一样。例如,startAt between('2015-01-09', '2015-01-11');name like ('%arlie')不是手滚API的常见过滤,也许如果没有此模块,API作者可能不会实现它。借助此模块,API开发者可以不费吹灰之力实现资源的复杂可查询性,从而保持A == B。

安装

此模块的安装使用composer。有关composer文档,请参阅getcomposer.org

$ composer require laminas-api-tools/api-tools-doctrine-querybuilder

安装完成后,将 Laminas\ApiTools\Doctrine\QueryBuilder 添加到 config/application.config.php 中的模块列表中。

laminas-component-installer

如果您使用 laminas-component-installer,该插件将为您将 api-tools-doctrine-querybuilder 作为模块安装。

配置模块

config/api-tools-doctrine-querybuilder.global.php.dist 复制到 config/autoload/api-tools-doctrine-querybuilder.global.php,并编辑.orm和odm的别名列表,使其默认启用您想要的。

与Laminas API Tools Doctrine一起使用

要启用所有过滤器,您可以覆盖 api-tools-doctrine 中的默认查询提供程序。将以下内容添加到您的 api-tools-doctrine-querybuilder.global.php 配置文件中,如果它们在 $_GET['filter']$_GET['order-by'] 请求中,则将应用过滤器和排序。这些 $_GET 键可以通过 api-tools-doctrine-querybuilder-options 进行自定义

'api-tools-doctrine-query-provider' => [
    'aliases' => [
        'default_orm' => \Laminas\ApiTools\Doctrine\QueryBuilder\Query\Provider\DefaultOrm::class,
        'default_odm' => \Laminas\ApiTools\Doctrine\QueryBuilder\Query\Provider\DefaultOdm::class,
    ],
    'factories' => [
        \Laminas\ApiTools\Doctrine\QueryBuilder\Query\Provider\DefaultOrm::class => \Laminas\ApiTools\Doctrine\QueryBuilder\Query\Provider\DefaultOrmFactory::class,
        \Laminas\ApiTools\Doctrine\QueryBuilder\Query\Provider\DefaultOdm::class => \Laminas\ApiTools\Doctrine\QueryBuilder\Query\Provider\DefaultOdmFactory::class,
    ],
],

使用

配置示例

'api-tools-doctrine-querybuilder-orderby-orm' => [
    'aliases' => [
        'field' => \Laminas\ApiTools\Doctrine\QueryBuilder\OrderBy\ORM\Field::class,
    ],
    'factories' => [
        \Laminas\ApiTools\Doctrine\QueryBuilder\OrderBy\ORM\Field::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
    ],
],
'api-tools-doctrine-querybuilder-filter-orm' => [
    'aliases' => [
        'eq' => \Laminas\ApiTools\Doctrine\QueryBuilder\Filter\ORM\Equals::class,
    ],
    'factories' => [
        \Laminas\ApiTools\Doctrine\QueryBuilder\Filter\ORM\Equals::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
    ],
],

请求示例

$_GET = [
    'filter' => [
        [
            'type'  => 'eq',
            'field' => 'name',
            'value' => 'Tom',
        ],
    ],
    'order-by' => [
        [
            'type'      => 'field',
            'field'     => 'startAt',
            'direction' => 'desc',
        ],
    ],
];

资源示例

$serviceLocator = $this->getApplication()->getServiceLocator();
$objectManager = $serviceLocator->get('doctrine.entitymanager.orm_default');

$filterManager = $serviceLocator->get('LaminasDoctrineQueryBuilderFilterManagerOrm');
$orderByManager = $serviceLocator->get('LaminasDoctrineQueryBuilderOrderByManagerOrm');

$queryBuilder = $objectManager->createQueryBuilder();
$queryBuilder->select('row')
    ->from($entity, 'row')
;

$metadata = $objectManager->getMetadataFactory()->getMetadataFor(ENTITY_NAME); // $e->getEntity() using doctrine resource event
$filterManager->filter($queryBuilder, $metadata, $_GET['filter']);
$orderByManager->orderBy($queryBuilder, $metadata, $_GET['order-by']);

$result = $queryBuilder->getQuery()->getResult();

过滤器

过滤器不是简单的键/值对。过滤器是一个无键的过滤器定义数组。每个过滤器定义是一个数组,数组值因过滤器类型而异。

每个过滤器定义至少需要有一个 'type'。类型引用配置键,例如 'eq'、'neq'、'between'。

每个过滤器定义至少需要有一个 'field'。这是目标实体上的字段名称。

每个过滤器定义可以指定 'where',值为 'and' 或 'or'。

通过 AndX 和 OrX 过滤器类型支持嵌入式逻辑,例如 and(x or y)。

构建HTTP GET查询

JavaScript示例

$(function () {
    $.ajax({
        url: "http://localhost:8081/api/db/entity/user_data",
        type: "GET",
        data: {
            'filter': [
                {
                    'field': 'cycle',
                    'where': 'or',
                    'type': 'between',
                    'from': '1',
                    'to': '100'
                },
                {
                    'field': 'cycle',
                    'where': 'or',
                    'type': 'gte',
                    'value': '1000'
                }
            ]
        },
        dataType: "json"
    });
});

查询关系

单值

您可以查询集合通过关系 - 只需提供关系名称作为 fieldName 和标识符作为 value

假设我们定义了2个实体,UserUserGroup...

/**
 * @Entity
 */
class User {
    /**
     * @ManyToOne(targetEntity="UserGroup")
     * @var UserGroup
     */
    protected $group;
}
/**
 * @Entity
 */
class UserGroup {}

通过查询以下过滤器找到所有属于UserGroup id #1 的用户

['type' => 'eq', 'field' => 'group', 'value' => '1']

集合值

要匹配具有在集合中实体 B 的实体 A,请使用 ismemberof。假设 UserUserGroup 有 ManyToMany(或 OneToMany)关联...

/**
 * @Entity
 */
class User {
    /**
     * @ManyToMany(targetEntity="UserGroup")
     * @var UserGroup[]|ArrayCollection
     */
    protected $groups;
}

通过查询以下过滤器找到所有属于UserGroup id #1 的用户

['type' => 'ismemberof', 'field' => 'groups', 'value' => '1']

日期字段格式

当日期字段涉及过滤器时,您可以使用 PHP 日期格式化选项指定日期格式。默认日期格式为 Y-m-d H:i:s 如果您有一个日期字段只是 Y-m-d,那么请将格式添加到过滤器中。有关完整的日期格式选项,请参阅 DateTime::createFromFormat

[
    'format' => 'Y-m-d',
    'value' => '2014-02-04',
]

连接实体和别名查询

有一个内置的 ORM 查询类型用于 Inner Join,因此对于每个过滤器类型都有一个可选的 alias。默认别名为 'row',它引用 REST 资源核心的实体。没有过滤器可以添加其他实体到返回数据中。也就是说,默认情况下,只有原始目标资源('row'),无论应用了什么过滤器或排序,都会返回。

默认情况下,Inner Join 不包含在 api-tools-doctrine-querybuilder.global.php.dist 中。

此示例通过在行实体上已定义的内部连接连接报告字段,然后对 r.id = 2 进行筛选。

    ['type' => 'innerjoin', 'field' => 'report', 'alias' => 'r'],
    ['type' => 'eq', 'alias' => 'r', 'field' => 'id', 'value' => '2']

您可以使用 parentAlias 从内部连接连接表

    ['type' => 'innerjoin', 'parentAlias' => 'r', 'field' => 'owner', 'alias' => 'o'],

默认情况下,在 api-tools-doctrine-querybuilder.global.php.dist 中注释了 Inner Join。

还有一个用于 LeftJoin 的 ORM 查询类型。此连接类型通常用于获取关系的空右侧。

默认情况下,在 api-tools-doctrine-querybuilder.global.php.dist 中注释了 LeftJoin。

    ['type' => 'leftjoin', 'field' => 'report', 'alias' => 'r'],
    ['type' => 'isnull', 'alias' => 'r', 'field' => 'id']

包含的过滤器类型

ORM 和 ODM

等于

['type' => 'eq', 'field' => 'fieldName', 'value' => 'matchValue']

不等于

['type' => 'neq', 'field' => 'fieldName', 'value' => 'matchValue']

小于

['type' => 'lt', 'field' => 'fieldName', 'value' => 'matchValue']

小于等于

['type' => 'lte', 'field' => 'fieldName', 'value' => 'matchValue']

大于

['type' => 'gt', 'field' => 'fieldName', 'value' => 'matchValue']

大于等于

['type' => 'gte', 'field' => 'fieldName', 'value' => 'matchValue']

为空

['type' => 'isnull', 'field' => 'fieldName']

不为空

['type' => 'isnotnull', 'field' => 'fieldName']

注意:In 和 NotIn 过滤器中的日期不按日期处理。建议您使用多个等于语句而不是这些过滤器来处理日期数据类型。

In

['type' => 'in', 'field' => 'fieldName', 'values' => [1, 2, 3]]

NotIn

['type' => 'notin', 'field' => 'fieldName', 'values' => [1, 2, 3]]

Between

['type' => 'between', 'field' => 'fieldName', 'from' => 'startValue', 'to' => 'endValue']

Like(使用%作为通配符)

['type' => 'like', 'field' => 'fieldName', 'value' => 'like%search']

ORM 仅用

Is Member Of

['type' => 'ismemberof', 'field' => 'fieldName', 'value' => 1]

AndX

在 AndX 查询中,conditions 是一个数组,包含这里描述的任何过滤类型。连接始终为 and,因此 conditions 中的 where 参数被忽略。AndX 过滤类型上的 where 参数不会被忽略。

[
    'type' => 'andx',
    'conditions' => [
        ['field' =>'name', 'type'=>'eq', 'value' => 'ArtistOne'],
        ['field' =>'name', 'type'=>'eq', 'value' => 'ArtistTwo'],
    ],
    'where' => 'and',
]

OrX

在 OrX 查询中,conditions 是一个数组,包含这里描述的任何过滤类型。连接始终为 or,因此 conditions 中的 where 参数被忽略。OrX 过滤类型上的 where 参数不会被忽略。

[
    'type' => 'orx',
    'conditions' => [
        ['field' =>'name', 'type'=>'eq', 'value' => 'ArtistOne'],
        ['field' =>'name', 'type'=>'eq', 'value' => 'ArtistTwo'],
    ],
    'where' => 'and',
]

ODM 仅用

Regex

['type' => 'regex', 'field' => 'fieldName', 'value' => '/.*search.*/i']

包含的 Order By 类型

字段

['type' => 'field', 'field' => 'fieldName', 'direction' => 'desc']

自定义 MappingTypes

如果您已配置自定义映射类型,您可以替换提供的 Laminas\ApiTools\Doctrine\QueryBuilder\Filter\TypeCastInterface 实现为您自己的实现。

例如,给定以下自定义类型转换实现

namespace My\Custom;

class TypeCaster implements \Laminas\ApiTools\Doctrine\QueryBuilder\Filter\TypeCastInterface
{
    public function typeCastField($metadata, $field, $value, $format = null, $doNotTypecastDatetime = false)
    {
        // implement your type casting logic
    }
}

然后您将提供一个用于您的实现的工厂,并将包别名 TypeCastInterface 映射到它

// config/autoload/api-tools-doctrine-querybuilder-global.php

use Laminas\ApiTools\Doctrine\QueryBuilder\Filter\TypeCastInterface;
use Laminas\ServiceManager\Factory\InvokableFactory;
use My\Custom\TypeCaster;

return [
    'service_manager => [
        'aliases' => [
            TypeCastInterface::class => TypeCaster::class,
        ],
        'factories' => [
            TypeCaster::class => InvokableFactory::class,
        ],
    ],
];