Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
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 | /** |
||
90 | * @var \Psr\Log\LoggerInterface |
||
91 | */ |
||
92 | private $logger; |
||
93 | |||
94 | /** |
||
95 | * Creates a new content handler. |
||
96 | * |
||
97 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway |
||
98 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway |
||
99 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $mapper |
||
100 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler $fieldHandler |
||
101 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter $slugConverter |
||
102 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway $urlAliasGateway |
||
103 | * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler |
||
104 | * @param \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler $treeHandler |
||
105 | * @param \Psr\Log\LoggerInterface|null $logger |
||
106 | */ |
||
107 | public function __construct( |
||
128 | |||
129 | /** |
||
130 | * Creates a new Content entity in the storage engine. |
||
131 | * |
||
132 | * The values contained inside the $content will form the basis of stored |
||
133 | * entity. |
||
134 | * |
||
135 | * Will contain always a complete list of fields. |
||
136 | * |
||
137 | * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct Content creation struct. |
||
138 | * |
||
139 | * @return \eZ\Publish\SPI\Persistence\Content Content value object |
||
140 | */ |
||
141 | public function create(CreateStruct $struct) |
||
145 | |||
146 | /** |
||
147 | * Creates a new Content entity in the storage engine. |
||
148 | * |
||
149 | * The values contained inside the $content will form the basis of stored |
||
150 | * entity. |
||
151 | * |
||
152 | * Will contain always a complete list of fields. |
||
153 | * |
||
154 | * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct Content creation struct. |
||
155 | * @param mixed $versionNo Used by self::copy() to maintain version numbers |
||
156 | * |
||
157 | * @return \eZ\Publish\SPI\Persistence\Content Content value object |
||
158 | */ |
||
159 | protected function internalCreate(CreateStruct $struct, $versionNo = 1) |
||
198 | |||
199 | /** |
||
200 | * Performs the publishing operations required to set the version identified by $updateStruct->versionNo and |
||
201 | * $updateStruct->id as the published one. |
||
202 | * |
||
203 | * The publish procedure will: |
||
204 | * - Create location nodes based on the node assignments |
||
205 | * - Update the content object using the provided metadata update struct |
||
206 | * - Update the node assignments |
||
207 | * - Update location nodes of the content with the new published version |
||
208 | * - Set content and version status to published |
||
209 | * |
||
210 | * @param int $contentId |
||
211 | * @param int $versionNo |
||
212 | * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $metaDataUpdateStruct |
||
213 | * |
||
214 | * @return \eZ\Publish\SPI\Persistence\Content The published Content |
||
215 | */ |
||
216 | public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUpdateStruct) |
||
242 | |||
243 | /** |
||
244 | * Creates a new draft version from $contentId in $version. |
||
245 | * |
||
246 | * Copies all fields from $contentId in $srcVersion and creates a new |
||
247 | * version of the referred Content from it. |
||
248 | * |
||
249 | * Note: When creating a new draft in the old admin interface there will |
||
250 | * also be an entry in the `eznode_assignment` created for the draft. This |
||
251 | * is ignored in this implementation. |
||
252 | * |
||
253 | * @param mixed $contentId |
||
254 | * @param mixed $srcVersion |
||
255 | * @param mixed $userId |
||
256 | * |
||
257 | * @return \eZ\Publish\SPI\Persistence\Content |
||
258 | */ |
||
259 | public function createDraftFromVersion($contentId, $srcVersion, $userId) |
||
305 | |||
306 | /** |
||
307 | * {@inheritdoc} |
||
308 | */ |
||
309 | public function load($id, $version = null, array $translations = null) |
||
331 | |||
332 | /** |
||
333 | * {@inheritdoc} |
||
334 | */ |
||
335 | public function loadContentList(array $contentIds, array $translations = null): array |
||
407 | |||
408 | /** |
||
409 | * Returns the metadata object for a content identified by $contentId. |
||
410 | * |
||
411 | * @param int|string $contentId |
||
412 | * |
||
413 | * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo |
||
414 | */ |
||
415 | public function loadContentInfo($contentId) |
||
419 | |||
420 | public function loadContentInfoList(array $contentIds) |
||
433 | |||
434 | /** |
||
435 | * Returns the metadata object for a content identified by $remoteId. |
||
436 | * |
||
437 | * @param mixed $remoteId |
||
438 | * |
||
439 | * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo |
||
440 | */ |
||
441 | public function loadContentInfoByRemoteId($remoteId) |
||
447 | |||
448 | /** |
||
449 | * Returns the version object for a content/version identified by $contentId and $versionNo. |
||
450 | * |
||
451 | * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If version is not found |
||
452 | * |
||
453 | * @param int|string $contentId |
||
454 | * @param int $versionNo Version number to load |
||
455 | * |
||
456 | * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo |
||
457 | */ |
||
458 | View Code Duplication | public function loadVersionInfo($contentId, $versionNo) |
|
472 | |||
473 | /** |
||
474 | * Returns all versions with draft status created by the given $userId. |
||
475 | * |
||
476 | * @param int $userId |
||
477 | * |
||
478 | * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] |
||
479 | */ |
||
480 | View Code Duplication | public function loadDraftsForUser($userId) |
|
500 | |||
501 | /** |
||
502 | * Sets the status of object identified by $contentId and $version to $status. |
||
503 | * |
||
504 | * The $status can be one of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED |
||
505 | * When status is set to VersionInfo::STATUS_PUBLISHED content status is updated to ContentInfo::STATUS_PUBLISHED |
||
506 | * |
||
507 | * @param int $contentId |
||
508 | * @param int $status |
||
509 | * @param int $version |
||
510 | * |
||
511 | * @return bool |
||
512 | */ |
||
513 | public function setStatus($contentId, $status, $version) |
||
517 | |||
518 | /** |
||
519 | * Updates a content object meta data, identified by $contentId. |
||
520 | * |
||
521 | * @param int $contentId |
||
522 | * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content |
||
523 | * |
||
524 | * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo |
||
525 | */ |
||
526 | public function updateMetadata($contentId, MetadataUpdateStruct $content) |
||
533 | |||
534 | /** |
||
535 | * Updates path identification string for locations of given $contentId if main language |
||
536 | * is set in update struct. |
||
537 | * |
||
538 | * This is specific to the Legacy storage engine, as path identification string is deprecated. |
||
539 | * |
||
540 | * @param int $contentId |
||
541 | * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content |
||
542 | */ |
||
543 | protected function updatePathIdentificationString($contentId, MetadataUpdateStruct $content) |
||
569 | |||
570 | /** |
||
571 | * Updates a content version, identified by $contentId and $versionNo. |
||
572 | * |
||
573 | * @param int $contentId |
||
574 | * @param int $versionNo |
||
575 | * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $updateStruct |
||
576 | * |
||
577 | * @return \eZ\Publish\SPI\Persistence\Content |
||
578 | */ |
||
579 | public function updateContent($contentId, $versionNo, UpdateStruct $updateStruct) |
||
596 | |||
597 | /** |
||
598 | * Deletes all versions and fields, all locations (subtree), and all relations. |
||
599 | * |
||
600 | * Removes the relations, but not the related objects. All subtrees of the |
||
601 | * assigned nodes of this content objects are removed (recursively). |
||
602 | * |
||
603 | * @param int $contentId |
||
604 | * |
||
605 | * @return bool |
||
606 | */ |
||
607 | public function deleteContent($contentId) |
||
618 | |||
619 | /** |
||
620 | * Deletes raw content data. |
||
621 | * |
||
622 | * @param int $contentId |
||
623 | */ |
||
624 | public function removeRawContent($contentId) |
||
628 | |||
629 | /** |
||
630 | * Deletes given version, its fields, node assignment, relations and names. |
||
631 | * |
||
632 | * Removes the relations, but not the related objects. |
||
633 | * |
||
634 | * @param int $contentId |
||
635 | * @param int $versionNo |
||
636 | * |
||
637 | * @return bool |
||
638 | */ |
||
639 | public function deleteVersion($contentId, $versionNo) |
||
651 | |||
652 | /** |
||
653 | * Returns the versions for $contentId. |
||
654 | * |
||
655 | * Result is returned with oldest version first (sorted by created, alternatively version id if auto increment). |
||
656 | * |
||
657 | * @param int $contentId |
||
658 | * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. |
||
659 | * @param int $limit Limit for items returned, -1 means none. |
||
660 | * |
||
661 | * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] |
||
662 | */ |
||
663 | public function listVersions($contentId, $status = null, $limit = -1) |
||
667 | |||
668 | /** |
||
669 | * Copy Content with Fields, Versions & Relations from $contentId in $version. |
||
670 | * |
||
671 | * {@inheritdoc} |
||
672 | * |
||
673 | * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If content or version is not found |
||
674 | * |
||
675 | * @param mixed $contentId |
||
676 | * @param mixed|null $versionNo Copy all versions if left null |
||
677 | * @param int|null $newOwnerId |
||
678 | * |
||
679 | * @return \eZ\Publish\SPI\Persistence\Content |
||
680 | */ |
||
681 | public function copy($contentId, $versionNo = null, $newOwnerId = null) |
||
737 | |||
738 | /** |
||
739 | * Creates a relation between $sourceContentId in $sourceContentVersionNo |
||
740 | * and $destinationContentId with a specific $type. |
||
741 | * |
||
742 | * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? |
||
743 | * |
||
744 | * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $createStruct |
||
745 | * |
||
746 | * @return \eZ\Publish\SPI\Persistence\Content\Relation |
||
747 | */ |
||
748 | public function addRelation(RelationCreateStruct $createStruct) |
||
756 | |||
757 | /** |
||
758 | * Removes a relation by relation Id. |
||
759 | * |
||
760 | * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? |
||
761 | * |
||
762 | * @param mixed $relationId |
||
763 | * @param int $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, |
||
764 | * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, |
||
765 | * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, |
||
766 | * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} |
||
767 | */ |
||
768 | public function removeRelation($relationId, $type) |
||
772 | |||
773 | /** |
||
774 | * Loads relations from $sourceContentId. Optionally, loads only those with $type and $sourceContentVersionNo. |
||
775 | * |
||
776 | * @param mixed $sourceContentId Source Content ID |
||
777 | * @param mixed|null $sourceContentVersionNo Source Content Version, null if not specified |
||
778 | * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, |
||
779 | * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, |
||
780 | * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, |
||
781 | * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} |
||
782 | * |
||
783 | * @return \eZ\Publish\SPI\Persistence\Content\Relation[] |
||
784 | */ |
||
785 | public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null) |
||
791 | |||
792 | /** |
||
793 | * Loads relations from $contentId. Optionally, loads only those with $type. |
||
794 | * |
||
795 | * Only loads relations against published versions. |
||
796 | * |
||
797 | * @param mixed $destinationContentId Destination Content ID |
||
798 | * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, |
||
799 | * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, |
||
800 | * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, |
||
801 | * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} |
||
802 | * |
||
803 | * @return \eZ\Publish\SPI\Persistence\Content\Relation[] |
||
804 | */ |
||
805 | public function loadReverseRelations($destinationContentId, $type = null) |
||
811 | |||
812 | /** |
||
813 | * {@inheritdoc} |
||
814 | */ |
||
815 | public function removeTranslationFromContent($contentId, $languageCode) |
||
823 | |||
824 | /** |
||
825 | * {@inheritdoc} |
||
826 | */ |
||
827 | public function deleteTranslationFromContent($contentId, $languageCode) |
||
836 | |||
837 | /** |
||
838 | * {@inheritdoc} |
||
839 | */ |
||
840 | public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode) |
||
875 | } |
||
876 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.