Complex classes like Handler 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 Handler, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class Handler implements BaseContentHandler |
||
32 | { |
||
33 | /** |
||
34 | * Content gateway. |
||
35 | * |
||
36 | * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway |
||
37 | */ |
||
38 | protected $contentGateway; |
||
39 | |||
40 | /** |
||
41 | * Location gateway. |
||
42 | * |
||
43 | * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway |
||
44 | */ |
||
45 | protected $locationGateway; |
||
46 | |||
47 | /** |
||
48 | * Mapper. |
||
49 | * |
||
50 | * @var Mapper |
||
51 | */ |
||
52 | protected $mapper; |
||
53 | |||
54 | /** |
||
55 | * FieldHandler. |
||
56 | * |
||
57 | * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler |
||
58 | */ |
||
59 | protected $fieldHandler; |
||
60 | |||
61 | /** |
||
62 | * URL slug converter. |
||
63 | * |
||
64 | * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter |
||
65 | */ |
||
66 | protected $slugConverter; |
||
67 | |||
68 | /** |
||
69 | * UrlAlias gateway. |
||
70 | * |
||
71 | * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway |
||
72 | */ |
||
73 | protected $urlAliasGateway; |
||
74 | |||
75 | /** |
||
76 | * ContentType handler. |
||
77 | * |
||
78 | * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler |
||
79 | */ |
||
80 | protected $contentTypeHandler; |
||
81 | |||
82 | /** |
||
83 | * Tree handler. |
||
84 | * |
||
85 | * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler |
||
86 | */ |
||
87 | protected $treeHandler; |
||
88 | |||
89 | /** @var \Psr\Log\LoggerInterface */ |
||
90 | private $logger; |
||
91 | |||
92 | /** |
||
93 | * Creates a new content handler. |
||
94 | * |
||
95 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway |
||
96 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway |
||
97 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $mapper |
||
98 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler $fieldHandler |
||
99 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter $slugConverter |
||
100 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway $urlAliasGateway |
||
101 | * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler |
||
102 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler $treeHandler |
||
103 | * @param \Psr\Log\LoggerInterface|null $logger |
||
104 | */ |
||
105 | public function __construct( |
||
126 | |||
127 | /** |
||
128 | * Creates a new Content entity in the storage engine. |
||
129 | * |
||
130 | * The values contained inside the $content will form the basis of stored |
||
131 | * entity. |
||
132 | * |
||
133 | * Will contain always a complete list of fields. |
||
134 | * |
||
135 | * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct Content creation struct. |
||
136 | * |
||
137 | * @return \eZ\Publish\SPI\Persistence\Content Content value object |
||
138 | */ |
||
139 | public function create(CreateStruct $struct) |
||
143 | |||
144 | /** |
||
145 | * Creates a new Content entity in the storage engine. |
||
146 | * |
||
147 | * The values contained inside the $content will form the basis of stored |
||
148 | * entity. |
||
149 | * |
||
150 | * Will contain always a complete list of fields. |
||
151 | * |
||
152 | * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct Content creation struct. |
||
153 | * @param mixed $versionNo Used by self::copy() to maintain version numbers |
||
154 | * |
||
155 | * @return \eZ\Publish\SPI\Persistence\Content Content value object |
||
156 | */ |
||
157 | protected function internalCreate(CreateStruct $struct, $versionNo = 1) |
||
158 | { |
||
159 | $content = new Content(); |
||
160 | |||
161 | $content->fields = $struct->fields; |
||
162 | $content->versionInfo = $this->mapper->createVersionInfoFromCreateStruct($struct, $versionNo); |
||
163 | |||
164 | $content->versionInfo->contentInfo->id = $this->contentGateway->insertContentObject($struct, $versionNo); |
||
165 | $content->versionInfo->id = $this->contentGateway->insertVersion( |
||
166 | $content->versionInfo, |
||
167 | $struct->fields |
||
168 | ); |
||
169 | |||
170 | $contentType = $this->contentTypeHandler->load($struct->typeId); |
||
171 | $this->fieldHandler->createNewFields($content, $contentType); |
||
172 | |||
173 | // Create node assignments |
||
174 | foreach ($struct->locations as $location) { |
||
175 | $location->contentId = $content->versionInfo->contentInfo->id; |
||
176 | $location->contentVersion = $content->versionInfo->versionNo; |
||
177 | $this->locationGateway->createNodeAssignment( |
||
178 | $location, |
||
179 | $location->parentId, |
||
180 | LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE |
||
181 | ); |
||
182 | } |
||
183 | |||
184 | // Create names |
||
185 | foreach ($content->versionInfo->names as $language => $name) { |
||
186 | $this->contentGateway->setName( |
||
187 | $content->versionInfo->contentInfo->id, |
||
188 | $content->versionInfo->versionNo, |
||
189 | $name, |
||
190 | $language |
||
191 | ); |
||
192 | } |
||
193 | |||
194 | return $content; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Performs the publishing operations required to set the version identified by $updateStruct->versionNo and |
||
199 | * $updateStruct->id as the published one. |
||
200 | * |
||
201 | * The publish procedure will: |
||
202 | * - Create location nodes based on the node assignments |
||
203 | * - Update the content object using the provided metadata update struct |
||
204 | * - Update the node assignments |
||
205 | * - Update location nodes of the content with the new published version |
||
206 | * - Set content and version status to published |
||
207 | * |
||
208 | * @param int $contentId |
||
209 | * @param int $versionNo |
||
210 | * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $metaDataUpdateStruct |
||
211 | * |
||
212 | * @return \eZ\Publish\SPI\Persistence\Content The published Content |
||
213 | * |
||
214 | * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException |
||
215 | * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException |
||
216 | */ |
||
217 | public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUpdateStruct) |
||
243 | |||
244 | /** |
||
245 | * Creates a new draft version from $contentId in $version. |
||
246 | * |
||
247 | * Copies all fields from $contentId in $srcVersion and creates a new |
||
248 | * version of the referred Content from it. |
||
249 | * |
||
250 | * Note: When creating a new draft in the old admin interface there will |
||
251 | * also be an entry in the `eznode_assignment` created for the draft. This |
||
252 | * is ignored in this implementation. |
||
253 | * |
||
254 | * @param mixed $contentId |
||
255 | * @param mixed $srcVersion |
||
256 | * @param mixed $userId |
||
257 | * |
||
258 | * @return \eZ\Publish\SPI\Persistence\Content |
||
259 | */ |
||
260 | public function createDraftFromVersion($contentId, $srcVersion, $userId) |
||
306 | |||
307 | /** |
||
308 | * {@inheritdoc} |
||
309 | */ |
||
310 | public function load($id, $version = null, array $translations = null) |
||
332 | |||
333 | /** |
||
334 | * {@inheritdoc} |
||
335 | */ |
||
336 | public function loadContentList(array $contentIds, array $translations = null): array |
||
408 | |||
409 | /** |
||
410 | * Returns the metadata object for a content identified by $contentId. |
||
411 | * |
||
412 | * @param int|string $contentId |
||
413 | * |
||
414 | * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo |
||
415 | */ |
||
416 | public function loadContentInfo($contentId) |
||
420 | |||
421 | public function loadContentInfoList(array $contentIds) |
||
434 | |||
435 | /** |
||
436 | * Returns the metadata object for a content identified by $remoteId. |
||
437 | * |
||
438 | * @param mixed $remoteId |
||
439 | * |
||
440 | * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo |
||
441 | */ |
||
442 | public function loadContentInfoByRemoteId($remoteId) |
||
448 | |||
449 | /** |
||
450 | * {@inheritdoc} |
||
451 | */ |
||
452 | public function loadVersionInfo($contentId, $versionNo = null) |
||
466 | |||
467 | /** |
||
468 | * Returns the number of versions with draft status created by the given $userId. |
||
469 | * |
||
470 | * @param int $userId |
||
471 | * |
||
472 | * @return int |
||
473 | */ |
||
474 | public function countDraftsForUser(int $userId): int |
||
478 | |||
479 | /** |
||
480 | * Returns all versions with draft status created by the given $userId. |
||
481 | * |
||
482 | * @param int $userId |
||
483 | * |
||
484 | * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] |
||
485 | */ |
||
486 | public function loadDraftsForUser($userId) |
||
487 | { |
||
488 | $rows = $this->contentGateway->listVersionsForUser($userId, VersionInfo::STATUS_DRAFT); |
||
489 | if (empty($rows)) { |
||
490 | return []; |
||
491 | } |
||
492 | |||
493 | $idVersionPairs = array_map( |
||
494 | function ($row) { |
||
495 | return [ |
||
496 | 'id' => $row['ezcontentobject_version_contentobject_id'], |
||
497 | 'version' => $row['ezcontentobject_version_version'], |
||
498 | ]; |
||
499 | }, |
||
500 | $rows |
||
501 | ); |
||
502 | $nameRows = $this->contentGateway->loadVersionedNameData($idVersionPairs); |
||
503 | |||
504 | return $this->mapper->extractVersionInfoListFromRows($rows, $nameRows); |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * {@inheritdoc} |
||
509 | */ |
||
510 | public function loadDraftListForUser(int $userId, int $offset = 0, int $limit = -1): array |
||
511 | { |
||
512 | $rows = $this->contentGateway->loadVersionsForUser($userId, VersionInfo::STATUS_DRAFT, $offset, $limit); |
||
513 | if (empty($rows)) { |
||
514 | return []; |
||
515 | } |
||
516 | |||
517 | $idVersionPairs = array_map( |
||
518 | static function (array $row): array { |
||
519 | return [ |
||
520 | 'id' => $row['ezcontentobject_version_contentobject_id'], |
||
521 | 'version' => $row['ezcontentobject_version_version'], |
||
522 | ]; |
||
523 | }, |
||
524 | $rows |
||
525 | ); |
||
526 | $nameRows = $this->contentGateway->loadVersionedNameData($idVersionPairs); |
||
527 | |||
528 | return $this->mapper->extractVersionInfoListFromRows($rows, $nameRows); |
||
529 | } |
||
530 | |||
531 | /** |
||
532 | * Sets the status of object identified by $contentId and $version to $status. |
||
533 | * |
||
534 | * The $status can be one of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED |
||
535 | * When status is set to VersionInfo::STATUS_PUBLISHED content status is updated to ContentInfo::STATUS_PUBLISHED |
||
536 | * |
||
537 | * @param int $contentId |
||
538 | * @param int $status |
||
539 | * @param int $version |
||
540 | * |
||
541 | * @return bool |
||
542 | */ |
||
543 | public function setStatus($contentId, $status, $version) |
||
547 | |||
548 | /** |
||
549 | * Updates a content object meta data, identified by $contentId. |
||
550 | * |
||
551 | * @param int $contentId |
||
552 | * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content |
||
553 | * |
||
554 | * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo |
||
555 | */ |
||
556 | public function updateMetadata($contentId, MetadataUpdateStruct $content) |
||
563 | |||
564 | /** |
||
565 | * Updates path identification string for locations of given $contentId if main language |
||
566 | * is set in update struct. |
||
567 | * |
||
568 | * This is specific to the Legacy storage engine, as path identification string is deprecated. |
||
569 | * |
||
570 | * @param int $contentId |
||
571 | * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content |
||
572 | */ |
||
573 | protected function updatePathIdentificationString($contentId, MetadataUpdateStruct $content) |
||
599 | |||
600 | /** |
||
601 | * Updates a content version, identified by $contentId and $versionNo. |
||
602 | * |
||
603 | * @param int $contentId |
||
604 | * @param int $versionNo |
||
605 | * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $updateStruct |
||
606 | * |
||
607 | * @return \eZ\Publish\SPI\Persistence\Content |
||
608 | */ |
||
609 | public function updateContent($contentId, $versionNo, UpdateStruct $updateStruct) |
||
626 | |||
627 | /** |
||
628 | * Deletes all versions and fields, all locations (subtree), and all relations. |
||
629 | * |
||
630 | * Removes the relations, but not the related objects. All subtrees of the |
||
631 | * assigned nodes of this content objects are removed (recursively). |
||
632 | * |
||
633 | * @param int $contentId |
||
634 | * |
||
635 | * @return bool |
||
636 | */ |
||
637 | public function deleteContent($contentId) |
||
648 | |||
649 | /** |
||
650 | * Deletes raw content data. |
||
651 | * |
||
652 | * @param int $contentId |
||
653 | */ |
||
654 | public function removeRawContent($contentId) |
||
658 | |||
659 | /** |
||
660 | * Deletes given version, its fields, node assignment, relations and names. |
||
661 | * |
||
662 | * Removes the relations, but not the related objects. |
||
663 | * |
||
664 | * @param int $contentId |
||
665 | * @param int $versionNo |
||
666 | * |
||
667 | * @return bool |
||
668 | */ |
||
669 | public function deleteVersion($contentId, $versionNo) |
||
681 | |||
682 | /** |
||
683 | * Returns the versions for $contentId. |
||
684 | * |
||
685 | * Result is returned with oldest version first (sorted by created, alternatively version id if auto increment). |
||
686 | * |
||
687 | * @param int $contentId |
||
688 | * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. |
||
689 | * @param int $limit Limit for items returned, -1 means none. |
||
690 | * |
||
691 | * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] |
||
692 | */ |
||
693 | public function listVersions($contentId, $status = null, $limit = -1) |
||
697 | |||
698 | /** |
||
699 | * Copy Content with Fields, Versions & Relations from $contentId in $version. |
||
700 | * |
||
701 | * {@inheritdoc} |
||
702 | * |
||
703 | * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If content or version is not found |
||
704 | * |
||
705 | * @param mixed $contentId |
||
706 | * @param mixed|null $versionNo Copy all versions if left null |
||
707 | * @param int|null $newOwnerId |
||
708 | * |
||
709 | * @return \eZ\Publish\SPI\Persistence\Content |
||
710 | */ |
||
711 | public function copy($contentId, $versionNo = null, $newOwnerId = null) |
||
712 | { |
||
713 | $currentVersionNo = isset($versionNo) ? |
||
714 | $versionNo : |
||
715 | $this->loadContentInfo($contentId)->currentVersionNo; |
||
716 | |||
717 | // Copy content in given version or current version |
||
718 | $createStruct = $this->mapper->createCreateStructFromContent( |
||
719 | $this->load($contentId, $currentVersionNo) |
||
720 | ); |
||
721 | if ($newOwnerId) { |
||
722 | $createStruct->ownerId = $newOwnerId; |
||
723 | } |
||
724 | $content = $this->internalCreate($createStruct, $currentVersionNo); |
||
725 | |||
726 | // If version was not passed also copy other versions |
||
727 | if (!isset($versionNo)) { |
||
728 | $contentType = $this->contentTypeHandler->load($createStruct->typeId); |
||
729 | |||
730 | foreach ($this->listVersions($contentId) as $versionInfo) { |
||
731 | if ($versionInfo->versionNo === $currentVersionNo) { |
||
732 | continue; |
||
733 | } |
||
734 | |||
735 | $versionContent = $this->load($contentId, $versionInfo->versionNo); |
||
736 | |||
737 | $versionContent->versionInfo->contentInfo->id = $content->versionInfo->contentInfo->id; |
||
738 | $versionContent->versionInfo->modificationDate = $createStruct->modified; |
||
739 | $versionContent->versionInfo->creationDate = $createStruct->modified; |
||
740 | $versionContent->versionInfo->id = $this->contentGateway->insertVersion( |
||
741 | $versionContent->versionInfo, |
||
742 | $versionContent->fields |
||
743 | ); |
||
744 | |||
745 | $this->fieldHandler->createNewFields($versionContent, $contentType); |
||
746 | |||
747 | // Create names |
||
748 | foreach ($versionContent->versionInfo->names as $language => $name) { |
||
749 | $this->contentGateway->setName( |
||
750 | $content->versionInfo->contentInfo->id, |
||
751 | $versionInfo->versionNo, |
||
752 | $name, |
||
753 | $language |
||
754 | ); |
||
755 | } |
||
756 | } |
||
757 | |||
758 | // Batch copy relations for all versions |
||
759 | $this->contentGateway->copyRelations($contentId, $content->versionInfo->contentInfo->id); |
||
760 | } else { |
||
761 | // Batch copy relations for published version |
||
762 | $this->contentGateway->copyRelations($contentId, $content->versionInfo->contentInfo->id, $versionNo); |
||
763 | } |
||
764 | |||
765 | return $content; |
||
766 | } |
||
767 | |||
768 | /** |
||
769 | * Creates a relation between $sourceContentId in $sourceContentVersionNo |
||
770 | * and $destinationContentId with a specific $type. |
||
771 | * |
||
772 | * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? |
||
773 | * |
||
774 | * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $createStruct |
||
775 | * |
||
776 | * @return \eZ\Publish\SPI\Persistence\Content\Relation |
||
777 | */ |
||
778 | public function addRelation(RelationCreateStruct $createStruct) |
||
786 | |||
787 | /** |
||
788 | * Removes a relation by relation Id. |
||
789 | * |
||
790 | * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? |
||
791 | * |
||
792 | * @param mixed $relationId |
||
793 | * @param int $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, |
||
794 | * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, |
||
795 | * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, |
||
796 | * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} |
||
797 | */ |
||
798 | public function removeRelation($relationId, $type) |
||
802 | |||
803 | /** |
||
804 | * Loads relations from $sourceContentId. Optionally, loads only those with $type and $sourceContentVersionNo. |
||
805 | * |
||
806 | * @param mixed $sourceContentId Source Content ID |
||
807 | * @param mixed|null $sourceContentVersionNo Source Content Version, null if not specified |
||
808 | * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, |
||
809 | * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, |
||
810 | * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, |
||
811 | * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} |
||
812 | * |
||
813 | * @return \eZ\Publish\SPI\Persistence\Content\Relation[] |
||
814 | */ |
||
815 | public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null) |
||
821 | |||
822 | /** |
||
823 | * {@inheritdoc} |
||
824 | */ |
||
825 | public function countReverseRelations(int $destinationContentId, ?int $type = null): int |
||
829 | |||
830 | /** |
||
831 | * Loads relations from $contentId. Optionally, loads only those with $type. |
||
832 | * |
||
833 | * Only loads relations against published versions. |
||
834 | * |
||
835 | * @param mixed $destinationContentId Destination Content ID |
||
836 | * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, |
||
837 | * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, |
||
838 | * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, |
||
839 | * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} |
||
840 | * |
||
841 | * @return \eZ\Publish\SPI\Persistence\Content\Relation[] |
||
842 | */ |
||
843 | public function loadReverseRelations($destinationContentId, $type = null) |
||
849 | |||
850 | /** |
||
851 | * {@inheritdoc} |
||
852 | */ |
||
853 | public function loadReverseRelationList( |
||
863 | |||
864 | /** |
||
865 | * {@inheritdoc} |
||
866 | */ |
||
867 | public function removeTranslationFromContent($contentId, $languageCode) |
||
875 | |||
876 | /** |
||
877 | * {@inheritdoc} |
||
878 | */ |
||
879 | public function deleteTranslationFromContent($contentId, $languageCode) |
||
888 | |||
889 | /** |
||
890 | * {@inheritdoc} |
||
891 | */ |
||
892 | public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode) |
||
927 | } |
||
928 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.