Add OrderByIdentifierSqlWalker

This commit is contained in:
Kevin Kaniaburka 2022-12-19 16:01:42 +01:00
parent fe1ef6002b
commit af5ad175f8
No known key found for this signature in database
GPG key ID: 8DB4C54474F3FD72
2 changed files with 102 additions and 0 deletions

View file

@ -0,0 +1,86 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* 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\CoreBundle\Doctrine\ORM\SqlWalker;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\OrderByClause;
use Doctrine\ORM\Query\AST\OrderByItem;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\SqlWalker;
final class OrderByIdentifierSqlWalker extends SqlWalker
{
public function walkSelectStatement(SelectStatement $ast)
{
$dqlAlias = $this->getDqlAlias();
if (null !== $dqlAlias && $this->isOrderByIdentifierAllowed($ast)) {
$this->appendOrderByIdentifier($ast, $dqlAlias);
}
return parent::walkSelectStatement($ast);
}
private function appendOrderByIdentifier(SelectStatement $ast, string $dqlAlias): void
{
$metadata = $this->getMetadataForDqlAlias($dqlAlias);
$expression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$dqlAlias,
$metadata->getSingleIdentifierFieldName()
);
$expression->type = PathExpression::TYPE_STATE_FIELD;
$orderById = new OrderByItem($expression);
$orderById->type = 'ASC';
if (null === $ast->orderByClause) {
$ast->orderByClause = new OrderByClause([$orderById]);
return;
}
$ast->orderByClause->orderByItems[] = $orderById;
}
/**
* @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/cookbook/dql-custom-walkers.html#extending-dql-in-doctrine-orm-custom-ast-walkers
*/
private function getDqlAlias(): ?string
{
foreach ($this->getQueryComponents() as $dqlAlias => $queryComponent) {
if (null === ($queryComponent['parent'] ?? null) && 0 === ($queryComponent['nestingLevel'] ?? 0)) {
return $dqlAlias;
}
}
return null;
}
private function isOrderByIdentifierAllowed(SelectStatement $ast): bool
{
// false if not sure the identifier is added to the GROUP BY clause
if (null !== $ast->groupByClause) {
return false;
}
$expression = current($ast->selectClause->selectExpressions)->expression;
// false if aggregate functions is used
return !$expression instanceof AggregateExpression && !$expression instanceof FunctionNode;
}
}

View file

@ -20,6 +20,7 @@ use Doctrine\Inflector\Rules\Substitution;
use Doctrine\Inflector\Rules\Substitutions;
use Doctrine\Inflector\Rules\Transformations;
use Doctrine\Inflector\Rules\Word;
use Doctrine\ORM\Query;
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\BackwardsCompatibility\CancelOrderStateMachineCallbackPass;
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\BackwardsCompatibility\ResolveShopUserTargetEntityPass;
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\CircularDependencyBreakingErrorListenerPass;
@ -30,6 +31,7 @@ use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\LiipImageFiltersPass;
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\RegisterTaxCalculationStrategiesPass;
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\RegisterUriBasedSectionResolverPass;
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\TranslatableEntityLocalePass;
use Sylius\Bundle\CoreBundle\Doctrine\ORM\SqlWalker\OrderByIdentifierSqlWalker;
use Sylius\Bundle\ResourceBundle\AbstractResourceBundle;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Sylius\Component\Resource\Metadata\Metadata;
@ -48,6 +50,8 @@ final class SyliusCoreBundle extends AbstractResourceBundle
{
parent::boot();
$this->setSqlWalker();
$factory = InflectorFactory::create();
$factory->withPluralRules(new Ruleset(
new Transformations(),
@ -82,4 +86,16 @@ final class SyliusCoreBundle extends AbstractResourceBundle
{
return 'Sylius\Component\Core\Model';
}
private function setSqlWalker(): void
{
$this->container
->get('doctrine.orm.entity_manager')
->getConfiguration()
->setDefaultQueryHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
OrderByIdentifierSqlWalker::class
)
;
}
}