vendor/shopware/core/Framework/DataAbstractionLayer/Search/Parser/SqlQueryParser.php line 268

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer\Search\Parser;
  3. use Doctrine\DBAL\ArrayParameterType;
  4. use Doctrine\DBAL\Connection;
  5. use Shopware\Core\Framework\Context;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityDefinitionQueryHelper;
  7. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Field\FkField;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Field\ListField;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\Filter;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\PrefixFilter;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\SingleFieldFilter;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\SuffixFilter;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Query\ScoreQuery;
  22. use Shopware\Core\Framework\Log\Package;
  23. use Shopware\Core\Framework\Uuid\Uuid;
  24. /**
  25.  * @internal
  26.  */
  27. #[Package('core')]
  28. class SqlQueryParser
  29. {
  30.     /**
  31.      * @internal
  32.      */
  33.     public function __construct(
  34.         private readonly EntityDefinitionQueryHelper $queryHelper,
  35.         private readonly Connection $connection
  36.     ) {
  37.     }
  38.     public function parseRanking(
  39.         array $queries,
  40.         EntityDefinition $definition,
  41.         string $root,
  42.         Context $context
  43.     ): ParseResult {
  44.         $result = new ParseResult();
  45.         /** @var ScoreQuery $query */
  46.         foreach ($queries as $query) {
  47.             $parsed $this->parse($query->getQuery(), $definition$context$root);
  48.             foreach ($parsed->getWheres() as $where) {
  49.                 if ($query->getScoreField()) {
  50.                     $field $this->queryHelper->getFieldAccessor(
  51.                         $query->getScoreField(),
  52.                         $definition,
  53.                         $root,
  54.                         $context
  55.                     );
  56.                     $result->addWhere(
  57.                         sprintf('IF(%s , %s * %s, 0)'$where, (string) $this->connection->quote($query->getScore()), $field)
  58.                     );
  59.                     continue;
  60.                 }
  61.                 $result->addWhere(
  62.                     sprintf('IF(%s , %s, 0)'$where, (string) $this->connection->quote($query->getScore()))
  63.                 );
  64.             }
  65.             foreach ($parsed->getParameters() as $key => $parameter) {
  66.                 $result->addParameter($key$parameter$parsed->getType($key));
  67.             }
  68.         }
  69.         return $result;
  70.     }
  71.     public function parse(
  72.         Filter $query,
  73.         EntityDefinition $definition,
  74.         Context $context,
  75.         ?string $root null,
  76.         bool $negated false
  77.     ): ParseResult {
  78.         if ($root === null) {
  79.             $root $definition->getEntityName();
  80.         }
  81.         if ($query instanceof SingleFieldFilter && $query->getResolved()) {
  82.             $result = new ParseResult();
  83.             $result->addWhere((string) $query->getResolved());
  84.             return $result;
  85.         }
  86.         return match (true) {
  87.             $query instanceof EqualsFilter => $this->parseEqualsFilter($query$definition$root$context$negated),
  88.             $query instanceof EqualsAnyFilter => $this->parseEqualsAnyFilter($query$definition$root$context),
  89.             $query instanceof ContainsFilter => $this->parseContainsFilter($query$definition$root$context),
  90.             $query instanceof PrefixFilter => $this->parsePrefixFilter($query$definition$root$context),
  91.             $query instanceof SuffixFilter => $this->parseSuffixFilter($query$definition$root$context),
  92.             $query instanceof RangeFilter => $this->parseRangeFilter($query$definition$root$context),
  93.             $query instanceof NotFilter => $this->parseNotFilter($query$definition$root$context),
  94.             $query instanceof MultiFilter => $this->parseMultiFilter($query$definition$root$context$negated),
  95.             default => throw new \RuntimeException(sprintf('Unsupported query %s'$query::class)),
  96.         };
  97.     }
  98.     private function parseRangeFilter(
  99.         RangeFilter $query,
  100.         EntityDefinition $definition,
  101.         string $root,
  102.         Context $context
  103.     ): ParseResult {
  104.         $result = new ParseResult();
  105.         $key $this->getKey();
  106.         $field $this->queryHelper->getFieldAccessor($query->getField(), $definition$root$context);
  107.         $where = [];
  108.         if ($query->hasParameter(RangeFilter::GT)) {
  109.             $where[] = $field ' > :' $key;
  110.             $result->addParameter($key$query->getParameter(RangeFilter::GT));
  111.         } elseif ($query->hasParameter(RangeFilter::GTE)) {
  112.             $where[] = $field ' >= :' $key;
  113.             $result->addParameter($key$query->getParameter(RangeFilter::GTE));
  114.         }
  115.         $key $this->getKey();
  116.         if ($query->hasParameter(RangeFilter::LT)) {
  117.             $where[] = $field ' < :' $key;
  118.             $result->addParameter($key$query->getParameter(RangeFilter::LT));
  119.         } elseif ($query->hasParameter(RangeFilter::LTE)) {
  120.             $where[] = $field ' <= :' $key;
  121.             $result->addParameter($key$query->getParameter(RangeFilter::LTE));
  122.         }
  123.         $where '(' implode(' AND '$where) . ')';
  124.         $result->addWhere($where);
  125.         return $result;
  126.     }
  127.     private function parseContainsFilter(ContainsFilter $queryEntityDefinition $definitionstring $rootContext $context): ParseResult
  128.     {
  129.         $key $this->getKey();
  130.         $field $this->queryHelper->getFieldAccessor($query->getField(), $definition$root$context);
  131.         $result = new ParseResult();
  132.         $result->addWhere($field ' LIKE :' $key);
  133.         $escaped addcslashes((string) $query->getValue(), '\\_%');
  134.         $result->addParameter($key'%' $escaped '%');
  135.         return $result;
  136.     }
  137.     private function parsePrefixFilter(PrefixFilter $queryEntityDefinition $definitionstring $rootContext $context): ParseResult
  138.     {
  139.         $key $this->getKey();
  140.         $field $this->queryHelper->getFieldAccessor($query->getField(), $definition$root$context);
  141.         $result = new ParseResult();
  142.         $result->addWhere($field ' LIKE :' $key);
  143.         $escaped addcslashes($query->getValue(), '\\_%');
  144.         $result->addParameter($key$escaped '%');
  145.         return $result;
  146.     }
  147.     private function parseSuffixFilter(SuffixFilter $queryEntityDefinition $definitionstring $rootContext $context): ParseResult
  148.     {
  149.         $key $this->getKey();
  150.         $field $this->queryHelper->getFieldAccessor($query->getField(), $definition$root$context);
  151.         $result = new ParseResult();
  152.         $result->addWhere($field ' LIKE :' $key);
  153.         $escaped addcslashes($query->getValue(), '\\_%');
  154.         $result->addParameter($key'%' $escaped);
  155.         return $result;
  156.     }
  157.     private function parseEqualsAnyFilter(EqualsAnyFilter $queryEntityDefinition $definitionstring $rootContext $context): ParseResult
  158.     {
  159.         $key $this->getKey();
  160.         $select $this->queryHelper->getFieldAccessor($query->getField(), $definition$root$context);
  161.         $field $this->queryHelper->getField($query->getField(), $definition$root);
  162.         $result = new ParseResult();
  163.         if ($field instanceof ListField) {
  164.             $where = [];
  165.             foreach ($query->getValue() as $value) {
  166.                 $key $this->getKey();
  167.                 $where[] = sprintf('JSON_CONTAINS(%s, JSON_ARRAY(%s))'$select':' $key);
  168.                 $result->addParameter($key$value);
  169.             }
  170.             $result->addWhere('(' implode(' OR '$where) . ')');
  171.             return $result;
  172.         }
  173.         $result->addWhere($select ' IN (:' $key ')');
  174.         $value array_values($query->getValue());
  175.         if ($field instanceof IdField || $field instanceof FkField) {
  176.             $value array_filter(array_map(fn (bool|float|int|string $id): string => Uuid::fromHexToBytes((string) $id), $value));
  177.         }
  178.         $result->addParameter($key$valueArrayParameterType::STRING);
  179.         return $result;
  180.     }
  181.     private function parseEqualsFilter(EqualsFilter $queryEntityDefinition $definitionstring $rootContext $contextbool $negated): ParseResult
  182.     {
  183.         $key $this->getKey();
  184.         $select $this->queryHelper->getFieldAccessor($query->getField(), $definition$root$context);
  185.         $field $this->queryHelper->getField($query->getField(), $definition$root);
  186.         $result = new ParseResult();
  187.         if ($field instanceof ListField) {
  188.             $result->addWhere('JSON_CONTAINS(' $select ', JSON_ARRAY(:' $key '))');
  189.             $result->addParameter($key$query->getValue());
  190.             return $result;
  191.         }
  192.         if ($negated || $query->getValue() === null) {
  193.             $result->addWhere($select ' <=> :' $key);
  194.         } else {
  195.             $result->addWhere($select ' = :' $key);
  196.         }
  197.         $value $query->getValue();
  198.         if ($value === null) {
  199.             $result->addParameter($keynull);
  200.             return $result;
  201.         }
  202.         if ($field instanceof IdField || $field instanceof FkField) {
  203.             $value Uuid::fromHexToBytes((string) $value);
  204.         }
  205.         $result->addParameter($key$value);
  206.         return $result;
  207.     }
  208.     private function parseMultiFilter(MultiFilter $queryEntityDefinition $definitionstring $rootContext $contextbool $negated): ParseResult
  209.     {
  210.         $result $this->iterateNested($query$definition$root$context$negated);
  211.         $wheres $result->getWheres();
  212.         $result->resetWheres();
  213.         $glue ' ' $query->getOperator() . ' ';
  214.         if (!empty($wheres)) {
  215.             $result->addWhere('(' implode($glue$wheres) . ')');
  216.         }
  217.         return $result;
  218.     }
  219.     private function parseNotFilter(NotFilter $queryEntityDefinition $definitionstring $rootContext $context): ParseResult
  220.     {
  221.         $result $this->iterateNested($query$definition$root$contexttrue);
  222.         $wheres $result->getWheres();
  223.         $result->resetWheres();
  224.         $glue ' ' $query->getOperator() . ' ';
  225.         if (!empty($wheres)) {
  226.             $result->addWhere('NOT (' implode($glue$wheres) . ')');
  227.         }
  228.         return $result;
  229.     }
  230.     private function iterateNested(MultiFilter $queryEntityDefinition $definitionstring $rootContext $contextbool $negated): ParseResult
  231.     {
  232.         $result = new ParseResult();
  233.         foreach ($query->getQueries() as $multiFilter) {
  234.             $result $result->merge(
  235.                 $this->parse($multiFilter$definition$context$root$negated)
  236.             );
  237.         }
  238.         return $result;
  239.     }
  240.     private function getKey(): string
  241.     {
  242.         return 'param_' Uuid::randomHex();
  243.     }
  244. }