I'm currently experiencing issues when trying to filter my results when using an external API source (Stripe) in API-Platform.
What I need to be able to do, is return a list of subscriptions for a specified customer. So going to http://localhost/api/subscriptions?customer=123foo would return all records matched to this customer.
Now, the code below is throwing an error because of the ORM\Filter and would be functional without it, as the actual filtering is performed on Stripes API, not by me, BUT, I really want the Swagger-API GUI to have the filter box.
In short, how do I get the Annotations in my Entity to display, searchable fields within the Swagger UI when using an external data source.
What I have is an Entity as below (simplified for example purposes):
<?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
}
And the 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;
}
}
}
Error result, which is presumably because I'm using an ORM/Filter without any ORM setup:
Call to a member function getClassMetadata() on null
Any pointers would be greatly appreciated.
So I finally managed to work it out. It was as simple as creating my own version of the SearchFilter
, implementing 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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With