如何在 Api-Platform 中为虚拟属性引入过滤功能?

Posted

技术标签:

【中文标题】如何在 Api-Platform 中为虚拟属性引入过滤功能?【英文标题】:How can I introduce filtering functionality for a virtual property in Api-Platform? 【发布时间】:2021-09-30 11:55:53 【问题描述】:

我正在使用 Symfony 5 和 API 平台。

我的一个类有一个通过postLoad 监听器设置的属性。该属性仅在某些条件下设置(否则为NULL),我想允许REST API用户根据该属性是否为空或有值来过滤资源。

因为虚拟属性没有持久化到数据库中,我假设没有 Doctrine 过滤器,例如ExistsFilter 将在此属性上工作。

如何使用 Symfony 5 和 API 平台为虚拟属性创建过滤功能?

【问题讨论】:

我认为这并不容易。只是好奇:您为实体设置了什么样的属性,但没有持久化?为什么不能持久化? 你看过custom data providers吗? 感谢您的 cmets,是的,我正在考虑使用自定义数据提供程序,但我很难理解事物的分页方面。我是否需要创建一个自定义分页器来对(以某种方式过滤的)资源集进行分页并在 getCollection() 中返回这个分页器? 【参考方案1】:

您可以创建自己的custom ORM filters。

一个非常简单的例子来展示它是如何完成的:

假设一个类:

Foo 

    public int $weight;

    public function isHeavy(): bool 
        return $this->weight > 40;
    

由于heavy 是“虚拟”属性,您无法直接对其进行过滤。

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;

class HeavyFilter extends AbstractContextAwareFilter

    public function getDescription(string $resourceClass): array
    
        // I'm making the filter available only 
        if (Foo::class !== $resourceClass) 
            return [];
        

        
        if (!$this->properties) 
            return [];
        

        $description                      = [];
        $description['heavySearch']   =
            [
                'property' => 'heavy',
                'type'     => 'bool',
                'required' => false,
                'swagger'  => [
                    'description' => 'Search for heavy foos',
                    'name'        => 'Heavey Search',
                    'type'        => 'bool',
                ],
            ];

        return $description;
    

    protected function filterProperty(
        string $property,
        $value,
        QueryBuilder $queryBuilder,
        QueryNameGeneratorInterface $queryNameGenerator,
        string $resourceClass,
        string $operationName = null
    ): void 
        if ('heavySearch' !== $property) 
            return;
        

        if ($value === true) 
            $queryBuilder
                ->andWhere('o.weigth > 40');
        
        else 
            $queryBuilder
                ->andWhere('o.weight <= 40');
        
    

还没有实际测试过,只是在这里写的,但基本想法是正确的。您需要根据自己的情况进行调整,并且您将拥有一个自定义过滤器,该过滤器甚至可以在 Open Api 文档中找到。

【讨论】:

嗨 yivi,非常感谢您的评论。我的应用程序中的虚拟属性不是映射的 Doctrine 字段,因此 queryBuilder 返回一个 QueryException 表示该类没有这样的字段或关联。

以上是关于如何在 Api-Platform 中为虚拟属性引入过滤功能?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用来自 api-platform 的 JSON

如何使用 api-platform 构建 GraphQL API?

api-platform 可以将 200 返回到 POST 吗?

如何在textview中为字符串数组引入下一行?

api-platform 中的 API 版本控制

如何在 Visual Studio Code 中为 Python 设置虚拟环境?