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

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