api-skeletons / doctrine-orm-querybuilder-filter
使用filter[fieldName|operator]=value对QueryBuilder进行筛选
Requires
- php: ~7.4||^8.0
- doctrine/orm: ^2.8
Requires (Dev)
- doctrine/coding-standard: ^9.0
- doctrine/dbal: ^3.1.1
- php-parallel-lint/php-parallel-lint: ^1.3
- phpunit/phpunit: ^9
- symfony/cache: ^5.3
- vimeo/psalm: ^4.14
README
根据请求参数对QueryBuilder应用筛选器。支持使用连接进行深度查询。此存储库旨在将查询参数应用于筛选实体数据。
安装
运行以下命令使用Composer安装此库
composer require api-skeletons/doctrine-orm-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();
在将QueryBuilder返回并应用过滤器之后,您可以在使用它来获取结果之前对其进行修改。
现实世界Laravel示例
在这个示例中,通过控制器操作,使用HAL返回了来自Entity\Style
实体的数据,以分页形式响应。
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非常简单,并且有许多配置选项。首先创建Applicator,然后运行配置函数
$applicator = new Applicator($entityManager, Entity\Style::class);
enableAssociations()
此配置方法通过使用Doctrine元数据,通过现有的连接遍历ORM到目标实体,以启用深度过滤。这只能在具有完整元数据并且实体之间在元数据中定义了正确关联的Doctrine安装中实现。
removeOperator(string|array)
如果您想禁用任何运算符,可以将其删除。一个好的例子是like
运算符,它可能会导致查询成本高昂。
setEntityAlias(string)
创建过滤器时默认使用的别名是entity
。您可能希望更改它,以便在QueryBuilder返回后知道别名,并可以向其添加额外的参数。
setFieldAliases(array)
如果您想使过滤器将字段别名为ORM字段名(例如在hydrators中使用命名策略)而不是使用ORM字段名,您可以传递一个包含[alias => field]
值的数组,以便进行调整映射。
setFilterableFields(array)
默认情况下,目标实体上的所有字段都可以进行过滤。如果您想限制用户可以创建过滤器的字段,则可以将这些字段名称传递到这个数组中。
getEntityAliasMap()
此方法用于对目标实体进行后处理。当用户使用enableAssociations()
进行深度过滤时,会为查询中连接到原始实体的每个实体创建别名。此方法返回一个包含[alias => entityClass]
的数组,表示QueryBuilder中连接的所有实体。