Complex classes like Crud 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 Crud, and based on these observations, apply Extract Interface, too.
| 1 | <?php namespace Limoncello\Flute\Api; |
||
| 57 | class Crud implements CrudInterface |
||
| 58 | { |
||
| 59 | use HasContainerTrait; |
||
| 60 | |||
| 61 | /** Internal constant. Query param name. */ |
||
| 62 | protected const INDEX_BIND = ':index'; |
||
| 63 | |||
| 64 | /** Internal constant. Query param name. */ |
||
| 65 | protected const CHILD_INDEX_BIND = ':childIndex'; |
||
| 66 | |||
| 67 | /** Internal constant. Path constant. */ |
||
| 68 | protected const ROOT_PATH = ''; |
||
| 69 | |||
| 70 | /** Internal constant. Path constant. */ |
||
| 71 | protected const PATH_SEPARATOR = DocumentInterface::PATH_SEPARATOR; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * @var FactoryInterface |
||
| 75 | */ |
||
| 76 | private $factory; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * @var string |
||
| 80 | */ |
||
| 81 | private $modelClass; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @var RepositoryInterface |
||
| 85 | */ |
||
| 86 | private $repository; |
||
| 87 | |||
| 88 | /** |
||
| 89 | * @var ModelSchemeInfoInterface |
||
| 90 | */ |
||
| 91 | private $modelSchemes; |
||
| 92 | |||
| 93 | /** |
||
| 94 | * @var PaginationStrategyInterface |
||
| 95 | */ |
||
| 96 | private $paginationStrategy; |
||
| 97 | |||
| 98 | /** |
||
| 99 | * @param ContainerInterface $container |
||
| 100 | * @param string $modelClass |
||
| 101 | */ |
||
| 102 | 46 | public function __construct(ContainerInterface $container, string $modelClass) |
|
| 103 | { |
||
| 104 | 46 | $this->setContainer($container); |
|
| 105 | |||
| 106 | 46 | $this->factory = $this->getContainer()->get(FactoryInterface::class); |
|
| 107 | 46 | $this->modelClass = $modelClass; |
|
| 108 | 46 | $this->repository = $this->getContainer()->get(RepositoryInterface::class); |
|
| 109 | 46 | $this->modelSchemes = $this->getContainer()->get(ModelSchemeInfoInterface::class); |
|
| 110 | 46 | $this->paginationStrategy = $this->getContainer()->get(PaginationStrategyInterface::class); |
|
| 111 | } |
||
| 112 | |||
| 113 | /** |
||
| 114 | * @inheritdoc |
||
| 115 | */ |
||
| 116 | 16 | public function index( |
|
| 117 | FilterParameterCollection $filterParams = null, |
||
| 118 | array $sortParams = null, |
||
| 119 | array $includePaths = null, |
||
| 120 | array $pagingParams = null |
||
| 121 | ): ModelsDataInterface { |
||
| 122 | 16 | $modelClass = $this->getModelClass(); |
|
| 123 | |||
| 124 | 16 | $builder = $this->getRepository()->index($modelClass); |
|
| 125 | |||
| 126 | 16 | $errors = $this->getFactory()->createErrorCollection(); |
|
| 127 | 16 | $filterParams === null ?: $this->getRepository()->applyFilters($errors, $builder, $modelClass, $filterParams); |
|
| 128 | 16 | $this->checkErrors($errors); |
|
| 129 | 15 | $sortParams === null ?: $this->getRepository()->applySorting($builder, $modelClass, $sortParams); |
|
| 130 | |||
| 131 | 15 | list($offset, $limit) = $this->getPaginationStrategy()->parseParameters($pagingParams); |
|
| 132 | 15 | $builder->setFirstResult($offset)->setMaxResults($limit); |
|
| 133 | |||
| 134 | 15 | $data = $this->fetchCollectionData($this->builderOnIndex($builder), $modelClass, $limit, $offset); |
|
| 135 | |||
| 136 | 15 | $relationships = null; |
|
| 137 | 15 | if ($data->getData() !== null && $includePaths !== null) { |
|
| 138 | 4 | $relationships = $this->readRelationships($data, $includePaths); |
|
| 139 | } |
||
| 140 | |||
| 141 | 15 | $result = $this->getFactory()->createModelsData($data, $relationships); |
|
| 142 | |||
| 143 | 15 | return $result; |
|
| 144 | } |
||
| 145 | |||
| 146 | /** |
||
| 147 | * @inheritdoc |
||
| 148 | */ |
||
| 149 | 1 | public function indexResources(FilterParameterCollection $filterParams = null, array $sortParams = null): array |
|
| 150 | { |
||
| 151 | 1 | $modelClass = $this->getModelClass(); |
|
| 152 | |||
| 153 | 1 | $builder = $this->getRepository()->index($modelClass); |
|
| 154 | |||
| 155 | 1 | $errors = $this->getFactory()->createErrorCollection(); |
|
| 156 | 1 | $filterParams === null ?: $this->getRepository()->applyFilters($errors, $builder, $modelClass, $filterParams); |
|
| 157 | 1 | $this->checkErrors($errors); |
|
| 158 | 1 | $sortParams === null ?: $this->getRepository()->applySorting($builder, $modelClass, $sortParams); |
|
| 159 | |||
| 160 | 1 | list($models) = $this->fetchCollection($this->builderOnIndex($builder), $modelClass); |
|
| 161 | |||
| 162 | 1 | return $models; |
|
| 163 | } |
||
| 164 | |||
| 165 | /** |
||
| 166 | * @inheritdoc |
||
| 167 | */ |
||
| 168 | 1 | public function count(FilterParameterCollection $filterParams = null): ?int |
|
| 169 | { |
||
| 170 | 1 | $modelClass = $this->getModelClass(); |
|
| 171 | |||
| 172 | 1 | $builder = $this->getRepository()->count($modelClass); |
|
| 173 | |||
| 174 | 1 | $errors = $this->getFactory()->createErrorCollection(); |
|
| 175 | 1 | $filterParams === null ?: $this->getRepository()->applyFilters($errors, $builder, $modelClass, $filterParams); |
|
| 176 | 1 | $this->checkErrors($errors); |
|
| 177 | |||
| 178 | 1 | $result = $this->builderOnCount($builder)->execute()->fetchColumn(); |
|
| 179 | |||
| 180 | 1 | return $result === false ? null : $result; |
|
| 181 | } |
||
| 182 | |||
| 183 | /** |
||
| 184 | * @inheritdoc |
||
| 185 | */ |
||
| 186 | 11 | public function read( |
|
| 187 | $index, |
||
| 188 | FilterParameterCollection $filterParams = null, |
||
| 189 | array $includePaths = null |
||
| 190 | ): ModelsDataInterface { |
||
| 191 | 11 | $model = $this->readResource($index, $filterParams); |
|
| 192 | 10 | $data = $this->getFactory()->createPaginatedData($model); |
|
| 193 | |||
| 194 | 10 | $relationships = null; |
|
| 195 | 10 | if ($data->getData() !== null && $includePaths !== null) { |
|
| 196 | 4 | $relationships = $this->readRelationships($data, $includePaths); |
|
| 197 | } |
||
| 198 | |||
| 199 | 10 | $result = $this->getFactory()->createModelsData($data, $relationships); |
|
| 200 | |||
| 201 | 10 | return $result; |
|
| 202 | } |
||
| 203 | |||
| 204 | /** |
||
| 205 | * @inheritdoc |
||
| 206 | */ |
||
| 207 | 11 | public function readResource($index, FilterParameterCollection $filterParams = null) |
|
| 208 | { |
||
| 209 | 11 | if ($index !== null && is_scalar($index) === false) { |
|
| 210 | 1 | throw new InvalidArgumentException( |
|
| 211 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 212 | ); |
||
| 213 | } |
||
| 214 | |||
| 215 | 10 | $modelClass = $this->getModelClass(); |
|
| 216 | |||
| 217 | 10 | $builder = $this->getRepository() |
|
| 218 | 10 | ->read($modelClass, static::INDEX_BIND) |
|
| 219 | 10 | ->setParameter(static::INDEX_BIND, $index); |
|
| 220 | |||
| 221 | 10 | $errors = $this->getFactory()->createErrorCollection(); |
|
| 222 | 10 | $filterParams === null ?: $this->getRepository()->applyFilters($errors, $builder, $modelClass, $filterParams); |
|
| 223 | 10 | $this->checkErrors($errors); |
|
| 224 | |||
| 225 | 10 | $model = $this->fetchSingle($this->builderOnRead($builder), $modelClass); |
|
| 226 | |||
| 227 | 10 | return $model; |
|
| 228 | } |
||
| 229 | |||
| 230 | /** |
||
| 231 | * @inheritdoc |
||
| 232 | * |
||
| 233 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
| 234 | */ |
||
| 235 | 5 | public function readRelationship( |
|
| 275 | |||
| 276 | /** |
||
| 277 | * @inheritdoc |
||
| 278 | */ |
||
| 279 | 6 | public function hasInRelationship($parentId, string $name, $childId): bool |
|
| 280 | { |
||
| 281 | 6 | if ($parentId !== null && is_scalar($parentId) === false) { |
|
| 282 | 1 | throw new InvalidArgumentException( |
|
| 283 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 284 | ); |
||
| 285 | } |
||
| 286 | 5 | if ($childId !== null && is_scalar($childId) === false) { |
|
| 287 | 1 | throw new InvalidArgumentException( |
|
| 288 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 289 | ); |
||
| 290 | } |
||
| 291 | |||
| 292 | 4 | $modelClass = $this->getModelClass(); |
|
| 293 | |||
| 294 | /** @var QueryBuilder $builder */ |
||
| 295 | 4 | list ($builder) = $this->getRepository() |
|
| 296 | 4 | ->hasInRelationship($modelClass, static::INDEX_BIND, $name, static::CHILD_INDEX_BIND); |
|
| 297 | |||
| 298 | 4 | $builder->setParameter(static::INDEX_BIND, $parentId); |
|
| 299 | 4 | $builder->setParameter(static::CHILD_INDEX_BIND, $childId); |
|
| 300 | |||
| 301 | 4 | $result = $builder->execute()->fetch(); |
|
| 302 | |||
| 303 | 4 | return $result !== false; |
|
| 304 | } |
||
| 305 | |||
| 306 | /** |
||
| 307 | * @inheritdoc |
||
| 308 | */ |
||
| 309 | 2 | public function readRow($index): ?array |
|
| 310 | { |
||
| 311 | 2 | if ($index !== null && is_scalar($index) === false) { |
|
| 312 | 1 | throw new InvalidArgumentException( |
|
| 313 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 314 | ); |
||
| 315 | } |
||
| 316 | |||
| 317 | 1 | $modelClass = $this->getModelClass(); |
|
| 318 | 1 | $builder = $this->getRepository() |
|
| 319 | 1 | ->read($modelClass, static::INDEX_BIND) |
|
| 320 | 1 | ->setParameter(static::INDEX_BIND, $index); |
|
| 321 | 1 | $typedRow = $this->fetchRow($builder, $modelClass); |
|
| 322 | |||
| 323 | 1 | return $typedRow; |
|
| 324 | } |
||
| 325 | |||
| 326 | /** |
||
| 327 | * @inheritdoc |
||
| 328 | */ |
||
| 329 | 6 | public function delete($index): int |
|
| 330 | { |
||
| 331 | 6 | if ($index !== null && is_scalar($index) === false) { |
|
| 332 | 1 | throw new InvalidArgumentException( |
|
| 333 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 334 | ); |
||
| 335 | } |
||
| 336 | |||
| 337 | 5 | $modelClass = $this->getModelClass(); |
|
| 338 | |||
| 339 | 5 | $builder = $this->builderOnDelete( |
|
| 340 | 5 | $this->getRepository()->delete($modelClass, static::INDEX_BIND)->setParameter(static::INDEX_BIND, $index) |
|
| 341 | ); |
||
| 342 | |||
| 343 | 5 | $deleted = $builder->execute(); |
|
| 344 | |||
| 345 | 4 | return (int)$deleted; |
|
| 346 | } |
||
| 347 | |||
| 348 | /** |
||
| 349 | * @inheritdoc |
||
| 350 | */ |
||
| 351 | 5 | public function create($index, array $attributes, array $toMany = []): string |
|
| 352 | { |
||
| 353 | 5 | if ($index !== null && is_scalar($index) === false) { |
|
| 354 | 1 | throw new InvalidArgumentException( |
|
| 355 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 356 | ); |
||
| 357 | } |
||
| 358 | |||
| 359 | 4 | $modelClass = $this->getModelClass(); |
|
| 360 | |||
| 361 | 4 | $allowedChanges = $this->filterAttributesOnCreate($modelClass, $attributes, $index); |
|
| 362 | |||
| 363 | 4 | $saveMain = $this->getRepository()->create($modelClass, $allowedChanges); |
|
| 364 | 4 | $saveMain = $this->builderSaveResourceOnCreate($saveMain); |
|
| 365 | 4 | $saveMain->getSQL(); // prepare |
|
| 366 | 4 | $this->inTransaction(function () use ($modelClass, $saveMain, $toMany, &$index) { |
|
| 367 | 4 | $saveMain->execute(); |
|
| 368 | // if no index given will use last insert ID as index |
||
| 369 | 4 | $index !== null ?: $index = $saveMain->getConnection()->lastInsertId(); |
|
| 370 | 4 | foreach ($toMany as $name => $values) { |
|
| 371 | 2 | $indexBind = ':index'; |
|
| 372 | 2 | $otherIndexBind = ':otherIndex'; |
|
| 373 | 2 | $saveToMany = $this->getRepository() |
|
| 374 | 2 | ->createToManyRelationship($modelClass, $indexBind, $name, $otherIndexBind); |
|
| 375 | 2 | $saveToMany = $this->builderSaveRelationshipOnCreate($name, $saveToMany); |
|
| 376 | 2 | $saveToMany->setParameter($indexBind, $index); |
|
| 377 | 2 | foreach ($values as $value) { |
|
| 378 | 2 | $saveToMany->setParameter($otherIndexBind, $value)->execute(); |
|
| 379 | } |
||
| 380 | } |
||
| 381 | 4 | }); |
|
| 382 | |||
| 383 | 4 | return $index; |
|
| 384 | } |
||
| 385 | |||
| 386 | /** |
||
| 387 | * @inheritdoc |
||
| 388 | */ |
||
| 389 | 4 | public function update($index, array $attributes, array $toMany = []): int |
|
| 390 | { |
||
| 391 | 4 | if ($index !== null && is_scalar($index) === false) { |
|
| 392 | 1 | throw new InvalidArgumentException( |
|
| 393 | 1 | $this->createMessageFormatter()->formatMessage(Messages::MSG_ERR_INVALID_ARGUMENT) |
|
| 394 | ); |
||
| 395 | } |
||
| 396 | |||
| 397 | 3 | $updated = 0; |
|
| 398 | 3 | $modelClass = $this->getModelClass(); |
|
| 399 | |||
| 400 | 3 | $allowedChanges = $this->filterAttributesOnUpdate($modelClass, $attributes); |
|
| 401 | |||
| 402 | 3 | $saveMain = $this->getRepository()->update($modelClass, $index, $allowedChanges); |
|
| 403 | 3 | $saveMain = $this->builderSaveResourceOnUpdate($saveMain); |
|
| 404 | 3 | $saveMain->getSQL(); // prepare |
|
| 405 | 3 | $this->inTransaction(function () use ($modelClass, $saveMain, $toMany, $index, &$updated) { |
|
| 406 | 3 | $updated = $saveMain->execute(); |
|
| 407 | 3 | foreach ($toMany as $name => $values) { |
|
| 408 | 2 | $indexBind = ':index'; |
|
| 409 | 2 | $otherIndexBind = ':otherIndex'; |
|
| 410 | |||
| 411 | 2 | $cleanToMany = $this->getRepository()->cleanToManyRelationship($modelClass, $indexBind, $name); |
|
| 412 | 2 | $cleanToMany = $this->builderCleanRelationshipOnUpdate($name, $cleanToMany); |
|
| 413 | 2 | $cleanToMany->setParameter($indexBind, $index)->execute(); |
|
| 414 | |||
| 415 | 2 | $saveToMany = $this->getRepository() |
|
| 416 | 2 | ->createToManyRelationship($modelClass, $indexBind, $name, $otherIndexBind); |
|
| 417 | 2 | $saveToMany = $this->builderSaveRelationshipOnUpdate($name, $saveToMany); |
|
| 418 | 2 | $saveToMany->setParameter($indexBind, $index); |
|
| 419 | 2 | foreach ($values as $value) { |
|
| 420 | 2 | $updated += (int)$saveToMany->setParameter($otherIndexBind, $value)->execute(); |
|
| 421 | } |
||
| 422 | } |
||
| 423 | 3 | }); |
|
| 424 | |||
| 425 | 3 | return (int)$updated; |
|
| 426 | } |
||
| 427 | |||
| 428 | /** |
||
| 429 | * @return FactoryInterface |
||
| 430 | */ |
||
| 431 | 32 | protected function getFactory(): FactoryInterface |
|
| 435 | |||
| 436 | /** |
||
| 437 | * @return string |
||
| 438 | */ |
||
| 439 | 38 | protected function getModelClass(): string |
|
| 440 | { |
||
| 441 | 38 | return $this->modelClass; |
|
| 442 | } |
||
| 443 | |||
| 444 | /** |
||
| 445 | * @return RepositoryInterface |
||
| 446 | */ |
||
| 447 | 38 | protected function getRepository(): RepositoryInterface |
|
| 448 | { |
||
| 449 | 38 | return $this->repository; |
|
| 450 | } |
||
| 451 | |||
| 452 | /** |
||
| 453 | * @return ModelSchemeInfoInterface |
||
| 454 | */ |
||
| 455 | 31 | protected function getModelSchemes(): ModelSchemeInfoInterface |
|
| 456 | { |
||
| 457 | 31 | return $this->modelSchemes; |
|
| 458 | } |
||
| 459 | |||
| 460 | /** |
||
| 461 | * @return PaginationStrategyInterface |
||
| 462 | */ |
||
| 463 | 21 | protected function getPaginationStrategy(): PaginationStrategyInterface |
|
| 464 | { |
||
| 465 | 21 | return $this->paginationStrategy; |
|
| 466 | } |
||
| 467 | |||
| 468 | /** |
||
| 469 | * @param Closure $closure |
||
| 470 | * |
||
| 471 | * @return void |
||
| 472 | */ |
||
| 473 | 7 | protected function inTransaction(Closure $closure): void |
|
| 474 | { |
||
| 475 | 7 | $connection = $this->getRepository()->getConnection(); |
|
| 476 | 7 | $connection->beginTransaction(); |
|
| 477 | try { |
||
| 478 | 7 | $isOk = ($closure() === false ? null : true); |
|
| 479 | 7 | } finally { |
|
| 480 | 7 | isset($isOk) === true ? $connection->commit() : $connection->rollBack(); |
|
| 481 | } |
||
| 482 | } |
||
| 483 | |||
| 484 | /** |
||
| 485 | * @param QueryBuilder $builder |
||
| 486 | * @param string $class |
||
| 487 | * |
||
| 488 | * @return PaginatedDataInterface |
||
| 489 | */ |
||
| 490 | 1 | protected function fetchSingleData(QueryBuilder $builder, string $class): PaginatedDataInterface |
|
| 491 | { |
||
| 492 | 1 | $model = $this->fetchSingle($builder, $class); |
|
| 493 | 1 | $data = $this->getFactory()->createPaginatedData($model)->markAsSingleItem(); |
|
| 494 | |||
| 495 | 1 | return $data; |
|
| 496 | } |
||
| 497 | |||
| 498 | /** |
||
| 499 | * @param QueryBuilder $builder |
||
| 500 | * @param string $class |
||
| 501 | * @param int $limit |
||
| 502 | * @param int $offset |
||
| 503 | * |
||
| 504 | * @return PaginatedDataInterface |
||
| 505 | */ |
||
| 506 | 18 | protected function fetchCollectionData( |
|
| 523 | |||
| 524 | /** |
||
| 525 | * @param QueryBuilder $builder |
||
| 526 | * @param string $class |
||
| 527 | * |
||
| 528 | * @return array|null |
||
| 529 | */ |
||
| 530 | 1 | protected function fetchRow(QueryBuilder $builder, string $class): ?array |
|
| 531 | { |
||
| 532 | 1 | $statement = $builder->execute(); |
|
| 533 | 1 | $statement->setFetchMode(PDOConnection::FETCH_ASSOC); |
|
| 534 | 1 | $platform = $builder->getConnection()->getDatabasePlatform(); |
|
| 535 | 1 | $typeNames = $this->getModelSchemes()->getAttributeTypes($class); |
|
| 536 | |||
| 537 | 1 | $model = null; |
|
| 538 | 1 | if (($attributes = $statement->fetch()) !== false) { |
|
| 539 | 1 | $model = $this->readRowFromAssoc($attributes, $typeNames, $platform); |
|
| 540 | } |
||
| 541 | |||
| 542 | 1 | return $model; |
|
| 543 | } |
||
| 544 | |||
| 545 | /** |
||
| 546 | * @param QueryBuilder $builder |
||
| 547 | * @param string $class |
||
| 548 | * |
||
| 549 | * @return mixed|null |
||
| 550 | */ |
||
| 551 | 14 | protected function fetchSingle(QueryBuilder $builder, string $class) |
|
| 552 | { |
||
| 553 | 14 | $statement = $builder->execute(); |
|
| 554 | 14 | $statement->setFetchMode(PDOConnection::FETCH_ASSOC); |
|
| 555 | 14 | $platform = $builder->getConnection()->getDatabasePlatform(); |
|
| 556 | 14 | $typeNames = $this->getModelSchemes()->getAttributeTypes($class); |
|
| 557 | |||
| 558 | 14 | $model = null; |
|
| 559 | 14 | if (($attributes = $statement->fetch()) !== false) { |
|
| 560 | 14 | $model = $this->readInstanceFromAssoc($class, $attributes, $typeNames, $platform); |
|
| 561 | } |
||
| 562 | |||
| 563 | 14 | return $model; |
|
| 564 | } |
||
| 565 | |||
| 566 | /** |
||
| 567 | * @param QueryBuilder $builder |
||
| 568 | * @param string $class |
||
| 569 | * @param int|string|null $offset |
||
| 570 | * @param int|string|null $limit |
||
| 571 | * |
||
| 572 | * @return array |
||
| 573 | */ |
||
| 574 | 22 | protected function fetchCollection(QueryBuilder $builder, string $class, $limit = null, $offset = null): array |
|
| 575 | { |
||
| 576 | 22 | $statement = $builder->execute(); |
|
| 577 | 22 | $statement->setFetchMode(PDOConnection::FETCH_ASSOC); |
|
| 578 | 22 | $platform = $builder->getConnection()->getDatabasePlatform(); |
|
| 579 | 22 | $typeNames = $this->getModelSchemes()->getAttributeTypes($class); |
|
| 580 | |||
| 581 | 22 | $models = []; |
|
| 582 | 22 | while (($attributes = $statement->fetch()) !== false) { |
|
| 583 | 22 | $models[] = $this->readInstanceFromAssoc($class, $attributes, $typeNames, $platform); |
|
| 584 | } |
||
| 585 | |||
| 586 | 22 | return $this->normalizePagingParams($models, $limit, $offset); |
|
| 587 | } |
||
| 588 | |||
| 589 | /** |
||
| 590 | * @param string $modelClass |
||
| 591 | * @param array $attributes |
||
| 592 | * @param null|string $index |
||
| 593 | * |
||
| 594 | * @return array |
||
| 595 | */ |
||
| 596 | 4 | protected function filterAttributesOnCreate(string $modelClass, array $attributes, string $index = null): array |
|
| 597 | { |
||
| 598 | 4 | $allowedAttributes = array_flip($this->getModelSchemes()->getAttributes($modelClass)); |
|
| 599 | 4 | $allowedChanges = array_intersect_key($attributes, $allowedAttributes); |
|
| 600 | 4 | if ($index !== null) { |
|
| 601 | 1 | $pkName = $this->getModelSchemes()->getPrimaryKey($this->getModelClass()); |
|
| 602 | 1 | $allowedChanges[$pkName] = $index; |
|
| 603 | } |
||
| 604 | |||
| 605 | 4 | return $allowedChanges; |
|
| 606 | } |
||
| 607 | |||
| 608 | /** |
||
| 609 | * @param string $modelClass |
||
| 610 | * @param array $attributes |
||
| 611 | * |
||
| 612 | * @return array |
||
| 613 | */ |
||
| 614 | 3 | protected function filterAttributesOnUpdate(string $modelClass, array $attributes): array |
|
| 615 | { |
||
| 616 | 3 | $allowedAttributes = array_flip($this->getModelSchemes()->getAttributes($modelClass)); |
|
| 617 | 3 | $allowedChanges = array_intersect_key($attributes, $allowedAttributes); |
|
| 618 | |||
| 619 | 3 | return $allowedChanges; |
|
| 620 | } |
||
| 621 | |||
| 622 | /** |
||
| 623 | * @param QueryBuilder $builder |
||
| 624 | * |
||
| 625 | * @return QueryBuilder |
||
| 626 | */ |
||
| 627 | 1 | protected function builderOnCount(QueryBuilder $builder): QueryBuilder |
|
| 628 | { |
||
| 629 | 1 | return $builder; |
|
| 630 | } |
||
| 631 | |||
| 632 | /** |
||
| 633 | * @param QueryBuilder $builder |
||
| 634 | * |
||
| 635 | * @return QueryBuilder |
||
| 636 | */ |
||
| 637 | 16 | protected function builderOnIndex(QueryBuilder $builder): QueryBuilder |
|
| 638 | { |
||
| 639 | 16 | return $builder; |
|
| 640 | } |
||
| 641 | |||
| 642 | /** |
||
| 643 | * @param QueryBuilder $builder |
||
| 644 | * |
||
| 645 | * @return QueryBuilder |
||
| 646 | */ |
||
| 647 | 10 | protected function builderOnRead(QueryBuilder $builder): QueryBuilder |
|
| 648 | { |
||
| 649 | 10 | return $builder; |
|
| 650 | } |
||
| 651 | |||
| 652 | /** |
||
| 653 | * @param QueryBuilder $builder |
||
| 654 | * |
||
| 655 | * @return QueryBuilder |
||
| 656 | */ |
||
| 657 | 4 | protected function builderOnReadRelationship(QueryBuilder $builder): QueryBuilder |
|
| 658 | { |
||
| 659 | 4 | return $builder; |
|
| 660 | } |
||
| 661 | |||
| 662 | /** |
||
| 663 | * @param QueryBuilder $builder |
||
| 664 | * |
||
| 665 | * @return QueryBuilder |
||
| 666 | */ |
||
| 667 | 4 | protected function builderSaveResourceOnCreate(QueryBuilder $builder): QueryBuilder |
|
| 668 | { |
||
| 669 | 4 | return $builder; |
|
| 670 | } |
||
| 671 | |||
| 672 | /** |
||
| 673 | * @param QueryBuilder $builder |
||
| 674 | * |
||
| 675 | * @return QueryBuilder |
||
| 676 | */ |
||
| 677 | 3 | protected function builderSaveResourceOnUpdate(QueryBuilder $builder): QueryBuilder |
|
| 678 | { |
||
| 679 | 3 | return $builder; |
|
| 680 | } |
||
| 681 | |||
| 682 | /** |
||
| 683 | * @param string $relationshipName |
||
| 684 | * @param QueryBuilder $builder |
||
| 685 | * |
||
| 686 | * @return QueryBuilder |
||
| 687 | * |
||
| 688 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
||
| 689 | */ |
||
| 690 | 2 | protected function builderSaveRelationshipOnCreate(/** @noinspection PhpUnusedParameterInspection */ |
|
| 696 | |||
| 697 | /** |
||
| 698 | * @param string $relationshipName |
||
| 699 | * @param QueryBuilder $builder |
||
| 700 | * |
||
| 701 | * @return QueryBuilder |
||
| 702 | * |
||
| 703 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
||
| 704 | */ |
||
| 705 | 2 | protected function builderSaveRelationshipOnUpdate(/** @noinspection PhpUnusedParameterInspection */ |
|
| 706 | $relationshipName, |
||
| 707 | QueryBuilder $builder |
||
| 708 | ): QueryBuilder { |
||
| 709 | 2 | return $builder; |
|
| 710 | } |
||
| 711 | |||
| 712 | /** |
||
| 713 | * @param string $relationshipName |
||
| 714 | * @param QueryBuilder $builder |
||
| 715 | * |
||
| 716 | * @return QueryBuilder |
||
| 717 | * |
||
| 718 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
||
| 719 | */ |
||
| 720 | 2 | protected function builderCleanRelationshipOnUpdate(/** @noinspection PhpUnusedParameterInspection */ |
|
| 721 | $relationshipName, |
||
| 722 | QueryBuilder $builder |
||
| 723 | ): QueryBuilder { |
||
| 724 | 2 | return $builder; |
|
| 725 | } |
||
| 726 | |||
| 727 | /** |
||
| 728 | * @param QueryBuilder $builder |
||
| 729 | * |
||
| 730 | * @return QueryBuilder |
||
| 731 | */ |
||
| 732 | 5 | protected function builderOnDelete(QueryBuilder $builder): QueryBuilder |
|
| 733 | { |
||
| 734 | 5 | return $builder; |
|
| 735 | } |
||
| 736 | |||
| 737 | /** |
||
| 738 | * @param PaginatedDataInterface $data |
||
| 739 | * @param IncludeParameterInterface[] $paths |
||
| 740 | * |
||
| 741 | * @return RelationshipStorageInterface |
||
| 742 | * |
||
| 743 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
| 744 | */ |
||
| 745 | 8 | protected function readRelationships(PaginatedDataInterface $data, array $paths): RelationshipStorageInterface |
|
| 746 | { |
||
| 747 | 8 | $result = $this->getFactory()->createRelationshipStorage(); |
|
| 748 | |||
| 749 | 8 | if (empty($data->getData()) === false && empty($paths) === false) { |
|
| 750 | 8 | $modelStorage = $this->getFactory()->createModelStorage($this->getModelSchemes()); |
|
| 751 | 8 | $modelsAtPath = $this->getFactory()->createTagStorage(); |
|
| 752 | |||
| 753 | // we gonna send this storage via function params so it is an equivalent for &array |
||
| 754 | 8 | $classAtPath = new ArrayObject(); |
|
| 755 | |||
| 756 | 8 | $model = null; |
|
| 757 | 8 | if ($data->isCollection() === true) { |
|
| 758 | 4 | foreach ($data->getData() as $model) { |
|
| 759 | 4 | $uniqueModel = $modelStorage->register($model); |
|
| 760 | 4 | if ($uniqueModel !== null) { |
|
| 761 | 4 | $modelsAtPath->register($uniqueModel, static::ROOT_PATH); |
|
| 762 | } |
||
| 763 | } |
||
| 764 | } else { |
||
| 765 | 4 | $model = $data->getData(); |
|
| 766 | 4 | $uniqueModel = $modelStorage->register($model); |
|
| 767 | 4 | if ($uniqueModel !== null) { |
|
| 768 | 4 | $modelsAtPath->register($uniqueModel, static::ROOT_PATH); |
|
| 769 | } |
||
| 770 | } |
||
| 771 | 8 | $classAtPath[static::ROOT_PATH] = get_class($model); |
|
| 772 | |||
| 773 | 8 | foreach ($this->getPaths($paths) as list ($parentPath, $childPaths)) { |
|
| 774 | 8 | $this->loadRelationshipsLayer( |
|
| 775 | 8 | $result, |
|
| 776 | 8 | $modelsAtPath, |
|
| 777 | 8 | $classAtPath, |
|
| 778 | 8 | $modelStorage, |
|
| 779 | 8 | $parentPath, |
|
| 780 | 8 | $childPaths |
|
| 781 | ); |
||
| 782 | } |
||
| 783 | } |
||
| 784 | |||
| 785 | 8 | return $result; |
|
| 786 | } |
||
| 787 | |||
| 788 | /** |
||
| 789 | * @param array $models |
||
| 790 | * @param int|string|null $offset |
||
| 791 | * @param int|string|null $limit |
||
| 792 | * |
||
| 793 | * @return array |
||
| 794 | * |
||
| 795 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
| 796 | */ |
||
| 797 | 22 | private function normalizePagingParams(array $models, $limit, $offset): array |
|
| 810 | |||
| 811 | /** |
||
| 812 | * @param ErrorCollection $errors |
||
| 813 | * |
||
| 814 | * @return void |
||
| 815 | */ |
||
| 816 | 32 | private function checkErrors(ErrorCollection $errors): void |
|
| 817 | { |
||
| 818 | 32 | if (empty($errors->getArrayCopy()) === false) { |
|
| 819 | 1 | throw new E($errors); |
|
| 820 | } |
||
| 821 | } |
||
| 822 | |||
| 823 | /** |
||
| 824 | * @param IncludeParameterInterface[] $paths |
||
| 825 | * |
||
| 826 | * @return Generator |
||
| 827 | */ |
||
| 828 | 8 | private function getPaths(array $paths): Generator |
|
| 829 | { |
||
| 830 | // The idea is to normalize paths. It means build all intermediate paths. |
||
| 831 | // e.g. if only `a.b.c` path it given it will be normalized to `a`, `a.b` and `a.b.c`. |
||
| 832 | // Path depths store depth of each path (e.g. 0 for root, 1 for `a`, 2 for `a.b` and etc). |
||
| 833 | // It is needed for yielding them in correct order (from top level to bottom). |
||
| 834 | 8 | $normalizedPaths = []; |
|
| 835 | 8 | $pathsDepths = []; |
|
| 836 | 8 | foreach ($paths as $path) { |
|
| 837 | 8 | $parentDepth = 0; |
|
| 838 | 8 | $tmpPath = static::ROOT_PATH; |
|
| 839 | 8 | foreach ($path->getPath() as $pathPiece) { |
|
| 840 | 8 | $parent = $tmpPath; |
|
| 841 | 8 | $tmpPath = empty($tmpPath) === true ? |
|
| 842 | 8 | $pathPiece : $tmpPath . static::PATH_SEPARATOR . $pathPiece; |
|
| 843 | 8 | $normalizedPaths[$tmpPath] = [$parent, $pathPiece]; |
|
| 863 | |||
| 864 | /** |
||
| 865 | * @param RelationshipStorageInterface $result |
||
| 866 | * @param TagStorageInterface $modelsAtPath |
||
| 867 | * @param ArrayObject $classAtPath |
||
| 868 | * @param ModelStorageInterface $deDup |
||
| 869 | * @param string $parentsPath |
||
| 870 | * @param array $childRelationships |
||
| 871 | * |
||
| 872 | * @return void |
||
| 873 | * |
||
| 874 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||
| 875 | */ |
||
| 876 | 8 | protected function loadRelationshipsLayer( |
|
| 937 | |||
| 938 | /** |
||
| 939 | * @param string $namespace |
||
| 940 | * |
||
| 941 | * @return FormatterInterface |
||
| 942 | */ |
||
| 943 | 8 | protected function createMessageFormatter(string $namespace = Messages::RESOURCES_NAMESPACE): FormatterInterface |
|
| 951 | |||
| 952 | /** |
||
| 953 | * @param string $class |
||
| 954 | * @param array $attributes |
||
| 955 | * @param Type[] $typeNames |
||
| 956 | * @param AbstractPlatform $platform |
||
| 957 | * |
||
| 958 | * @return mixed|null |
||
| 959 | * |
||
| 960 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
| 961 | */ |
||
| 962 | 30 | private function readInstanceFromAssoc( |
|
| 979 | |||
| 980 | /** |
||
| 981 | * @param array $attributes |
||
| 982 | * @param Type[] $typeNames |
||
| 983 | * @param AbstractPlatform $platform |
||
| 984 | * |
||
| 985 | * @return array |
||
| 986 | * |
||
| 987 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
| 988 | */ |
||
| 989 | 1 | private function readRowFromAssoc(array $attributes, array $typeNames, AbstractPlatform $platform): array |
|
| 1002 | } |
||
| 1003 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.