mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
Merge branch '2.0' into api-platform-3
* 2.0: [API] Fix translation.name filters [Core] Fix #16603 SyliusCollector do not display API tag properly [Documentation] Update the dates on the release cycle page
This commit is contained in:
commit
df6f0c867f
6 changed files with 329 additions and 30 deletions
|
|
@ -59,9 +59,9 @@ Planned releases
|
||||||
+---------+----------------------+------------------------+--------------------+
|
+---------+----------------------+------------------------+--------------------+
|
||||||
| Version | Development starts | Stabilization starts | Release date |
|
| Version | Development starts | Stabilization starts | Release date |
|
||||||
+=========+======================+========================+====================+
|
+=========+======================+========================+====================+
|
||||||
| 2.0 | Sep 15, 2023 | Q2 2024 | Q3 2024 |
|
| 2.0 | Sep 15, 2023 | Q3 2024 | Q4 2024 |
|
||||||
+---------+----------------------+------------------------+--------------------+
|
+---------+----------------------+------------------------+--------------------+
|
||||||
| 1.14 | Apr 26, 2024 | Q2 2024 | Q3 2024 |
|
| 1.14 | Apr 26, 2024 | Q3 2024 | Q4 2024 |
|
||||||
+---------+----------------------+------------------------+--------------------+
|
+---------+----------------------+------------------------+--------------------+
|
||||||
|
|
||||||
Supported versions
|
Supported versions
|
||||||
|
|
@ -70,7 +70,7 @@ Supported versions
|
||||||
+---------+--------------------+--------------------+--------------------+---------------------+
|
+---------+--------------------+--------------------+--------------------+---------------------+
|
||||||
| Version | Release date | End of maintenance | End of life | Status |
|
| Version | Release date | End of maintenance | End of life | Status |
|
||||||
+=========+====================+====================+====================+=====================+
|
+=========+====================+====================+====================+=====================+
|
||||||
| 1.13 | Apr 23, 2024 | Oct 23, 2024 | Apr 23, 2025 | Fully supported |
|
| 1.13 | Apr 23, 2024 | Jan 23, 2025 | Apr 23, 2025 | Fully supported |
|
||||||
+---------+--------------------+--------------------+--------------------+---------------------+
|
+---------+--------------------+--------------------+--------------------+---------------------+
|
||||||
| 1.12 | Oct 31, 2022 | Jun 30, 2024 | Dec 31, 2024 | Security fixes only |
|
| 1.12 | Oct 31, 2022 | Jun 30, 2024 | Dec 31, 2024 | Security fixes only |
|
||||||
+---------+--------------------+--------------------+--------------------+---------------------+
|
+---------+--------------------+--------------------+--------------------+---------------------+
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Sylius package.
|
||||||
|
*
|
||||||
|
* (c) Sylius Sp. z o.o.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Common;
|
||||||
|
|
||||||
|
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
|
||||||
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||||
|
use ApiPlatform\Metadata\Operation;
|
||||||
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||||
|
|
||||||
|
final class TranslationOrderLocaleExtension implements QueryCollectionExtensionInterface
|
||||||
|
{
|
||||||
|
/** @param array<string, mixed> $context */
|
||||||
|
public function applyToCollection(
|
||||||
|
QueryBuilder $queryBuilder,
|
||||||
|
QueryNameGeneratorInterface $queryNameGenerator,
|
||||||
|
string $resourceClass,
|
||||||
|
?Operation $operation = null,
|
||||||
|
array $context = [],
|
||||||
|
): void {
|
||||||
|
if (!is_a($resourceClass, TranslatableInterface::class, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* @see \Sylius\Bundle\ApiBundle\Filter\Doctrine\TranslationOrderNameAndLocaleFilter */
|
||||||
|
if (!isset($context['filters']['order']['translation.name'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$queryBuilder->getEntityManager()->getClassMetadata($resourceClass)->hasAssociation('translations')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$localeCode = $this->resolveContextLocaleCode($context);
|
||||||
|
if (empty($localeCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootAlias = $queryBuilder->getRootAliases()[0];
|
||||||
|
$localeCodeParameterName = $queryNameGenerator->generateParameterName('localeCode');
|
||||||
|
|
||||||
|
$queryBuilder
|
||||||
|
->addSelect('translation')
|
||||||
|
->leftJoin(
|
||||||
|
sprintf('%s.translations', $rootAlias),
|
||||||
|
'translation',
|
||||||
|
Join::WITH,
|
||||||
|
sprintf('translation.locale = :%s', $localeCodeParameterName),
|
||||||
|
)
|
||||||
|
->setParameter($localeCodeParameterName, $localeCode)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param array<string, mixed> $context */
|
||||||
|
private function resolveContextLocaleCode(array $context): ?string
|
||||||
|
{
|
||||||
|
return
|
||||||
|
$context['filters']['localeCode for order']['translation.name'] ??
|
||||||
|
$context['filters']['order']['localeCode']['translation.name'] ??
|
||||||
|
$context['filters']['order']['localeCode'] ??
|
||||||
|
null
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Sylius\Bundle\ApiBundle\Filter\Doctrine;
|
namespace Sylius\Bundle\ApiBundle\Filter\Doctrine;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface;
|
use ApiPlatform\Core\Bridge\Doctrine\Common\Filter\OrderFilterInterface;
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
|
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
|
||||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
|
|
@ -23,41 +23,21 @@ final class TranslationOrderNameAndLocaleFilter extends AbstractFilter
|
||||||
{
|
{
|
||||||
protected function filterProperty(
|
protected function filterProperty(
|
||||||
string $property,
|
string $property,
|
||||||
$value,
|
$value,
|
||||||
QueryBuilder $queryBuilder,
|
QueryBuilder $queryBuilder,
|
||||||
QueryNameGeneratorInterface $queryNameGenerator,
|
QueryNameGeneratorInterface $queryNameGenerator,
|
||||||
string $resourceClass,
|
string $resourceClass,
|
||||||
?Operation $operation = null,
|
?Operation $operation = null,
|
||||||
array $context = [],
|
array $context = [],
|
||||||
): void {
|
): void {
|
||||||
if ('order' === $property) {
|
if ('order' === $property && isset($value['translation.name'])) {
|
||||||
if (!isset($value['translation.name'])) {
|
/** @phpstan-ignore-next-line */
|
||||||
return;
|
if (!$queryBuilder->getEntityManager()->getClassMetadata($resourceClass)->hasAssociation('translations')) {
|
||||||
}
|
|
||||||
|
|
||||||
$direction = $value['translation.name'];
|
|
||||||
$rootAlias = $queryBuilder->getRootAliases()[0];
|
|
||||||
|
|
||||||
if (isset($value['localeCode'])) {
|
|
||||||
$localeParameterName = $queryNameGenerator->generateParameterName('locale');
|
|
||||||
|
|
||||||
$queryBuilder
|
|
||||||
->addSelect('translation')
|
|
||||||
->leftJoin(
|
|
||||||
sprintf('%s.translations', $rootAlias),
|
|
||||||
'translation',
|
|
||||||
'WITH',
|
|
||||||
sprintf('translation.locale = :%s', $localeParameterName),
|
|
||||||
)
|
|
||||||
->orderBy('translation.name', $direction)
|
|
||||||
->setParameter($localeParameterName, $value['localeCode'])
|
|
||||||
;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$queryBuilder
|
$queryBuilder
|
||||||
->orderBy('translation.name', $direction)
|
->orderBy('translation.name', $value['translation.name'])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +57,7 @@ final class TranslationOrderNameAndLocaleFilter extends AbstractFilter
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
/* @see \Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Common\TranslationOrderLocaleExtension */
|
||||||
'localeCode for order[translation.name]' => [
|
'localeCode for order[translation.name]' => [
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@
|
||||||
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="sylius_api.doctrine.orm.query_extension.common.translation_order_locale" class="Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Common\TranslationOrderLocaleExtension">
|
||||||
|
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="sylius_api.doctrine.orm.query_extension.shop.product_review.accepted" class="Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\ProductReview\AcceptedExtension">
|
<service id="sylius_api.doctrine.orm.query_extension.shop.product_review.accepted" class="Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\ProductReview\AcceptedExtension">
|
||||||
<argument type="service" id="sylius.section_resolver.uri_based_section_resolver" />
|
<argument type="service" id="sylius.section_resolver.uri_based_section_resolver" />
|
||||||
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Sylius package.
|
||||||
|
*
|
||||||
|
* (c) Sylius Sp. z o.o.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sylius\Bundle\ApiBundle\Tests\Doctrine\ORM\QueryExtension\Common;
|
||||||
|
|
||||||
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Common\TranslationOrderLocaleExtension;
|
||||||
|
use Sylius\Component\Core\Model\ProductInterface;
|
||||||
|
|
||||||
|
final class TranslationOrderLocaleExtensionTest extends TestCase
|
||||||
|
{
|
||||||
|
private QueryNameGeneratorInterface&MockObject $queryNameGenerator;
|
||||||
|
|
||||||
|
private QueryBuilder&MockObject $queryBuilder;
|
||||||
|
|
||||||
|
private EntityManagerInterface&MockObject $entityManager;
|
||||||
|
|
||||||
|
private ClassMetadata&MockObject $classMetadata;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->queryNameGenerator = $this->createMock(QueryNameGeneratorInterface::class);
|
||||||
|
$this->queryBuilder = $this->createMock(QueryBuilder::class);
|
||||||
|
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||||
|
$this->classMetadata = $this->createMock(ClassMetadata::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_does_nothing_when_resource_class_is_not_translatable(): void
|
||||||
|
{
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->never())
|
||||||
|
->method('leftJoin')
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->doApplyToCollection(\stdClass::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_does_nothing_when_the_resource_is_not_sorted_by_translation_name(): void
|
||||||
|
{
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->never())
|
||||||
|
->method('leftJoin')
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->doApplyToCollection(ProductInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_does_nothing_when_the_resource_does_not_have_a_translations_association(): void
|
||||||
|
{
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getEntityManager')
|
||||||
|
->willReturn($this->entityManager)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->entityManager
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getClassMetadata')
|
||||||
|
->with(ProductInterface::class)
|
||||||
|
->willReturn($this->classMetadata)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->classMetadata
|
||||||
|
->expects($this->once())
|
||||||
|
->method('hasAssociation')
|
||||||
|
->with('translations')
|
||||||
|
->willReturn(false)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->never())
|
||||||
|
->method('leftJoin')
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->doApplyToCollection(ProductInterface::class, [
|
||||||
|
'filters' => [
|
||||||
|
'order' => [
|
||||||
|
'translation.name' => 'test',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_does_nothing_when_no_locale_code_has_been_resolved_from_filters(): void
|
||||||
|
{
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getEntityManager')
|
||||||
|
->willReturn($this->entityManager)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->entityManager
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getClassMetadata')
|
||||||
|
->with(ProductInterface::class)
|
||||||
|
->willReturn($this->classMetadata)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->classMetadata
|
||||||
|
->expects($this->once())
|
||||||
|
->method('hasAssociation')
|
||||||
|
->with('translations')
|
||||||
|
->willReturn(true)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->never())
|
||||||
|
->method('leftJoin')
|
||||||
|
->withAnyParameters()
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->doApplyToCollection(ProductInterface::class, [
|
||||||
|
'filters' => [
|
||||||
|
'order' => [
|
||||||
|
'translation.name' => 'test',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @dataProvider getLocaleCodeContexts
|
||||||
|
*/
|
||||||
|
public function it_joins_on_a_specific_translation_when_locale_code_has_been_resolved_from_filters(
|
||||||
|
array $contextWithLocaleCode,
|
||||||
|
): void {
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getEntityManager')
|
||||||
|
->willReturn($this->entityManager)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->entityManager
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getClassMetadata')
|
||||||
|
->with(ProductInterface::class)
|
||||||
|
->willReturn($this->classMetadata)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->classMetadata
|
||||||
|
->expects($this->once())
|
||||||
|
->method('hasAssociation')
|
||||||
|
->with('translations')
|
||||||
|
->willReturn(true)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getRootAliases')
|
||||||
|
->willReturn(['alias'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryNameGenerator
|
||||||
|
->method('generateParameterName')
|
||||||
|
->with('localeCode')
|
||||||
|
->willReturn('param')
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('addSelect')
|
||||||
|
->with('translation')
|
||||||
|
->willReturnSelf()
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('leftJoin')
|
||||||
|
->with('alias.translations', 'translation', Join::WITH, 'translation.locale = :param')
|
||||||
|
->willReturnSelf()
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->queryBuilder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('setParameter')
|
||||||
|
->with('param', 'en_US')
|
||||||
|
->willReturnSelf()
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->doApplyToCollection(ProductInterface::class, array_merge_recursive([
|
||||||
|
'filters' => [
|
||||||
|
'order' => [
|
||||||
|
'translation.name' => 'test',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
], $contextWithLocaleCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param array<string, mixed> $context */
|
||||||
|
private function doApplyToCollection(string $resourceClass, array $context = []): void
|
||||||
|
{
|
||||||
|
(new TranslationOrderLocaleExtension())->applyToCollection(
|
||||||
|
queryBuilder: $this->queryBuilder,
|
||||||
|
queryNameGenerator: $this->queryNameGenerator,
|
||||||
|
resourceClass: $resourceClass,
|
||||||
|
context: $context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return iterable<array<string, mixed>> */
|
||||||
|
private function getLocaleCodeContexts(): iterable
|
||||||
|
{
|
||||||
|
yield 'locale code in documentation filter' => [[
|
||||||
|
'filters' => [
|
||||||
|
'localeCode for order' => ['translation.name' => 'en_US'],
|
||||||
|
],
|
||||||
|
]];
|
||||||
|
yield 'locale code in localeCode translation.name filter' => [[
|
||||||
|
'filters' => [
|
||||||
|
'order' => ['localeCode' => ['translation.name' => 'en_US']],
|
||||||
|
],
|
||||||
|
]];
|
||||||
|
yield 'locale code in localeCode filter' => [[
|
||||||
|
'filters' => [
|
||||||
|
'order' => ['localeCode' => 'en_US'],
|
||||||
|
],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ final class SyliusCollector extends DataCollector
|
||||||
'default_locale_code' => $defaultLocaleCode,
|
'default_locale_code' => $defaultLocaleCode,
|
||||||
'locale_code' => null,
|
'locale_code' => null,
|
||||||
'extensions' => [
|
'extensions' => [
|
||||||
'SyliusAdminApiBundle' => ['name' => 'API', 'enabled' => false],
|
'SyliusApiBundle' => ['name' => 'API', 'enabled' => false],
|
||||||
'SyliusAdminBundle' => ['name' => 'Admin', 'enabled' => false],
|
'SyliusAdminBundle' => ['name' => 'Admin', 'enabled' => false],
|
||||||
'SyliusShopBundle' => ['name' => 'Shop', 'enabled' => false],
|
'SyliusShopBundle' => ['name' => 'Shop', 'enabled' => false],
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue