API 平台:过滤自定义数据提供者

Dou*_*oug 7 php symfony swagger api-platform.com

我目前在 API 平台中使用外部 API 源 (Stripe) 时尝试过滤结果时遇到问题。

我需要做的是返回指定客户的订阅列表。因此,访问http://localhost/api/subscriptions?customer=123foo将返回与该客户匹配的所有记录。

现在,下面的代码由于 ORM\Filter 而引发错误,并且没有它也能正常工作,因为实际过滤是在 Stripes API 上执行的,而不是由我执行,但是,我真的希望 Swagger-API GUI 具有过滤器盒子。

简而言之,当使用外部数据源时,如何让实体中的注释显示 Swagger UI 中的可搜索字段。

我拥有的是如下实体(出于示例目的而简化):

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use Symfony\Component\Serializer\Annotation\Groups;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Annotation\ApiFilter;

/**
 * Subscriptions allow you to charge a customer on a recurring basis. A subscription ties a customer to a particular plan you've created.
 * @ApiResource()
 * @ApiFilter(SearchFilter::class, properties={"customer": "exact"})
 * @package App\Entity
 */
class Subscription
{
     /**
     * Unique identifier for the object.
     * @ApiProperty(identifier=true)
     * @var string | null
     */
    protected $id; 

    /**
     * ID of the customer who owns the subscription.
     * @var string | null
     */
    protected $customer;

    // Plus a bunch more properties and their Getters & Setters
}
Run Code Online (Sandbox Code Playgroud)

和 SubscriptionCollectionDataProvider:

<?php

namespace App\DataProvider;

use App\Entity\Subscription;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Controller\BaseController;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Class SubscriptionCollectionDataProvider
 * @package App\DataProvider
 * @author dhayward
 */
final class SubscriptionCollectionDataProvider extends BaseController implements CollectionDataProviderInterface, RestrictedDataProviderInterface
{

    protected $requestStack;

    /**
     * SubscriptionCollectionDataProvider constructor.
     * @param RequestStack $requestStack
     */
    public function __construct(RequestStack $requestStack)
    {
        $this->request = $requestStack->getCurrentRequest();
    }

    /**
     * @param string $resourceClass
     * @param string|null $operationName
     * @param array $context
     * @return bool
     */
    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
    {
        return Subscription::class === $resourceClass;
    }

    /**
     * @param string $resourceClass
     * @param string|null $operationName
     * @return \Generator
     * @throws \Stripe\Error\Api
     */
    public function getCollection(string $resourceClass, string $operationName = null): \Generator
    {
        $customer = $this->request->get("customer");

        $data = \Stripe\Subscription::all(["customer" => $customer]);

        foreach($data['data'] as $subscriptionObject){
            $this->serializer()->deserialize(json_encode($subscriptionObject), Subscription::class, 'json', array('object_to_populate' => $subscription = new Subscription()));
            yield $subscription;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

错误结果,这大概是因为我使用的是没有任何 ORM 设置的 ORM/过滤器:

在 null 上调用成员函数 getClassMetadata()

任何指针将不胜感激。

Dou*_*oug 6

所以我终于设法解决了。它是那样简单创造我自己的版本SearchFilter,实施ApiPlatform\Core\Api\FilterInterface

<?php

namespace App\Filter;

use ApiPlatform\Core\Api\FilterInterface;

/**
 * Class SearchFilter
 * @package App\Filter
 */
class SearchFilter implements FilterInterface
{
    /**
     * @var string Exact matching
     */
    const STRATEGY_EXACT = 'exact';

    /**
     * @var string The value must be contained in the field
     */
    const STRATEGY_PARTIAL = 'partial';

    /**
     * @var string Finds fields that are starting with the value
     */
    const STRATEGY_START = 'start';

    /**
     * @var string Finds fields that are ending with the value
     */
    const STRATEGY_END = 'end';

    /**
     * @var string Finds fields that are starting with the word
     */
    const STRATEGY_WORD_START = 'word_start';

    protected $properties;

    /**
     * SearchFilter constructor.
     * @param array|null $properties
     */
    public function __construct(array $properties = null)
    {
        $this->properties = $properties;
    }

    /**
     * {@inheritdoc}
     */
    public function getDescription(string $resourceClass): array
    {
        $description = [];

        $properties = $this->properties;

        foreach ($properties as $property => $strategy) {

                $filterParameterNames = [
                    $property,
                    $property.'[]',
                ];

                foreach ($filterParameterNames as $filterParameterName) {
                    $description[$filterParameterName] = [
                        'property' => $property,
                        'type' => 'string',
                        'required' => false,
                        'strategy' => self::STRATEGY_EXACT,
                        'is_collection' => '[]' === substr($filterParameterName, -2),
                    ];
                }
            }

        return $description;
    }

}
Run Code Online (Sandbox Code Playgroud)