metaclass-nl / filter-bundle
API Platform 和 Filter Logic 的过滤组件
Requires
- php: >=7.2.5
- api-platform/core: ^2.7 || ^3.0
- doctrine/orm: ^2.14 || ^3.0
- symfony/http-kernel: ^4.4 || ^5.1 || ^6.0 || ^7.0
This package is auto-updated.
Last update: 2024-08-25 19:01:48 UTC
README
根据客户端请求将 API Platform ORM 过滤器结合 AND、OR 和 NOT。
- 支持嵌套逻辑(如 SQL 中的括号)
- 支持同一属性的多项标准
- 如果未使用 "and"、"or" 或 "not" 作为查询参数,则现有请求将保持未修改的工作状态。
- 与 Api Platform 内置过滤器一起工作,除了具有 EXCLUDE_NULL 的 DateFilter。提供 DateFilter 子类以纠正此问题。
- 出于安全原因,从版本 2.0 开始,未嵌套在 "and"、"or" 或 "not" 中的扩展和过滤器标准始终通过 AND 与 LogicFilter 添加的标准结合
添加
用法
一旦将 FilterLogic 类和服务配置安装到您的应用程序中,只需将其添加为最后一个 ApiFilter 注释。然后通过查询字符串中的过滤参数进行嵌套请求。
例如,如果您有
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact', 'price' => 'exact', 'description' => 'partial'])]
#[ApiFilter(FilterLogic::class)]
class Offer {
// ...
}
调用集合获取操作
/offers/?or[price]=10&or[description]=shirt
将返回所有价格正好为 10 或描述包含 "shirt" 一词的报价。
NOT 表达式与其他表达式通过复合逻辑(and、or)结合,它们嵌套在其中
/offers/?or[not][price]=10&or[not][description]=shirt
将返回(所有价格不是 10 的报价)或(描述不包含 "shirt" 一词的报价)。如果没有嵌套在复合逻辑中,则使用 AND
您可以像这样拥有嵌套逻辑和同一属性的多项标准
/offers/?and[price]=10&and[][description]=shirt&and[][description]=cotton
API 将返回所有价格正好为 10 且(描述包含 "shirt" 且包含 "cotton")的报价。
嵌套在 "and"、"or" 或 "not" 中的表达式始终通过 AND 与普通表达式结合。例如
/offers/?price=10¬[description]=shirt
将返回所有价格正好为 10 且描述不包含 "shirt" 一词的报价。
/offers/?price=10&or[][description]=shirt&or[][description]=cotton
将返回所有价格正好为 10 且(描述包含 "shirt" 或包含 "cotton")的报价。因此,这与相同
/offers/?and[price]=10&and[or][][description]=shirt&and[or][][description]=cotton
这可能有些反直觉,但这是必要的,因为查询构建器也可能包含来自扩展的表达式,这些表达式限制了出于安全原因的数据访问,如果它们通过 OR 结合,则可以通过客户端绕过。
如果您希望它们通过 OR 结合,请将它们移动到 "or" 中嵌套
/offers/?or[price]=10&or[][description]=shirt&or[][description]=cotton
然后 API 将返回所有价格正好为 10 或描述包含 "shirt" 或描述包含 "cotton" 的报价。
您可以通过类名称配置 classExp 来排除/包含过滤器
* @ApiFilter(FilterLogic::class, arguments={"classExp"="/ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\+/"})
将仅在逻辑上下文中应用 API Platform ORM 过滤器
安装
此版本适用于 Api Platform 3.0 和 2.7,其中 metadata_backward_compatibility_layer 设置为 false
composer require metaclass-nl/filter-bundle "^3.0"
然后,将组件添加到您的 api config/bundles.php
// (...) Metaclass\FilterBundle\MetaclassFilterBundle::class => ['all' => true], ];
嵌套属性解决方案
Api Platform 的内置过滤器通常生成 INNER JOIN。因此,与 OR 结合可能不会产生预期结果,对于嵌套在可空和多对多关联中的属性
作为解决方案,FilterLogic 可以将所有内部连接转换为左连接
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: ['title' => 'partial', 'keywords.word' => 'exact'])]
#[ApiFilter(FilterLogic::class, arguments: ['innerJoinsLeft' => true])]
class Article {
// ...
}
安全警告:如果您使用的任何扩展依赖于INNER JOIN仅选择包含NOT NULL值的行,请勿使用此选项!
如果您不喜欢FilterLogic对连接进行操作,您可以通过先添加一个左连接然后再删除它,使Api Platform内置的过滤器自行生成左连接。
#[ApiResource]
#[ApiFilter(AddFakeLeftJoin::class)]
#[ApiFilter(SearchFilter::class, properties: ['title' => 'partial', 'keywords.word' => 'exact'])]
#[ApiFilter(FilterLogic::class)]
#[ApiFilter(RemoveFakeLeftJoin::class)]
class Article {
// ...
}
安全警告:使用ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryBuilderHelper::addJoinOnce或ApiPlatform\Core\Bridge\Doctrine\Orm\PropertyHelperTrait::addJoinsForNestedProperty的扩展可能不是有意生成左连接而不是内连接。当然,这将是他们的错误,而不是您的错误,但最好还是预防潜在的漏洞,而不是要处理后果。
使用这些解决方案之一,以下将找到标题包含'pro'的文章,以及关键词包含单词'php'的文章。
/articles/?or[title]=pro&or[keywords.word]=php
如果没有解决方案,即使标题包含'pro',没有关键词的文章也不会被找到。
这两种解决方案都会改变ExistsFilter =false与嵌套属性的行为。通常,此过滤器仅查找至少引用一个嵌套属性包含NULL的实体的实体,但使用左连接它也会找到引用本身为空或NULL的实体。这会破坏向后兼容性。这可以通过扩展ExistsFilter来解决,但这个Bundle不包括这一点,因为在我看来,根据“存在”的语义,旧的行为不符合预期,因此除非明确说明为有意为之,否则应将其视为错误。
限制
通过OR和嵌套逻辑组合过滤器可能对您的数据库来说是一项更具挑战性的任务,并可能需要不同的索引。除了小型表之外,在部署之前建议进行性能测试和分析。
在我看来,Api Platform内置的过滤器在生成JOIN方面存在一个错误。因此,将它们与OR结合使用时,在跨越多对多和可空关联的嵌套属性中的属性上不会按预期工作。已提供解决方案,但它们会改变ExistsFilter =false的行为。
假设过滤器创建了语义完整的表达式,在QueryBuilder中添加的表达式通过::andWhere或::orWhere不依赖于彼此,因此即使通过Doctrine\ORM\Query\Expr\Andx或Doctrine\ORM\Query\Expr\Orx与其他表达式重新组合,也不会损害预期的逻辑。
如果过滤器或扩展使用QueryBuilder::where或::add,可能会失败。
建议检查所有自定义和第三方过滤器的代码,不要将使用QueryBuilder::where或::add的过滤器与FilterLogic或产生复杂且语义不完整的逻辑的过滤器组合。有关语义完整和不完整表达式的示例,请参阅DateFilterTest。
请注意,添加到现有过滤器(来自API Platform或第三方)的新功能可能会使它们创建语义不完整的表达式或使用::where或::add,从而在通过FilterLogic组合时产生意外的结果或错误的SQL。语义版本化仅要求新功能的小版本更新,因此根据此包的composer.json,composer将安装它们,除非您将那些包限制为已测试的次要版本。
致谢和许可证
版权所有(c)MetaClass,格罗宁根,2021。MetaClass在荷兰提供软件开发和支持,包括Symfony、API Platform、React.js和Next.js。