api-skeletons / doctrine-querybuilder-filter
使用 filter[fieldName|operator]=value 过滤 QueryBuilder
Requires
- php: ^8.0
- doctrine/orm: ^2.8
Requires (Dev)
- doctrine/coding-standard: ^9.0
- doctrine/dbal: ^3.1.1
- phpunit/phpunit: ^9
- symfony/cache: ^5.3
- vimeo/psalm: ^4.14
README
根据请求参数对 QueryBuilder 应用过滤器。支持使用连接进行深度查询。此仓库旨在将查询参数应用于筛选实体数据。
安装
运行以下命令使用 Composer 安装此库
composer require api-skeletons/doctrine-querybuilder-filter
快速入门
use ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator; $applicator = new Applicator($entityManager, Entity\User::class); $queryBuilder = $applicator($_REQUEST['filter']);
过滤器
创建过滤器的模式是 filter[fieldName|operator]=value
这种模式是一种简单的方法,可以定义利用 QueryBuilder 所有过滤功能的复杂查询。
以下 URL 将提供对用户 name
的 LIKE 过滤器
http://localhost/api/user?filter[name|like]=John
支持的运算符
- eq - 等于。如果没有指定运算符,则默认为此运算符
- neq - 不等于
- gt - 大于
- gte - 大于等于
- lt - 小于
- lte - 小于等于
- between - 在两个值之间,用逗号分隔,例如
filter[id|between]=1,5
- like - 模糊搜索,用通配符包裹值
- startswith - 带有右边通配符的
like
运算符 - endswith - 带有左边通配符的
like
运算符 - in - 要匹配的值列表,用逗号分隔,例如
filter[id|in]=1,2,3]
- notin - 与
in
运算符相反 - isnull - 任何值都接受;将检查字段是否为 null
- isnotnull - 与
isNull
运算符相反 - sort - 按字段对结果进行排序,可以是
asc
或desc
您可以使用任意数量的过滤器。过滤器作用于实体字段名和关联名。这允许您对关联进行筛选。例如,要按公司 ID 和正确的 Doctrine 元数据筛选用户列表,您可以按如下方式筛选
http://localhost/api/user?filter[company]=10
因此,即使没有名为公司的字段,也存在一个关联,并且可以通过此工具进行筛选。
对单个实体进行筛选
Applicator 的配置允许您启用关联筛选,但默认情况下是禁用的。因此,假设默认设置,这是对单个实体的复杂筛选
http://localhost/api/user?filter[company|neq]=15&filter[name]=John
对实体层次结构进行筛选
Applicator 的配置允许您启用关联筛选。这意味着您可以根据与当前实体关联的字段筛选当前实体,并且可以深入到您想要的程度。
在此示例中,我们将根据用户的公司名称检索用户数据
http://localhost/api/user?filter[company][name|eq]=AAA
在此示例中,我们将根据公司的公司类型和公司类型名称检索用户数据
http://localhost/api/user?filter[company][companyType][name|neq]=Consultant
在您过于担心此功能之前,请记住您可以在应用过滤器之后修改 QueryBuilder,因此如果您需要应用安全设置,这是支持的。
关于排序的说明
虽然排序在严格的定义上不是筛选,但在请求数据时它属于同一上下文。您可以根据多个字段进行排序,并且可以跨关联(如果启用)进行排序。排序是按照从左到右的顺序优先的。
使用
这是使用此库所需的最小配置。
use ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator; $applicator = (new Applicator($entityManager, Entity\User::class)); $queryBuilder = $applicator($_REQUEST['filter']);
这是一个配置应用器的示例,使用了所有可能的选项。
use ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator; $applicator = (new Applicator($entityManager, Entity\User::class)) ->enableRelationships() ->removeOperator('like') ->setEntityAlias('user') ->setFieldAliases(['firstName' => 'name']) ->setFilterableFields(['id', 'name']) ; $queryBuilder = $applicator($_REQUEST['filter']); $entityAliasMap = $applicator->getEntityAliasMap();
在将过滤器应用到查询构建器并返回后,您可以在使用它来获取结果之前按需修改它。
真实世界Laravel示例
在这个例子中,使用HAL在控制器操作中以分页响应返回了来自 Entity\Style
实体的数据。[HAL](https://github.com/API-Skeletons/laravel-hal "HAL")
use ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator; use Doctrine\ORM\Tools\Pagination\Paginator; use HAL; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; public function fetchAll(Request $request) { $filter = $request->query()['filter'] ?? []; if (! is_array($filter)) { $filter = []; ] $page = (int) $request->query()['page'] ?? 1; if ($page < 1) { $page = 1; } $applicator = (new Applicator(app('em'), Entity\Style::class)) ->enableRelationships(true); $queryBuilder = $applicator($filter); $paginator = new Paginator($queryBuilder); $paginator->getQuery() ->setFirstResult(25 * ($page - 1)) // Page is 0 indexed in query ->setMaxResults(25) ; $data = (new LengthAwarePaginator(iterator_to_array($paginator->getIterator()), $paginator->count(), 25)) ->appends($request->query()) ->withPath(route('api.style::fetchAll')) ; return HAL::paginate('style', $data)->toArray(); }
配置
使用应用器非常简单,有许多配置选项。首先创建应用器,然后运行配置函数。
$applicator = new Applicator($entityManager, Entity\Style::class);
enableAssociations()
此配置方法通过使用Doctrine元数据遍历ORM,通过现有连接到目标实体的连接来启用深度过滤。这仅在具有完整元数据和在元数据中定义的实体之间正确关联的Doctrine安装中才可行。
removeOperator(string|array)
如果您想禁用任何操作符,可以将其删除。一个好的例子是 like
操作符,它可能会导致昂贵的查询。
setEntityAlias(string)
在创建针对目标实体的过滤器时,默认使用的别名是 entity
。您可能希望更改此别名,以便在查询构建器返回后知道别名,并向它添加额外的参数。
setFieldAliases(array)
如果您想使过滤器使用别名而不是使用ORM字段名称(例如在 hydrators 中使用命名策略),您可以传递一个 [alias => field]
的值数组,以便调整映射。
setFilterableFields(array)
默认情况下,可以过滤目标实体上的所有字段。如果您想限制用户可以创建过滤器的字段,请将那些字段名称传递到该数组中。
getEntityAliasMap()
此方法用于目标实体的后处理。当用户使用 enableAssociations()
进行深度过滤时,将为每个连接到原始实体查询的实体创建别名。此方法返回一个包含 [alias => entityClass]
的数组,其中包含在查询构建器中连接的所有实体。