core icon indicating copy to clipboard operation
core copied to clipboard

Doctrine filter not working on itemOperations

Open 4d4ch4u32 opened this issue 2 years ago • 0 comments

API Platform version(s) affected: 2.6.8

Description

In a project which uses PostgreSQL and API-Platform, I need to filter all records by a locale string. A doctrine filter is my preferred choice to do so.

This is the filter:

class LocaleFilter extends SQLFilter
{
    public const LOCALE_FILTER_NAME = 'locale_filter';

    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
    {
        if (!$targetEntity->reflClass->implementsInterface(LocalizedEntityInterface::class)) {
            return '';
        }

        return $targetTableAlias . '.locale = ' . $this->getParameter('locale');
    }
}

The parameter locale will be set on each onKernelRequest event, the locale is the value of the header X-Locale:


    public function onKernelRequest(RequestEvent $event): void
    {
        $locale = $event->getRequest()->headers->get('X-Locale');

        $this->setFilterLocale($locale);
    }

    private function setFilterLocale(string $locale): void
    {
        if (!$this->entityManager->hasFilters()) {
            return;
        }

        $localeFilter = $this->entityManager->getFilters()->getFilter(LocaleFilter::LOCALE_FILTER_NAME);

        $localeFilter->setParameter('locale', $locale);
    }

Now, when I send a request to a collectionOperations endpoint, such as http://example.com/products with the X-Locale header value de_DE, the filter is working and I get a response which contains only the according data in de_DE. When I send a request with locale fr_FR, I get a response with data in fr_FR.

But, when I send a request with the same X-Locale header to a itemOperations endpoint like http://example.com/products/<a-existing-id> I'm getting the error message The parameter "locale" is not set which comes from doctrine.

After investigating that issue, I can say that it works when I override the default ItemDataProvider from API-platform:

<?php

namespace App\DataProvider;

[...]

class ItemDataProvider implements ItemDataProviderInterface
{
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly RequestStack $requestStack,
    ) {
    }

    public function getItem(string $resourceClass, $id, ?string $operationName = null, array $context = []): object
    {
        $locale = $this->requestStack->getMainRequest()->headers->get('X-Locale');

        if ($this->entityManager->hasFilters()) {
            $localeFilter = $this->entityManager->getFilters()->getFilter(LocaleFilter::LOCALE_FILTER_NAME);

            $localeFilter->setParameter('locale', $locale);
        }

        $query = $this->entityManager->getRepository($resourceClass)
            ->createQueryBuilder('x')
            ->where('x.publicId = :pubid')
            ->setParameter('pubid', $id)
            ->getQuery();

        return $query->getOneOrNullResult();
    }
}

But is still required to set the filter value again in "my own" ItemDataProvider. If I delete the first 7 lines of the method getItem of the ItemDataProvider, I get the same error from above.

That doesn't make sense like that, does it? It seems like Api-Platform overrides the Doctrine filters in the default ItemDataProvider and make them useless. Howewer, I didn't found the reason for that issue.

Overriding the ItemDataProvider is a working workaround, but I don't think it's a good one, since the cause is more likely a bug and that way some features of Api-Platform are no longer present in the whole project.

4d4ch4u32 avatar Aug 05 '22 10:08 4d4ch4u32