Complex classes like ModelQueryBuilder 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 ModelQueryBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Limoncello\Flute\Adapters; |
||
41 | class ModelQueryBuilder extends QueryBuilder |
||
42 | { |
||
43 | /** |
||
44 | * Condition joining method. |
||
45 | */ |
||
46 | public const AND = 0; |
||
47 | |||
48 | /** |
||
49 | * Condition joining method. |
||
50 | */ |
||
51 | public const OR = self::AND + 1; |
||
52 | |||
53 | /** |
||
54 | * @var string |
||
55 | */ |
||
56 | private $modelClass; |
||
57 | |||
58 | /** |
||
59 | * @var string |
||
60 | */ |
||
61 | private $mainTableName; |
||
62 | |||
63 | /** |
||
64 | * @var string |
||
65 | */ |
||
66 | private $mainAlias; |
||
67 | |||
68 | /** |
||
69 | * @var Closure |
||
70 | */ |
||
71 | private $columnMapper; |
||
72 | |||
73 | /** |
||
74 | * @var ModelSchemaInfoInterface |
||
75 | */ |
||
76 | private $modelSchemas; |
||
77 | |||
78 | /** |
||
79 | * @var int |
||
80 | */ |
||
81 | private $aliasIdCounter = 0; |
||
82 | |||
83 | /** |
||
84 | * @var array |
||
85 | */ |
||
86 | private $knownAliases = []; |
||
87 | |||
88 | /** |
||
89 | * @var Type|null |
||
90 | */ |
||
91 | private $dateTimeType; |
||
92 | |||
93 | /** |
||
94 | * @param Connection $connection |
||
95 | * @param string $modelClass |
||
96 | * @param ModelSchemaInfoInterface $modelSchemas |
||
97 | * |
||
98 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
99 | */ |
||
100 | 66 | public function __construct(Connection $connection, string $modelClass, ModelSchemaInfoInterface $modelSchemas) |
|
114 | |||
115 | /** |
||
116 | * @return string |
||
117 | */ |
||
118 | 66 | public function getModelClass(): string |
|
122 | |||
123 | /** |
||
124 | * @param string|null $tableAlias |
||
125 | * @param string|null $modelClass |
||
126 | * |
||
127 | * @return array |
||
128 | */ |
||
129 | 52 | public function getModelColumns(string $tableAlias = null, string $modelClass = null): array |
|
150 | |||
151 | /** |
||
152 | * Select all fields associated with model. |
||
153 | * |
||
154 | * @param iterable|null $columns |
||
155 | * |
||
156 | * @return self |
||
157 | * |
||
158 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
159 | */ |
||
160 | 57 | public function selectModelColumns(iterable $columns = null): self |
|
161 | { |
||
162 | 57 | if ($columns !== null) { |
|
163 | 5 | $quotedColumns = []; |
|
164 | 5 | foreach ($columns as $column) { |
|
165 | 5 | $quotedColumns[] = $this->buildColumnName($this->getAlias(), $column); |
|
166 | } |
||
167 | } else { |
||
168 | 52 | $quotedColumns = $this->getModelColumns(); |
|
169 | } |
||
170 | |||
171 | 57 | $this->select($quotedColumns); |
|
172 | |||
173 | 57 | return $this; |
|
174 | } |
||
175 | |||
176 | /** |
||
177 | * @return self |
||
178 | */ |
||
179 | 14 | public function distinct(): self |
|
180 | { |
||
181 | // emulate SELECT DISTINCT with grouping by primary key |
||
182 | 14 | $primaryColumn = $this->getModelSchemas()->getPrimaryKey($this->getModelClass()); |
|
183 | 14 | $this->addGroupBy($this->getQuotedMainAliasColumn($primaryColumn)); |
|
184 | |||
185 | 14 | return $this; |
|
186 | } |
||
187 | |||
188 | /** |
||
189 | * @param Closure $columnMapper |
||
190 | * |
||
191 | * @return self |
||
192 | */ |
||
193 | 66 | public function setColumnToDatabaseMapper(Closure $columnMapper): self |
|
194 | { |
||
195 | 66 | $this->columnMapper = $columnMapper; |
|
196 | |||
197 | 66 | return $this; |
|
198 | } |
||
199 | |||
200 | /** |
||
201 | * @return self |
||
202 | */ |
||
203 | 57 | public function fromModelTable(): self |
|
204 | { |
||
205 | 57 | $this->from( |
|
206 | 57 | $this->quoteTableName($this->getTableName()), |
|
207 | 57 | $this->quoteTableName($this->getAlias()) |
|
208 | ); |
||
209 | |||
210 | 57 | return $this; |
|
211 | } |
||
212 | |||
213 | /** |
||
214 | * @param iterable $attributes |
||
215 | * |
||
216 | * @return self |
||
217 | * |
||
218 | * @throws DBALException |
||
219 | * |
||
220 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
221 | */ |
||
222 | 5 | public function createModel(iterable $attributes): self |
|
223 | { |
||
224 | 5 | $this->insert($this->quoteTableName($this->getTableName())); |
|
225 | |||
226 | 5 | $valuesAsParams = []; |
|
227 | 5 | foreach ($this->bindAttributes($this->getModelClass(), $attributes) as $quotedColumn => $parameterName) { |
|
228 | 5 | $valuesAsParams[$quotedColumn] = $parameterName; |
|
229 | } |
||
230 | 5 | $this->values($valuesAsParams); |
|
231 | |||
232 | 5 | return $this; |
|
233 | } |
||
234 | |||
235 | /** |
||
236 | * @param iterable $attributes |
||
237 | * |
||
238 | * @return self |
||
239 | * |
||
240 | * @throws DBALException |
||
241 | * |
||
242 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
243 | */ |
||
244 | 6 | public function updateModels(iterable $attributes): self |
|
254 | |||
255 | /** |
||
256 | * @param string $modelClass |
||
257 | * @param iterable $attributes |
||
258 | * |
||
259 | * @return iterable |
||
|
|||
260 | * |
||
261 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
262 | * |
||
263 | * @throws DBALException |
||
264 | */ |
||
265 | 11 | public function bindAttributes(string $modelClass, iterable $attributes): iterable |
|
266 | { |
||
267 | 11 | $dbPlatform = $this->getConnection()->getDatabasePlatform(); |
|
268 | 11 | $types = $this->getModelSchemas()->getAttributeTypes($modelClass); |
|
269 | |||
270 | 11 | foreach ($attributes as $column => $value) { |
|
271 | 11 | assert(is_string($column) && $this->getModelSchemas()->hasAttributeType($this->getModelClass(), $column)); |
|
272 | |||
273 | 11 | $quotedColumn = $this->quoteColumnName($column); |
|
274 | 11 | $type = $this->getDbalType($types[$column]); |
|
275 | 11 | $pdoValue = $type->convertToDatabaseValue($value, $dbPlatform); |
|
276 | 11 | $parameterName = $this->createNamedParameter($pdoValue, $type->getBindingType()); |
|
277 | |||
278 | 11 | yield $quotedColumn => $parameterName; |
|
279 | } |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * @return self |
||
284 | */ |
||
285 | 4 | public function deleteModels(): self |
|
291 | |||
292 | /** |
||
293 | * @param string $relationshipName |
||
294 | * @param string $identity |
||
295 | * @param string $secondaryIdBindName |
||
296 | * |
||
297 | * @return self |
||
298 | */ |
||
299 | 5 | public function prepareCreateInToManyRelationship( |
|
300 | string $relationshipName, |
||
301 | string $identity, |
||
302 | string $secondaryIdBindName |
||
303 | ): self { |
||
304 | list ($intermediateTable, $primaryKey, $secondaryKey) = |
||
305 | 5 | $this->getModelSchemas()->getBelongsToManyRelationship($this->getModelClass(), $relationshipName); |
|
306 | |||
307 | $this |
||
308 | 5 | ->insert($this->quoteTableName($intermediateTable)) |
|
309 | 5 | ->values([ |
|
310 | 5 | $this->quoteColumnName($primaryKey) => $this->createNamedParameter($identity), |
|
311 | 5 | $this->quoteColumnName($secondaryKey) => $secondaryIdBindName, |
|
312 | ]); |
||
313 | |||
314 | 5 | return $this; |
|
315 | } |
||
316 | |||
317 | /** |
||
318 | * @param string $relationshipName |
||
319 | * @param string $identity |
||
320 | * @param iterable $secondaryIds |
||
321 | * |
||
322 | * @return ModelQueryBuilder |
||
323 | * |
||
324 | * @throws DBALException |
||
325 | */ |
||
326 | 1 | public function prepareDeleteInToManyRelationship( |
|
327 | string $relationshipName, |
||
328 | string $identity, |
||
329 | iterable $secondaryIds |
||
330 | ): self { |
||
331 | list ($intermediateTable, $primaryKey, $secondaryKey) = |
||
332 | 1 | $this->getModelSchemas()->getBelongsToManyRelationship($this->getModelClass(), $relationshipName); |
|
333 | |||
334 | $filters = [ |
||
335 | 1 | $primaryKey => [FilterParameterInterface::OPERATION_EQUALS => [$identity]], |
|
336 | 1 | $secondaryKey => [FilterParameterInterface::OPERATION_IN => $secondaryIds], |
|
337 | ]; |
||
338 | |||
339 | 1 | $addWith = $this->expr()->andX(); |
|
340 | $this |
||
341 | 1 | ->delete($this->quoteTableName($intermediateTable)) |
|
342 | 1 | ->applyFilters($addWith, $intermediateTable, $filters); |
|
343 | |||
344 | 1 | $addWith->count() <= 0 ?: $this->andWhere($addWith); |
|
345 | |||
346 | 1 | return $this; |
|
347 | } |
||
348 | |||
349 | /** |
||
350 | * @param string $relationshipName |
||
351 | * @param string $identity |
||
352 | * |
||
353 | * @return self |
||
354 | * |
||
355 | * @throws DBALException |
||
356 | */ |
||
357 | 2 | public function clearToManyRelationship(string $relationshipName, string $identity): self |
|
358 | { |
||
359 | list ($intermediateTable, $primaryKey) = |
||
360 | 2 | $this->getModelSchemas()->getBelongsToManyRelationship($this->getModelClass(), $relationshipName); |
|
361 | |||
362 | 2 | $filters = [$primaryKey => [FilterParameterInterface::OPERATION_EQUALS => [$identity]]]; |
|
363 | 2 | $addWith = $this->expr()->andX(); |
|
364 | $this |
||
365 | 2 | ->delete($this->quoteTableName($intermediateTable)) |
|
366 | 2 | ->applyFilters($addWith, $intermediateTable, $filters); |
|
367 | |||
368 | 2 | $addWith->count() <= 0 ?: $this->andWhere($addWith); |
|
369 | |||
370 | 2 | return $this; |
|
371 | } |
||
372 | |||
373 | /** |
||
374 | * @param iterable $filters |
||
375 | * |
||
376 | * @return self |
||
377 | * |
||
378 | * @throws DBALException |
||
379 | */ |
||
380 | 9 | public function addFiltersWithAndToTable(iterable $filters): self |
|
381 | { |
||
382 | 9 | $addWith = $this->expr()->andX(); |
|
383 | 9 | $this->applyFilters($addWith, $this->getTableName(), $filters); |
|
384 | 9 | $addWith->count() <= 0 ?: $this->andWhere($addWith); |
|
385 | |||
386 | 9 | return $this; |
|
387 | } |
||
388 | |||
389 | /** |
||
390 | * @param iterable $filters |
||
391 | * |
||
392 | * @return self |
||
393 | * |
||
394 | * @throws DBALException |
||
395 | */ |
||
396 | 1 | public function addFiltersWithOrToTable(iterable $filters): self |
|
397 | { |
||
398 | 1 | $addWith = $this->expr()->orX(); |
|
399 | 1 | $this->applyFilters($addWith, $this->getTableName(), $filters); |
|
400 | 1 | $addWith->count() <= 0 ?: $this->andWhere($addWith); |
|
401 | |||
402 | 1 | return $this; |
|
403 | } |
||
404 | |||
405 | /** |
||
406 | * @param iterable $filters |
||
407 | * |
||
408 | * @return self |
||
409 | * |
||
410 | * @throws DBALException |
||
411 | */ |
||
412 | 39 | public function addFiltersWithAndToAlias(iterable $filters): self |
|
413 | { |
||
414 | 39 | $addWith = $this->expr()->andX(); |
|
415 | 39 | $this->applyFilters($addWith, $this->getAlias(), $filters); |
|
416 | 38 | $addWith->count() <= 0 ?: $this->andWhere($addWith); |
|
417 | |||
418 | 38 | return $this; |
|
419 | } |
||
420 | |||
421 | /** |
||
422 | * @param iterable $filters |
||
423 | * |
||
424 | * @return self |
||
425 | * |
||
426 | * @throws DBALException |
||
427 | */ |
||
428 | 2 | public function addFiltersWithOrToAlias(iterable $filters): self |
|
429 | { |
||
430 | 2 | $addWith = $this->expr()->orX(); |
|
431 | 2 | $this->applyFilters($addWith, $this->getAlias(), $filters); |
|
432 | 2 | $addWith->count() <= 0 ?: $this->andWhere($addWith); |
|
433 | |||
434 | 2 | return $this; |
|
435 | } |
||
436 | |||
437 | /** |
||
438 | * @param string $relationshipName |
||
439 | * @param iterable|null $relationshipFilters |
||
440 | * @param iterable|null $relationshipSorts |
||
441 | * @param int $joinIndividuals |
||
442 | * @param int $joinRelationship |
||
443 | * |
||
444 | * @return self |
||
445 | * |
||
446 | * @throws DBALException |
||
447 | * |
||
448 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
449 | * @SuppressWarnings(PHPMD.NPathComplexity) |
||
450 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||
451 | */ |
||
452 | 29 | public function addRelationshipFiltersAndSorts( |
|
453 | string $relationshipName, |
||
454 | ?iterable $relationshipFilters, |
||
455 | ?iterable $relationshipSorts, |
||
456 | int $joinIndividuals = self::AND, |
||
457 | int $joinRelationship = self::AND |
||
458 | ): self { |
||
459 | 29 | $targetAlias = null; |
|
460 | |||
461 | 29 | if ($relationshipFilters !== null) { |
|
462 | 29 | $isBelongsTo = $this->getModelSchemas() |
|
463 | 29 | ->getRelationshipType($this->getModelClass(), $relationshipName) === RelationshipTypes::BELONGS_TO; |
|
464 | |||
465 | // it will have non-null value only in a `belongsTo` relationship |
||
466 | 29 | $reversePk = $isBelongsTo === true ? |
|
467 | 29 | $this->getModelSchemas()->getReversePrimaryKey($this->getModelClass(), $relationshipName)[0] : null; |
|
468 | |||
469 | 29 | $addWith = $joinIndividuals === self::AND ? $this->expr()->andX() : $this->expr()->orX(); |
|
470 | |||
471 | 29 | foreach ($relationshipFilters as $columnName => $operationsWithArgs) { |
|
472 | 28 | if ($columnName === $reversePk) { |
|
473 | // We are applying a filter to a primary key in `belongsTo` relationship |
||
474 | // It could be replaced with a filter to a value in main table. Why might we need it? |
||
475 | // Filter could be 'IS NULL' so joining a table will not work because there are no |
||
476 | // related records with 'NULL` key. For plain values it will produce shorter SQL. |
||
477 | $fkName = |
||
478 | 15 | $this->getModelSchemas()->getForeignKey($this->getModelClass(), $relationshipName); |
|
479 | 15 | $fullColumnName = $this->getQuotedMainAliasColumn($fkName); |
|
480 | } else { |
||
481 | // Will apply filters to a joined table. |
||
482 | 19 | $targetAlias = $targetAlias ?: $this->createRelationshipAlias($relationshipName); |
|
483 | 19 | $fullColumnName = $this->buildColumnName($targetAlias, $columnName); |
|
484 | } |
||
485 | |||
486 | 28 | foreach ($operationsWithArgs as $operation => $arguments) { |
|
487 | 28 | assert( |
|
488 | 28 | is_iterable($arguments) === true || is_array($arguments) === true, |
|
489 | 28 | "Operation arguments are missing for `$columnName` column. " . |
|
490 | 28 | 'Use an empty array as an empty argument list.' |
|
491 | ); |
||
492 | 28 | $addWith->add($this->createFilterExpression($fullColumnName, $operation, $arguments)); |
|
493 | } |
||
494 | |||
495 | 28 | if ($addWith->count() > 0) { |
|
496 | 28 | $joinRelationship === self::AND ? $this->andWhere($addWith) : $this->orWhere($addWith); |
|
497 | } |
||
498 | } |
||
499 | } |
||
500 | |||
501 | 29 | if ($relationshipSorts !== null) { |
|
502 | 23 | foreach ($relationshipSorts as $columnName => $isAsc) { |
|
503 | // we join the table only once and only if we have at least one 'sort' or non-belongsToPK filter. |
||
504 | 8 | $targetAlias = $targetAlias ?: $this->createRelationshipAlias($relationshipName); |
|
505 | |||
506 | 8 | assert(is_string($columnName) === true && is_bool($isAsc) === true); |
|
507 | 8 | $fullColumnName = $this->buildColumnName($targetAlias, $columnName); |
|
508 | 8 | $this->addOrderBy($fullColumnName, $isAsc === true ? 'ASC' : 'DESC'); |
|
509 | } |
||
510 | } |
||
511 | |||
512 | 29 | return $this; |
|
513 | } |
||
514 | |||
515 | /** |
||
516 | * @param iterable $sortParameters |
||
517 | * |
||
518 | * @return self |
||
519 | */ |
||
520 | 8 | public function addSorts(iterable $sortParameters): self |
|
521 | { |
||
522 | 8 | return $this->applySorts($this->getAlias(), $sortParameters); |
|
523 | } |
||
524 | |||
525 | /** |
||
526 | * @param string $column |
||
527 | * |
||
528 | * @return string |
||
529 | */ |
||
530 | 1 | public function getQuotedMainTableColumn(string $column): string |
|
531 | { |
||
532 | 1 | return $this->buildColumnName($this->getTableName(), $column); |
|
533 | } |
||
534 | |||
535 | /** |
||
536 | * @param string $column |
||
537 | * |
||
538 | * @return string |
||
539 | */ |
||
540 | 23 | public function getQuotedMainAliasColumn(string $column): string |
|
541 | { |
||
542 | 23 | return $this->buildColumnName($this->getAlias(), $column); |
|
543 | } |
||
544 | |||
545 | /** |
||
546 | * @param string $name |
||
547 | * |
||
548 | * @return string Table alias. |
||
549 | */ |
||
550 | 21 | public function createRelationshipAlias(string $name): string |
|
551 | { |
||
552 | 21 | $relationshipType = $this->getModelSchemas()->getRelationshipType($this->getModelClass(), $name); |
|
553 | switch ($relationshipType) { |
||
554 | 21 | case RelationshipTypes::BELONGS_TO: |
|
555 | list($targetColumn, $targetTable) = |
||
556 | 4 | $this->getModelSchemas()->getReversePrimaryKey($this->getModelClass(), $name); |
|
557 | 4 | $targetAlias = $this->innerJoinOneTable( |
|
558 | 4 | $this->getAlias(), |
|
559 | 4 | $this->getModelSchemas()->getForeignKey($this->getModelClass(), $name), |
|
560 | 4 | $targetTable, |
|
561 | 4 | $targetColumn |
|
562 | ); |
||
563 | 4 | break; |
|
564 | |||
565 | 18 | case RelationshipTypes::HAS_MANY: |
|
566 | list($targetColumn, $targetTable) = |
||
567 | 13 | $this->getModelSchemas()->getReverseForeignKey($this->getModelClass(), $name); |
|
568 | 13 | $targetAlias = $this->innerJoinOneTable( |
|
569 | 13 | $this->getAlias(), |
|
570 | 13 | $this->getModelSchemas()->getPrimaryKey($this->getModelClass()), |
|
571 | 13 | $targetTable, |
|
572 | 13 | $targetColumn |
|
573 | ); |
||
574 | 13 | break; |
|
575 | |||
576 | 10 | case RelationshipTypes::BELONGS_TO_MANY: |
|
577 | default: |
||
578 | 10 | assert($relationshipType === RelationshipTypes::BELONGS_TO_MANY); |
|
579 | 10 | $primaryKey = $this->getModelSchemas()->getPrimaryKey($this->getModelClass()); |
|
580 | list ($intermediateTable, $intermediatePk, $intermediateFk) = |
||
581 | 10 | $this->getModelSchemas()->getBelongsToManyRelationship($this->getModelClass(), $name); |
|
582 | list($targetPrimaryKey, $targetTable) = |
||
583 | 10 | $this->getModelSchemas()->getReversePrimaryKey($this->getModelClass(), $name); |
|
584 | |||
585 | 10 | $targetAlias = $this->innerJoinTwoSequentialTables( |
|
586 | 10 | $this->getAlias(), |
|
587 | 10 | $primaryKey, |
|
588 | 10 | $intermediateTable, |
|
589 | 10 | $intermediatePk, |
|
590 | 10 | $intermediateFk, |
|
591 | 10 | $targetTable, |
|
592 | 10 | $targetPrimaryKey |
|
593 | ); |
||
594 | 10 | break; |
|
595 | } |
||
596 | |||
597 | 21 | return $targetAlias; |
|
598 | } |
||
599 | |||
600 | /** |
||
601 | * @return string |
||
602 | */ |
||
603 | 58 | public function getAlias(): string |
|
604 | { |
||
605 | 58 | return $this->mainAlias; |
|
606 | } |
||
607 | |||
608 | /** |
||
609 | * @param CompositeExpression $expression |
||
610 | * @param string $tableOrAlias |
||
611 | * @param iterable $filters |
||
612 | * |
||
613 | * @return self |
||
614 | * |
||
615 | * @throws DBALException |
||
616 | * @throws InvalidArgumentException |
||
617 | */ |
||
618 | 46 | public function applyFilters(CompositeExpression $expression, string $tableOrAlias, iterable $filters): self |
|
619 | { |
||
620 | 46 | foreach ($filters as $columnName => $operationsWithArgs) { |
|
621 | 46 | assert( |
|
622 | 46 | is_string($columnName) === true && empty($columnName) === false, |
|
623 | 46 | "Haven't you forgotten to specify a column name in a relationship that joins `$tableOrAlias` table?" |
|
624 | ); |
||
625 | 46 | $fullColumnName = $this->buildColumnName($tableOrAlias, $columnName); |
|
626 | 46 | foreach ($operationsWithArgs as $operation => $arguments) { |
|
627 | 46 | assert( |
|
628 | 46 | is_iterable($arguments) === true || is_array($arguments) === true, |
|
629 | 46 | "Operation arguments are missing for `$columnName` column. " . |
|
630 | 46 | 'Use an empty array as an empty argument list.' |
|
631 | ); |
||
632 | 46 | $expression->add($this->createFilterExpression($fullColumnName, $operation, $arguments)); |
|
633 | } |
||
634 | } |
||
635 | |||
636 | 45 | return $this; |
|
637 | } |
||
638 | |||
639 | /** |
||
640 | * @param string $tableOrAlias |
||
641 | * @param iterable $sorts |
||
642 | * |
||
643 | * @return self |
||
644 | */ |
||
645 | 8 | public function applySorts(string $tableOrAlias, iterable $sorts): self |
|
646 | { |
||
647 | 8 | foreach ($sorts as $columnName => $isAsc) { |
|
648 | 8 | assert(is_string($columnName) === true && is_bool($isAsc) === true); |
|
649 | 8 | $fullColumnName = $this->buildColumnName($tableOrAlias, $columnName); |
|
650 | 8 | $this->addOrderBy($fullColumnName, $isAsc === true ? 'ASC' : 'DESC'); |
|
651 | } |
||
652 | |||
653 | 8 | return $this; |
|
654 | } |
||
655 | |||
656 | /** |
||
657 | * @param string $table |
||
658 | * @param string $column |
||
659 | * |
||
660 | * @return string |
||
661 | */ |
||
662 | 64 | public function buildColumnName(string $table, string $column): string |
|
663 | { |
||
664 | 64 | return $this->quoteTableName($table) . '.' . $this->quoteColumnName($column); |
|
665 | } |
||
666 | |||
667 | /** |
||
668 | * @param $value |
||
669 | * |
||
670 | * @return string |
||
671 | * |
||
672 | * @throws DBALException |
||
673 | */ |
||
674 | 58 | public function createSingleValueNamedParameter($value): string |
|
680 | |||
681 | /** |
||
682 | * @param iterable $values |
||
683 | * |
||
684 | * @return array |
||
685 | * |
||
686 | * @throws DBALException |
||
687 | */ |
||
688 | 18 | public function createArrayValuesNamedParameter(iterable $values): array |
|
689 | { |
||
690 | 18 | $names = []; |
|
691 | |||
692 | 18 | foreach ($values as $value) { |
|
693 | 18 | $names[] = $this->createSingleValueNamedParameter($value); |
|
694 | } |
||
695 | |||
696 | 18 | return $names; |
|
697 | } |
||
698 | |||
699 | /** |
||
700 | * @param string $tableName |
||
701 | * |
||
702 | * @return string |
||
703 | */ |
||
704 | 66 | public function createAlias(string $tableName): string |
|
705 | { |
||
706 | 66 | $alias = $tableName . (++$this->aliasIdCounter); |
|
707 | 66 | $this->knownAliases[$tableName] = $alias; |
|
708 | |||
709 | 66 | return $alias; |
|
710 | } |
||
711 | |||
712 | /** |
||
713 | * @param string $fromAlias |
||
714 | * @param string $fromColumn |
||
715 | * @param string $targetTable |
||
716 | * @param string $targetColumn |
||
717 | * |
||
718 | * @return string |
||
719 | */ |
||
720 | 21 | public function innerJoinOneTable( |
|
721 | string $fromAlias, |
||
722 | string $fromColumn, |
||
723 | string $targetTable, |
||
724 | string $targetColumn |
||
725 | ): string { |
||
726 | 21 | $targetAlias = $this->createAlias($targetTable); |
|
727 | 21 | $joinCondition = $this->buildColumnName($fromAlias, $fromColumn) . '=' . |
|
728 | 21 | $this->buildColumnName($targetAlias, $targetColumn); |
|
729 | |||
730 | 21 | $this->innerJoin( |
|
731 | 21 | $this->quoteTableName($fromAlias), |
|
732 | 21 | $this->quoteTableName($targetTable), |
|
733 | 21 | $this->quoteTableName($targetAlias), |
|
734 | 21 | $joinCondition |
|
735 | ); |
||
736 | |||
737 | 21 | return $targetAlias; |
|
738 | } |
||
739 | |||
740 | /** |
||
741 | * @param string $fromAlias |
||
742 | * @param string $fromColumn |
||
743 | * @param string $intTable |
||
744 | * @param string $intToFromColumn |
||
745 | * @param string $intToTargetColumn |
||
746 | * @param string $targetTable |
||
747 | * @param string $targetColumn |
||
748 | * |
||
749 | * @return string |
||
750 | */ |
||
751 | 10 | public function innerJoinTwoSequentialTables( |
|
765 | |||
766 | /** |
||
767 | * @param string $name |
||
768 | * |
||
769 | * @return Type |
||
770 | * |
||
771 | * @throws DBALException |
||
772 | * |
||
773 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
774 | */ |
||
775 | 11 | protected function getDbalType(string $name): Type |
|
776 | { |
||
777 | 11 | assert(Type::hasType($name), "Type `$name` either do not exist or registered."); |
|
778 | 11 | $type = Type::getType($name); |
|
779 | |||
780 | 11 | return $type; |
|
781 | } |
||
782 | |||
783 | /** |
||
784 | * @return string |
||
785 | */ |
||
786 | 66 | private function getTableName(): string |
|
790 | |||
791 | /** |
||
792 | * @return ModelSchemaInfoInterface |
||
793 | */ |
||
794 | 66 | private function getModelSchemas(): ModelSchemaInfoInterface |
|
795 | { |
||
796 | 66 | return $this->modelSchemas; |
|
797 | } |
||
798 | |||
799 | /** |
||
800 | * @param string $tableName |
||
801 | * |
||
802 | * @return string |
||
803 | */ |
||
804 | 66 | private function quoteTableName(string $tableName): string |
|
805 | { |
||
806 | 66 | return $this->getConnection()->quoteIdentifier($tableName); |
|
807 | } |
||
808 | |||
809 | /** |
||
810 | * @param string $columnName |
||
811 | * |
||
812 | * @return string |
||
813 | */ |
||
814 | 66 | private function quoteColumnName(string $columnName): string |
|
818 | |||
819 | /** |
||
820 | * @param string $fullColumnName |
||
821 | * @param int $operation |
||
822 | * @param iterable $arguments |
||
823 | * |
||
824 | * @return string |
||
825 | * |
||
826 | * @throws DBALException |
||
827 | * @throws InvalidArgumentException |
||
828 | * |
||
829 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
830 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||
831 | */ |
||
832 | 59 | private function createFilterExpression(string $fullColumnName, int $operation, iterable $arguments): string |
|
833 | { |
||
834 | switch ($operation) { |
||
835 | 59 | case FilterParameterInterface::OPERATION_EQUALS: |
|
836 | 48 | $parameter = $this->createSingleValueNamedParameter($this->firstValue($arguments)); |
|
837 | 47 | $expression = $this->expr()->eq($fullColumnName, $parameter); |
|
838 | 47 | break; |
|
839 | 30 | case FilterParameterInterface::OPERATION_NOT_EQUALS: |
|
840 | 1 | $parameter = $this->createSingleValueNamedParameter($this->firstValue($arguments)); |
|
841 | 1 | $expression = $this->expr()->neq($fullColumnName, $parameter); |
|
842 | 1 | break; |
|
843 | 30 | case FilterParameterInterface::OPERATION_LESS_THAN: |
|
844 | 6 | $parameter = $this->createSingleValueNamedParameter($this->firstValue($arguments)); |
|
845 | 6 | $expression = $this->expr()->lt($fullColumnName, $parameter); |
|
846 | 6 | break; |
|
887 | |||
888 | /** |
||
889 | * @param iterable $arguments |
||
890 | * |
||
891 | * @return mixed |
||
892 | * |
||
893 | * @throws InvalidArgumentException |
||
894 | */ |
||
895 | 55 | private function firstValue(iterable $arguments) |
|
904 | |||
905 | /** |
||
906 | * @return Closure |
||
907 | */ |
||
908 | 52 | private function getColumnToDatabaseMapper(): Closure |
|
912 | |||
913 | /** |
||
914 | * @param mixed $value |
||
915 | * |
||
916 | * @return mixed |
||
917 | * |
||
918 | * @throws DBALException |
||
919 | */ |
||
920 | 58 | private function getPdoValue($value) |
|
924 | |||
925 | /** |
||
926 | * @param DateTimeInterface $dateTime |
||
927 | * |
||
928 | * @return string |
||
929 | * |
||
930 | * @throws DBALException |
||
931 | */ |
||
932 | 1 | private function convertDataTimeToDatabaseFormat(DateTimeInterface $dateTime): string |
|
939 | |||
940 | /** |
||
941 | * @param mixed $value |
||
942 | * |
||
943 | * @return int |
||
944 | * |
||
945 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
946 | */ |
||
947 | 58 | private function getPdoType($value): int |
|
967 | |||
968 | /** |
||
969 | * @return Type |
||
970 | * |
||
971 | * @throws DBALException |
||
972 | * |
||
973 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
974 | */ |
||
975 | 1 | private function getDateTimeType(): Type |
|
983 | } |
||
984 |
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.