EntitySearcher::search()   F
last analyzed

Complexity

Conditions 21
Paths 6913

Size

Total Lines 111
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 57
nc 6913
nop 3
dl 0
loc 111
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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