src/Core/Framework/DataAbstractionLayer/Dbal/EntitySearcher.php line 98

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer\Dbal;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Framework\Context;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Field\AutoIncrementField;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\PrimaryKey;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Field\ReferenceVersionField;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Field\StorageAware;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Field\VersionField;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearcherInterface;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
  14. use Shopware\Core\Framework\Log\Package;
  15. use Shopware\Core\System\NumberRange\DataAbstractionLayer\NumberRangeField;
  16. /**
  17.  * Used for all search operations in the system.
  18.  * The dbal entity searcher only joins and select fields which defined in sorting, filter or query classes.
  19.  * Fields which are not necessary to determines which ids are affected are not fetched.
  20.  *
  21.  * @internal
  22.  */
  23. #[Package('core')]
  24. class EntitySearcher implements EntitySearcherInterface
  25. {
  26.     public function __construct(
  27.         private readonly Connection $connection,
  28.         private readonly EntityDefinitionQueryHelper $queryHelper,
  29.         private readonly CriteriaQueryBuilder $criteriaQueryBuilder
  30.     ) {
  31.     }
  32.     public function search(EntityDefinition $definitionCriteria $criteriaContext $context): IdSearchResult
  33.     {
  34.         if ($criteria->getLimit() === 0) {
  35.             return new IdSearchResult(0, [], $criteria$context);
  36.         }
  37.         $table $definition->getEntityName();
  38.         $query = new QueryBuilder($this->connection);
  39.         $fields = [];
  40.         foreach ($definition->getFields() as $field) {
  41.             if (!$field instanceof StorageAware || $field instanceof ReferenceVersionField || $field instanceof VersionField) {
  42.                 continue;
  43.             }
  44.             if ($field instanceof NumberRangeField) {
  45.                 $fields[$field->getStorageName()] = $field;
  46.                 continue;
  47.             }
  48.             if ($field instanceof AutoIncrementField) {
  49.                 $fields[$field->getStorageName()] = $field;
  50.                 continue;
  51.             }
  52.             if ($field->is(PrimaryKey::class)) {
  53.                 $fields[$field->getStorageName()] = $field;
  54.             }
  55.         }
  56.         /** @var StorageAware $field */
  57.         foreach ($fields as $field) {
  58.             $query->addSelect(
  59.                 EntityDefinitionQueryHelper::escape($table) . '.' EntityDefinitionQueryHelper::escape($field->getStorageName())
  60.             );
  61.         }
  62.         $query $this->criteriaQueryBuilder->build($query$definition$criteria$context);
  63.         if (!empty($criteria->getIds())) {
  64.             $this->queryHelper->addIdCondition($criteria$definition$query);
  65.         }
  66.         $this->addGroupBy($definition$criteria$context$query$table);
  67.         //add pagination
  68.         if ($criteria->getOffset() !== null) {
  69.             $query->setFirstResult($criteria->getOffset());
  70.         }
  71.         if ($criteria->getLimit() !== null) {
  72.             $query->setMaxResults($criteria->getLimit());
  73.         }
  74.         $this->addTotalCountMode($criteria$query);
  75.         if ($criteria->getTitle()) {
  76.             $query->setTitle($criteria->getTitle() . '::search-ids');
  77.         }
  78.         //execute and fetch ids
  79.         $rows $query->executeQuery()->fetchAllAssociative();
  80.         $total $this->getTotalCount($criteria$query$rows);
  81.         if ($criteria->getTotalCountMode() === Criteria::TOTAL_COUNT_MODE_NEXT_PAGES) {
  82.             $rows \array_slice($rows0$criteria->getLimit());
  83.         }
  84.         $converted = [];
  85.         foreach ($rows as $row) {
  86.             $pk = [];
  87.             $data = [];
  88.             foreach ($row as $storageName => $value) {
  89.                 $field $fields[$storageName] ?? null;
  90.                 if (!$field) {
  91.                     $data[$storageName] = $value;
  92.                     continue;
  93.                 }
  94.                 $value $field->getSerializer()->decode($field$value);
  95.                 $data[$field->getPropertyName()] = $value;
  96.                 if (!$field->is(PrimaryKey::class)) {
  97.                     continue;
  98.                 }
  99.                 $pk[$field->getPropertyName()] = $value;
  100.             }
  101.             $arrayKey implode('-'$pk);
  102.             if (\count($pk) === 1) {
  103.                 $pk array_shift($pk);
  104.             }
  105.             $converted[$arrayKey] = [
  106.                 'primaryKey' => $pk,
  107.                 'data' => $data,
  108.             ];
  109.         }
  110.         if ($criteria->useIdSorting()) {
  111.             $converted $this->sortByIdArray($criteria->getIds(), $converted);
  112.         }
  113.         return new IdSearchResult($total$converted$criteria$context);
  114.     }
  115.     private function addTotalCountMode(Criteria $criteriaQueryBuilder $query): void
  116.     {
  117.         if ($criteria->getTotalCountMode() !== Criteria::TOTAL_COUNT_MODE_NEXT_PAGES) {
  118.             return;
  119.         }
  120.         $query->setMaxResults($criteria->getLimit() * 1);
  121.     }
  122.     private function getTotalCount(Criteria $criteriaQueryBuilder $query, array $data): int
  123.     {
  124.         if ($criteria->getTotalCountMode() !== Criteria::TOTAL_COUNT_MODE_EXACT) {
  125.             return \count($data);
  126.         }
  127.         $query->resetQueryPart('orderBy');
  128.         $query->setMaxResults(null);
  129.         $query->setFirstResult(0);
  130.         $total = new QueryBuilder($query->getConnection());
  131.         $total->select(['COUNT(*)'])
  132.             ->from(sprintf('(%s) total'$query->getSQL()))
  133.             ->setParameters($query->getParameters(), $query->getParameterTypes());
  134.         return (int) $total->executeQuery()->fetchOne();
  135.     }
  136.     private function addGroupBy(EntityDefinition $definitionCriteria $criteriaContext $contextQueryBuilder $querystring $table): void
  137.     {
  138.         if ($criteria->getGroupFields()) {
  139.             foreach ($criteria->getGroupFields() as $grouping) {
  140.                 $accessor $this->queryHelper->getFieldAccessor($grouping->getField(), $definition$definition->getEntityName(), $context);
  141.                 $query->addGroupBy($accessor);
  142.             }
  143.             return;
  144.         }
  145.         if ($query->hasState(EntityDefinitionQueryHelper::HAS_TO_MANY_JOIN)) {
  146.             $query->addGroupBy(
  147.                 EntityDefinitionQueryHelper::escape($table) . '.' EntityDefinitionQueryHelper::escape('id')
  148.             );
  149.         }
  150.     }
  151.     private function sortByIdArray(array $ids, array $data): array
  152.     {
  153.         $sorted = [];
  154.         foreach ($ids as $id) {
  155.             if (\is_array($id)) {
  156.                 $id implode('-'$id);
  157.             }
  158.             if (\array_key_exists($id$data)) {
  159.                 $sorted[$id] = $data[$id];
  160.             }
  161.         }
  162.         return $sorted;
  163.     }
  164. }