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 - 任何值都接受;将检查字段是否为空
- 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)
在QueryBuilder中为目标实体创建过滤器时使用的默认别名是entity
。您可能想更改它,以便在QueryBuilder返回后知道别名,并可以向其中添加更多参数。
setFieldAliases(array)
如果您想使过滤器将字段别名为而不是使用ORM字段名称(例如在hydrators中使用命名策略),您可以传递一个包含[alias => field]
值的数组,以便调整映射。
setFilterableFields(array)
默认情况下,可以对目标实体的所有字段进行筛选。如果您想限制用户可以创建筛选器的字段,请在此数组中传递那些字段名称。
getEntityAliasMap()
此方法用于目标实体的后处理。当用户使用enableAssociations()
进行深度筛选时,将为与原始实体查询连接的每个实体创建别名。此方法返回一个包含所有在QueryBuilder中连接的实体的[alias => entityClass]
的数组。