Closed remoteclient closed 6 years ago
Same issue here. Collection operations, GET and POST, works fine. Item operation PUT works fine but GET operation gives default language content instead localized content.
@remoteclient, have you been able to solve it?
@ACC-Txomin The best way I think would be to hook into the query. But I don't know how to do it right now. In the meantime one could set up a custom action to retrieve the translation and expose it.
@remoteclient Thanks for your fast reply.
I think I'm going to do a custom action.
I hope eventually this bug get solved.
Today I can come up with a solution. The answer lies not in a custom action but in a custom extension described here. You have to set hints for the query described in the documentation of gedmo translatable. This is my implementation:
<?php
namespace MWS\NutritionCalculatorBundle\Doctrine;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
use Gedmo\Translatable\TranslatableListener;
use MWS\NutritionCalculatorBundle\Entity\DogBreed;
use Symfony\Component\HttpFoundation\RequestStack;
final class DogBreedExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
/**
* @var RequestStack
*/
private $requestStack;
/**
* DogBreedExtension constructor.
* @param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
$this->addHints($queryBuilder, $resourceClass);
}
/**
* @param QueryBuilder $queryBuilder
* @param QueryNameGeneratorInterface $queryNameGenerator
* @param string $resourceClass
* @param array $identifiers
* @param string|null $operationName
* @param array $context
*/
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = [])
{
$this->addHints($queryBuilder, $resourceClass);
}
/**
*
* @param QueryBuilder $queryBuilder
* @param string $resourceClass
*/
private function addHints(QueryBuilder $queryBuilder, string $resourceClass)
{
if (DogBreed::class === $resourceClass) {
$queryBuilder = $queryBuilder->getQuery();
$queryBuilder->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
// locale
$queryBuilder->setHint(
TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$this->requestStack->getCurrentRequest()->getLocale() // take locale from session or request etc.
);
// fallback
$queryBuilder->setHint(
TranslatableListener::HINT_FALLBACK,
1 // fallback to default values in case if record is not translated
);
$queryBuilder->getResult();
}
}
}
Adding hints to the collection request is not necessary, but it gives you one database query for the list view instead of multiple queries depending on your pagination size.
@ACC-Txomin please leave a message if this works for you too. Then I can close this issue.
@remoteclient works like a charm!! Thank you very much.
It is worth an entry in the StofDoctrineExtensionsBundle's or API Platform docs.
@dunglas Thank you. I wanted to ask you tomorrow on slack if I should make a doc entry on apip. Under which section should an update been posted? Directly unter extensions, or should there be something new?
I also changed the listener a bit and removed the session as there is none in stateless connections. should this also be posted? Then a new section "StofDoctrineExtensionsBundle Integration would be good.
Would be good enough if we had an entry in apiplatform docs. I'd put it under https://api-platform.com/docs/core/extensions
There needs some more to be done. As I realized now, filtering on translated entities works not like it is expected. Example: My default language is en_US. The second language I use is de_DE. Now I set the language to de_DE in the header to retrieve only the german translations. What the filter is doing right now is that it filters the english collection and then it gives the german translations for it back.
@remoteclient i also tried to add StofDoctrineExtensionsBundle. But how do you add translated records?
First i post this:
{
"id": 1,
"name": "Test en_US",
}
Then i set the request locale to de_DE and POST this, but it does not work.
{
"id": 1,
"name": "Test de_DE",
}
Have you an example how this should work?
@do-web StofDoctrineExtensionsBundle uses DoctrineExtensions. The way described in the documentation does it on a step by step basis. First POST an entity in default language. For translation PUT to that entity the new translated entity and set translatable locale to the one the fits.
Thanks, i have no my own translatable extension for api-platform. :)
Today I can come up with a solution. The answer lies not in a custom action but in a custom extension described here. You have to set hints for the query described in the documentation of gedmo translatable. This is my implementation:
<?php namespace MWS\NutritionCalculatorBundle\Doctrine; use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface; use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface; use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Gedmo\Translatable\Query\TreeWalker\TranslationWalker; use Gedmo\Translatable\TranslatableListener; use MWS\NutritionCalculatorBundle\Entity\DogBreed; use Symfony\Component\HttpFoundation\RequestStack; final class DogBreedExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface { /** * @var RequestStack */ private $requestStack; /** * DogBreedExtension constructor. * @param RequestStack $requestStack */ public function __construct(RequestStack $requestStack) { $this->requestStack = $requestStack; } public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null) { $this->addHints($queryBuilder, $resourceClass); } /** * @param QueryBuilder $queryBuilder * @param QueryNameGeneratorInterface $queryNameGenerator * @param string $resourceClass * @param array $identifiers * @param string|null $operationName * @param array $context */ public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []) { $this->addHints($queryBuilder, $resourceClass); } /** * * @param QueryBuilder $queryBuilder * @param string $resourceClass */ private function addHints(QueryBuilder $queryBuilder, string $resourceClass) { if (DogBreed::class === $resourceClass) { $queryBuilder = $queryBuilder->getQuery()->useQueryCache(false); // <<======== need this for cache pb $queryBuilder->setHint( Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' ); // locale $queryBuilder->setHint( TranslatableListener::HINT_TRANSLATABLE_LOCALE, $this->requestStack->getCurrentRequest()->getLocale() // take locale from session or request etc. ); // fallback $queryBuilder->setHint( TranslatableListener::HINT_FALLBACK, 1 // fallback to default values in case if record is not translated ); $queryBuilder->getResult(); } } }
Adding hints to the collection request is not necessary, but it gives you one database query for the list view instead of multiple queries depending on your pagination size.
thank you very much , this worked for me
Thanks, @remoteclient, furthermore I had a caching problem with the query in production environment, and I could solve it using the following statement:
$queryBuilder = $queryBuilder->getQuery()->useQueryCache(false);
Thanks @remoteclient. But sadly it is not working w/ nested properties, i.e. w/ serialization groups.
Thanks, @remoteclient, furthermore I had a caching problem with the query in production environment, and I could solve it using the following statement:
$queryBuilder = $queryBuilder->getQuery()->useQueryCache(false);
thanks bro yes i needed this because of cache problems
Thanks @remoteclient. But sadly it is not working w/ nested properties, i.e. w/ serialization groups.
Indeed, translations don't work on nested properties.
However, I may have found a solution (maybe wrong, but still):
<?php
namespace App\Extension;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryResultItemExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query;
use Symfony\Component\HttpFoundation\RequestStack;
use Gedmo\Translatable\TranslatableListener;
use Gedmo\Translatable\Translatable;
class ResultItemExtension implements QueryResultItemExtensionInterface
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function getResult(QueryBuilder $queryBuilder)
{
$query = $queryBuilder->getQuery();
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
// locale
$query->setHint(
TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$this->requestStack->getCurrentRequest()->getLocale() // take locale from session or request etc.
);
// fallback
$query->setHint(
TranslatableListener::HINT_FALLBACK,
1 // fallback to default values in case if record is not translated
);
return $query->getSingleResult();
}
public function supportsResult(string $resourceClass, string $operationName = null): bool
{
$reflection = new \ReflectionClass($resourceClass);
return $reflection->implementsInterface(Translatable::class);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?string $operationName = null, array $context = [])
{
return;
}
}
I was implementing the getResult method from QueryResultItemExtensionInterface.
If you want to fetch a collection of entities in a single request, just set a negative priority in service.yaml (yeah, I know it's discouraged in the official documentation, but I'm the bad guy):
App\Extension\ResultItemExtension:
arguments:
- '@request_stack'
tags:
- { name: api_platform.doctrine.orm.query_extension.item, priority: -9 }
Should work. The code was run on symfony 5.4 lts, api platform - v2.6.8
Thanks @remoteclient. But sadly it is not working w/ nested properties, i.e. w/ serialization groups.
I managed to make it work by setting fetch: 'EXTRA_LAZY'
to the attribute.
Hello I'm facing same issue with pagination enabled with ApiPlatform 2.7.
<?php
namespace App\Doctrine\Extension;
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
use Gedmo\Translatable\Translatable;
use Gedmo\Translatable\TranslatableListener;
use ReflectionClass;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* class App\Doctrine\Extension
*/
class TranslatableResultExtension implements QueryCollectionExtensionInterface
{
/**
* @var RequestStack
*/
private $requestStack;
/**
* TranslatableResultExtension constructor.
* @param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function supports(string $resourceClass): bool
{
$reflection = new ReflectionClass($resourceClass);
return $reflection->implementsInterface(Translatable::class);
}
/**
* {@inheritdoc}
*/
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
{
if (!$this->supports($resourceClass)) {
return;
}
$query = $queryBuilder->getQuery();
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
TranslationWalker::class
);
// locale
$query->setHint(
TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$this->requestStack->getCurrentRequest()->getLocale() // take locale from session or request etc.
);
// fallback
$query->setHint(
TranslatableListener::HINT_FALLBACK,
1 // fallback to default values in case if record is not translated
);
}
}
Do you have any idea of why it is not working ? Thanks in advance
You haven't wrote what the issue is, but I guess that your collection doesn't get paginated. Have a look at the docs and see how to inject the extensions.
Hello I'm facing same issue with pagination enabled with ApiPlatform 2.7.
<?php namespace App\Doctrine\Extension; use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface; use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; use ApiPlatform\Metadata\Operation; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Gedmo\Translatable\Query\TreeWalker\TranslationWalker; use Gedmo\Translatable\Translatable; use Gedmo\Translatable\TranslatableListener; use ReflectionClass; use Symfony\Component\HttpFoundation\RequestStack; /** * class App\Doctrine\Extension */ class TranslatableResultExtension implements QueryCollectionExtensionInterface { /** * @var RequestStack */ private $requestStack; /** * TranslatableResultExtension constructor. * @param RequestStack $requestStack */ public function __construct(RequestStack $requestStack) { $this->requestStack = $requestStack; } public function supports(string $resourceClass): bool { $reflection = new ReflectionClass($resourceClass); return $reflection->implementsInterface(Translatable::class); } /** * {@inheritdoc} */ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void { if (!$this->supports($resourceClass)) { return; } $query = $queryBuilder->getQuery(); $query->setHint( Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class ); // locale $query->setHint( TranslatableListener::HINT_TRANSLATABLE_LOCALE, $this->requestStack->getCurrentRequest()->getLocale() // take locale from session or request etc. ); // fallback $query->setHint( TranslatableListener::HINT_FALLBACK, 1 // fallback to default values in case if record is not translated ); } }
Do you have any idea of why it is not working ? Thanks in advance
Thanks @remoteclient
My issue is that $query->setHint
is not applying to$queryBuilder
I guess here https://github.com/api-platform/core/blob/main/src/Doctrine/Orm/State/CollectionProvider.php#L54
I have a custom Provider for that. You can make the support function more generic:
<?php
/**
* Class TranslatableDataProvider.
*
* @author Martin Walther <martin@myweb.solutions>
*
* (c) MyWebSolutions
*/
declare(strict_types=1);
namespace MWS\NutritionCalculatorBundle\DataProvider;
use ApiPlatform\Core\Bridge\Doctrine\Orm\AbstractPaginator;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\PaginationExtension;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryChecker;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrineOrmPaginator;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Gedmo\Translatable\TranslatableListener;
use MWS\NutritionCalculatorBundle\Entity\DogBreed;
use MWS\NutritionCalculatorBundle\Entity\NutritionDatabase;
use MWS\NutritionCalculatorBundle\Entity\NutritionDatabaseMetadata;
use MWS\NutritionCalculatorBundle\Entity\NutritionDatabaseMetadataClassification;
use MWS\NutritionCalculatorBundle\Entity\NutritionDatabaseMetadataDimension;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Traversable;
class TranslatableCollectionDataProvider implements CollectionDataProviderInterface, RestrictedDataProviderInterface
{
private $managerRegistry;
private $requestStack;
private $resourceMetadataFactory;
private $enabled;
private $clientEnabled;
private $clientItemsPerPage;
private $itemsPerPage;
private $pageParameterName;
private $enabledParameterName;
private $itemsPerPageParameterName;
private $maximumItemPerPage;
private $partial;
private $clientPartial;
private $partialParameterName;
private $collectionExtensions;
/**
* @param QueryCollectionExtensionInterface[] $collectionExtensions
*/
public function __construct(ManagerRegistry $managerRegistry, RequestStack $requestStack, ResourceMetadataFactoryInterface $resourceMetadataFactory, bool $enabled = true, bool $clientEnabled = false, bool $clientItemsPerPage = false, int $itemsPerPage = 30, string $pageParameterName = 'page', string $enabledParameterName = 'pagination', string $itemsPerPageParameterName = 'itemsPerPage', int $maximumItemPerPage = null, bool $partial = false, bool $clientPartial = false, string $partialParameterName = 'partial', array $collectionExtensions = [])
{
$this->managerRegistry = $managerRegistry;
$this->requestStack = $requestStack;
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->enabled = $enabled;
$this->clientEnabled = $clientEnabled;
$this->clientItemsPerPage = $clientItemsPerPage;
$this->itemsPerPage = $itemsPerPage;
$this->pageParameterName = $pageParameterName;
$this->enabledParameterName = $enabledParameterName;
$this->itemsPerPageParameterName = $itemsPerPageParameterName;
$this->maximumItemPerPage = $maximumItemPerPage;
$this->partial = $partial;
$this->clientPartial = $clientPartial;
$this->partialParameterName = $partialParameterName;
$this->collectionExtensions = $collectionExtensions;
}
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return DogBreed::class === $resourceClass
|| NutritionDatabase::class === $resourceClass
|| NutritionDatabaseMetadata::class === $resourceClass
|| NutritionDatabaseMetadataClassification::class === $resourceClass
|| NutritionDatabaseMetadataDimension::class === $resourceClass;
}
/**
* Retrieves a collection.
*
* @return array|AbstractPaginator|Traversable
*
* @throws ResourceClassNotSupportedException
* @throws ResourceClassNotFoundException
*/
public function getCollection(string $resourceClass, string $operationName = null, array $context = [])
{
/** @var ObjectManager $manager */
$manager = $this->managerRegistry->getManagerForClass($resourceClass);
if (null === $manager) {
throw new ResourceClassNotSupportedException();
}
$repository = $manager->getRepository($resourceClass);
if (!method_exists($repository, 'createQueryBuilder')) {
throw new RuntimeException('The repository class must have a "createQueryBuilder" method.');
}
$queryBuilder = $repository->createQueryBuilder('o');
$queryNameGenerator = new QueryNameGenerator();
foreach ($this->collectionExtensions as $extension) {
/* @var PaginationExtension $extension */
$extension->applyToCollection($queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context);
if ($extension instanceof PaginationExtension && $extension->supportsResult($resourceClass, $operationName, $context)) {
/** @var QueryBuilder $queryBuilder */
$query = $queryBuilder->getQuery()->useQueryCache(true)->enableResultCache();
$query = $this->addTranslatableQueryHints($query);
// Do the pagination again unless we can find a good solution here to reuse the code :|
$doctrineOrmPaginator = new DoctrineOrmPaginator($query, $this->useFetchJoinCollection($queryBuilder));
$doctrineOrmPaginator->setUseOutputWalkers($this->useOutputWalkers($queryBuilder));
$resourceMetadata = null === $resourceClass ? null : $this->resourceMetadataFactory->create($resourceClass);
if ($this->isPartialPaginationEnabled($this->requestStack->getCurrentRequest(), $resourceMetadata, $operationName)) {
return new class($doctrineOrmPaginator) extends AbstractPaginator {
};
}
return new Paginator($doctrineOrmPaginator);
}
}
/** @var QueryBuilder $queryBuilder */
$query = $queryBuilder->getQuery()->useQueryCache(true)->enableResultCache();
$query = $this->addTranslatableQueryHints($query);
return $query->getResult();
}
/**
* @param $query
*
* @return mixed
*/
private function addTranslatableQueryHints(Query $query)
{
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
// locale
$query->setHint(
TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$this->requestStack->getCurrentRequest()->getLocale() // take locale from session or request etc.
);
// fallback
$query->setHint(
TranslatableListener::HINT_FALLBACK,
0 // fallback to default values in case if record is not translated
);
$query->setHint(TranslatableListener::HINT_INNER_JOIN, true);
// $query->setHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION);
// $query->setHint(Query::HINT_REFRESH, true);
return $query;
}
/**
* Determines whether the Paginator should fetch join collections, if the root entity uses composite identifiers it should not.
*
* @see https://github.com/doctrine/doctrine2/issues/2910
*/
private function useFetchJoinCollection(QueryBuilder $queryBuilder): bool
{
return !QueryChecker::hasRootEntityWithCompositeIdentifier($queryBuilder, $this->managerRegistry);
}
/**
* Determines whether output walkers should be used.
*/
private function useOutputWalkers(QueryBuilder $queryBuilder): bool
{
/*
* "Cannot count query that uses a HAVING clause. Use the output walkers for pagination"
*
* @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php#L50
*/
if (QueryChecker::hasHavingClause($queryBuilder)) {
return true;
}
/*
* "Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator."
*
* @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php#L87
*/
if (QueryChecker::hasRootEntityWithForeignKeyIdentifier($queryBuilder, $this->managerRegistry)) {
return true;
}
/*
* "Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers."
*
* @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php#L149
*/
if (
QueryChecker::hasMaxResults($queryBuilder) &&
QueryChecker::hasOrderByOnToManyJoin($queryBuilder, $this->managerRegistry)
) {
return true;
}
/*
* When using composite identifiers pagination will need Output walkers
*/
if (QueryChecker::hasRootEntityWithCompositeIdentifier($queryBuilder, $this->managerRegistry)) {
return true;
}
// Disable output walkers by default (performance)
return false;
}
private function isPartialPaginationEnabled(Request $request = null, ResourceMetadata $resourceMetadata = null, string $operationName = null): bool
{
$enabled = $this->partial;
$clientEnabled = $this->clientPartial;
if ($resourceMetadata) {
$enabled = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_partial', $enabled, true);
if ($request) {
$clientEnabled = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_client_partial', $clientEnabled, true);
}
}
if ($clientEnabled && $request) {
$enabled = filter_var($this->getPaginationParameter($request, $this->partialParameterName, $enabled), FILTER_VALIDATE_BOOLEAN);
}
return $enabled;
}
private function getPaginationParameter(Request $request, string $parameterName, $default = null)
{
if (null !== $paginationAttribute = $request->attributes->get('_api_pagination')) {
return \array_key_exists($parameterName, $paginationAttribute) ? $paginationAttribute[$parameterName] : $default;
}
return $request->query->get($parameterName, $default);
}
}
@remoteclient How did you declare this provider in services.yaml
?
I use xml for configuration:
<services>
<service id="mws_nc.data_provider.translatable_collection_data_provider"
class="MWS\NutritionCalculatorBundle\DataProvider\TranslatableCollectionDataProvider"
>
<tag name="api_platform.collection_data_provider" priority="2"/>
<argument type="service" id="doctrine"/>
<argument type="service" id="request_stack"/>
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<argument>%api_platform.collection.pagination.enabled%</argument>
<argument>%api_platform.collection.pagination.client_enabled%</argument>
<argument>%api_platform.collection.pagination.client_items_per_page%</argument>
<argument>%api_platform.collection.pagination.items_per_page%</argument>
<argument>%api_platform.collection.pagination.page_parameter_name%</argument>
<argument>%api_platform.collection.pagination.enabled_parameter_name%</argument>
<argument>%api_platform.collection.pagination.items_per_page_parameter_name%</argument>
<argument>%api_platform.collection.pagination.maximum_items_per_page%</argument>
<argument>%api_platform.collection.pagination.partial%</argument>
<argument>%api_platform.collection.pagination.client_partial%</argument>
<argument>%api_platform.collection.pagination.partial_parameter_name%</argument>
<argument type="collection">
<argument type="service" id="api_platform.doctrine.orm.query_extension.eager_loading"/>
<argument type="service" id="api_platform.doctrine.orm.query_extension.filter"/>
<argument type="service" id="api_platform.doctrine.orm.query_extension.filter_eager_loading"/>
<argument type="service" id="api_platform.doctrine.orm.query_extension.order"/>
<argument type="service" id="api_platform.doctrine.orm.query_extension.pagination"/>
</argument>
</service>
I use xml for configuration:
<services> <service id="mws_nc.data_provider.translatable_collection_data_provider" class="MWS\NutritionCalculatorBundle\DataProvider\TranslatableCollectionDataProvider" > <tag name="api_platform.collection_data_provider" priority="2"/> <argument type="service" id="doctrine"/> <argument type="service" id="request_stack"/> <argument type="service" id="api_platform.metadata.resource.metadata_factory" /> <argument>%api_platform.collection.pagination.enabled%</argument> <argument>%api_platform.collection.pagination.client_enabled%</argument> <argument>%api_platform.collection.pagination.client_items_per_page%</argument> <argument>%api_platform.collection.pagination.items_per_page%</argument> <argument>%api_platform.collection.pagination.page_parameter_name%</argument> <argument>%api_platform.collection.pagination.enabled_parameter_name%</argument> <argument>%api_platform.collection.pagination.items_per_page_parameter_name%</argument> <argument>%api_platform.collection.pagination.maximum_items_per_page%</argument> <argument>%api_platform.collection.pagination.partial%</argument> <argument>%api_platform.collection.pagination.client_partial%</argument> <argument>%api_platform.collection.pagination.partial_parameter_name%</argument> <argument type="collection"> <argument type="service" id="api_platform.doctrine.orm.query_extension.eager_loading"/> <argument type="service" id="api_platform.doctrine.orm.query_extension.filter"/> <argument type="service" id="api_platform.doctrine.orm.query_extension.filter_eager_loading"/> <argument type="service" id="api_platform.doctrine.orm.query_extension.order"/> <argument type="service" id="api_platform.doctrine.orm.query_extension.pagination"/> </argument> </service>
It's working :+1: Thanks a lot!
you can check my ticket for cleaner implementation https://github.com/api-platform/core/issues/5412
I am using StofDoctrineExtensionsBundle to translate entities. While it is working perfectly on collection operations, it fails on item operations and the returned fields are always in default locale. I use an event subscriber to set the locale: