Total Complexity | 44 |
Total Lines | 277 |
Duplicated Lines | 0 % |
Changes | 5 | ||
Bugs | 1 | Features | 1 |
Complex classes like Query often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Query, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class Query |
||
18 | { |
||
19 | public const TYPE = 'type'; |
||
20 | public const CREATED_AT = 'created_at'; |
||
21 | public const TRANSACTION_HASH = 'transaction_hash'; |
||
22 | public const OBJECT_ID = 'object_id'; |
||
23 | public const USER_ID = 'blame_id'; |
||
24 | public const ID = 'id'; |
||
25 | public const DISCRIMINATOR = 'discriminator'; |
||
26 | |||
27 | /** |
||
28 | * @var array |
||
29 | */ |
||
30 | private $filters = []; |
||
31 | |||
32 | /** |
||
33 | * @var array |
||
34 | */ |
||
35 | private $orderBy = []; |
||
36 | |||
37 | /** |
||
38 | * @var Connection |
||
39 | */ |
||
40 | private $connection; |
||
41 | |||
42 | /** |
||
43 | * @var string |
||
44 | */ |
||
45 | private $table; |
||
46 | |||
47 | /** |
||
48 | * @var int |
||
49 | */ |
||
50 | private $offset = 0; |
||
51 | |||
52 | /** |
||
53 | * @var int |
||
54 | */ |
||
55 | private $limit = 0; |
||
56 | |||
57 | public function __construct(string $table, Connection $connection) |
||
64 | } |
||
65 | } |
||
66 | |||
67 | public function execute(): array |
||
68 | { |
||
69 | $queryBuilder = $this->buildQueryBuilder(); |
||
70 | if (method_exists($queryBuilder, 'executeQuery')) { |
||
71 | // doctrine/dbal v3.x |
||
72 | $statement = $queryBuilder->executeQuery(); |
||
73 | } else { |
||
74 | // doctrine/dbal v2.13.x |
||
75 | $statement = $queryBuilder->execute(); |
||
|
|||
76 | } |
||
77 | |||
78 | $result = []; |
||
79 | \assert($statement instanceof Result); |
||
80 | foreach ($statement->fetchAllAssociative() as $row) { |
||
81 | $result[] = Entry::fromArray($row); |
||
82 | } |
||
83 | |||
84 | return $result; |
||
85 | } |
||
86 | |||
87 | public function count(): int |
||
88 | { |
||
89 | $queryBuilder = $this->buildQueryBuilder(); |
||
90 | |||
91 | try { |
||
92 | $queryBuilder |
||
93 | ->resetQueryPart('select') |
||
94 | ->resetQueryPart('orderBy') |
||
95 | ->setMaxResults(null) |
||
96 | ->setFirstResult(null) |
||
97 | ->select('COUNT(id)') |
||
98 | ; |
||
99 | |||
100 | if (method_exists($queryBuilder, 'executeQuery')) { |
||
101 | // doctrine/dbal v3.x |
||
102 | $result = $queryBuilder |
||
103 | ->executeQuery() |
||
104 | ->fetchOne() |
||
105 | ; |
||
106 | } else { |
||
107 | // doctrine/dbal v2.13.x |
||
108 | $result = $queryBuilder |
||
109 | ->execute() |
||
110 | ->fetchColumn(0) |
||
111 | ; |
||
112 | } |
||
113 | } catch (Exception $e) { |
||
114 | $result = false; |
||
115 | } |
||
116 | |||
117 | return false === $result ? 0 : $result; |
||
118 | } |
||
119 | |||
120 | public function addFilter(FilterInterface $filter): self |
||
121 | { |
||
122 | $this->checkFilter($filter->getName()); |
||
123 | $this->filters[$filter->getName()][] = $filter; |
||
124 | |||
125 | return $this; |
||
126 | } |
||
127 | |||
128 | public function addOrderBy(string $field, string $direction = 'DESC'): self |
||
129 | { |
||
130 | $this->checkFilter($field); |
||
131 | |||
132 | if (!\in_array($direction, ['ASC', 'DESC'], true)) { |
||
133 | throw new InvalidArgumentException('Invalid sort direction, allowed value: ASC, DESC'); |
||
134 | } |
||
135 | |||
136 | $this->orderBy[$field] = $direction; |
||
137 | |||
138 | return $this; |
||
139 | } |
||
140 | |||
141 | public function limit(int $limit, int $offset = 0): self |
||
142 | { |
||
143 | if (0 > $limit) { |
||
144 | throw new InvalidArgumentException('Limit cannot be negative.'); |
||
145 | } |
||
146 | if (0 > $offset) { |
||
147 | throw new InvalidArgumentException('Offset cannot be negative.'); |
||
148 | } |
||
149 | |||
150 | $this->limit = $limit; |
||
151 | $this->offset = $offset; |
||
152 | |||
153 | return $this; |
||
154 | } |
||
155 | |||
156 | public function getSupportedFilters(): array |
||
157 | { |
||
158 | return array_keys(SchemaHelper::getAuditTableIndices('fake')); |
||
159 | } |
||
160 | |||
161 | public function getFilters(): array |
||
162 | { |
||
163 | return $this->filters; |
||
164 | } |
||
165 | |||
166 | public function getOrderBy(): array |
||
167 | { |
||
168 | return $this->orderBy; |
||
169 | } |
||
170 | |||
171 | public function getLimit(): array |
||
172 | { |
||
173 | return [$this->limit, $this->offset]; |
||
174 | } |
||
175 | |||
176 | private function buildQueryBuilder(): QueryBuilder |
||
177 | { |
||
178 | $queryBuilder = $this->connection->createQueryBuilder(); |
||
179 | $queryBuilder |
||
180 | ->select('*') |
||
181 | ->from($this->table, 'at') |
||
182 | ; |
||
183 | |||
184 | // build WHERE clause(s) |
||
185 | $queryBuilder = $this->buildWhere($queryBuilder); |
||
186 | |||
187 | // build ORDER BY part |
||
188 | $queryBuilder = $this->buildOrderBy($queryBuilder); |
||
189 | |||
190 | // build LIMIT part |
||
191 | return $this->buildLimit($queryBuilder); |
||
192 | } |
||
193 | |||
194 | private function groupFilters(array $filters): array |
||
195 | { |
||
196 | $grouped = []; |
||
197 | |||
198 | foreach ($filters as $filter) { |
||
199 | $class = \get_class($filter); |
||
200 | if (!isset($grouped[$class])) { |
||
201 | $grouped[$class] = []; |
||
202 | } |
||
203 | $grouped[$class][] = $filter; |
||
204 | } |
||
205 | |||
206 | return $grouped; |
||
207 | } |
||
208 | |||
209 | private function mergeSimpleFilters(array $filters): SimpleFilter |
||
210 | { |
||
211 | $merged = []; |
||
212 | $name = null; |
||
213 | |||
214 | foreach ($filters as $filter) { |
||
215 | if (null === $name) { |
||
216 | $name = $filter->getName(); |
||
217 | } |
||
218 | |||
219 | if (\is_array($filter->getValue())) { |
||
220 | $merged = array_merge($merged, $filter->getValue()); |
||
221 | } else { |
||
222 | $merged[] = $filter->getValue(); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | return new SimpleFilter($name, $merged); |
||
227 | } |
||
228 | |||
229 | private function buildWhere(QueryBuilder $queryBuilder): QueryBuilder |
||
230 | { |
||
231 | foreach ($this->filters as $name => $rawFilters) { |
||
232 | if (0 === \count($rawFilters)) { |
||
233 | continue; |
||
234 | } |
||
235 | |||
236 | // group filters by class |
||
237 | $grouped = $this->groupFilters($rawFilters); |
||
238 | |||
239 | foreach ($grouped as $class => $filters) { |
||
240 | switch ($class) { |
||
241 | case SimpleFilter::class: |
||
242 | $filters = [$this->mergeSimpleFilters($filters)]; |
||
243 | |||
244 | break; |
||
245 | case RangeFilter::class: |
||
246 | case DateRangeFilter::class: |
||
247 | break; |
||
248 | } |
||
249 | |||
250 | foreach ($filters as $filter) { |
||
251 | $data = $filter->getSQL(); |
||
252 | |||
253 | $queryBuilder->andWhere($data['sql']); |
||
254 | |||
255 | foreach ($data['params'] as $name => $value) { |
||
256 | if (\is_array($value)) { |
||
257 | $queryBuilder->setParameter($name, $value, Connection::PARAM_STR_ARRAY); |
||
258 | } else { |
||
259 | $queryBuilder->setParameter($name, $value); |
||
260 | } |
||
261 | } |
||
262 | } |
||
263 | } |
||
264 | } |
||
265 | |||
266 | return $queryBuilder; |
||
267 | } |
||
268 | |||
269 | private function buildOrderBy(QueryBuilder $queryBuilder): QueryBuilder |
||
270 | { |
||
271 | foreach ($this->orderBy as $field => $direction) { |
||
272 | $queryBuilder->addOrderBy($field, $direction); |
||
273 | } |
||
274 | |||
275 | return $queryBuilder; |
||
276 | } |
||
277 | |||
278 | private function buildLimit(QueryBuilder $queryBuilder): QueryBuilder |
||
288 | } |
||
289 | |||
290 | private function checkFilter(string $filter): void |
||
291 | { |
||
292 | if (!\in_array($filter, $this->getSupportedFilters(), true)) { |
||
293 | throw new InvalidArgumentException(sprintf('Unsupported "%s" filter, allowed filters: %s.', $filter, implode(', ', $this->getSupportedFilters()))); |
||
294 | } |
||
295 | } |
||
296 | } |
||
297 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.