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 ClassMetadata 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 ClassMetadata, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 19 | class ClassMetadata implements IClassMetadata |
||
| 20 | { |
||
| 21 | const IDGENERATOR_UUID = 1; |
||
| 22 | const IDGENERATOR_ASSIGNED = 2; |
||
| 23 | |||
| 24 | const TO_ONE = 5; |
||
| 25 | const TO_MANY = 10; |
||
| 26 | const ONE_TO_ONE = 1; |
||
| 27 | const ONE_TO_MANY = 2; |
||
| 28 | const MANY_TO_ONE = 4; |
||
| 29 | const MANY_TO_MANY = 8; |
||
| 30 | |||
| 31 | const CASCADE_PERSIST = 1; |
||
| 32 | const CASCADE_REMOVE = 2; |
||
| 33 | const CASCADE_MERGE = 4; |
||
| 34 | const CASCADE_DETACH = 8; |
||
| 35 | const CASCADE_REFRESH = 16; |
||
| 36 | const CASCADE_ALL = 31; |
||
| 37 | |||
| 38 | public $idGenerator = self::IDGENERATOR_UUID; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * READ-ONLY: The field name of the document identifier. |
||
| 42 | */ |
||
| 43 | public $identifier; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * READ-ONLY: The name of the document class. |
||
| 47 | */ |
||
| 48 | public $name; |
||
| 49 | |||
| 50 | /** |
||
| 51 | * READ-ONLY: The root document class name. |
||
| 52 | */ |
||
| 53 | public $rootDocumentName; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * READ-ONLY: Is this entity in an inheritance hierachy? |
||
| 57 | */ |
||
| 58 | public $inInheritanceHierachy = false; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * READ-ONLY: a list of all parent classes. |
||
| 62 | */ |
||
| 63 | public $parentClasses = array(); |
||
| 64 | |||
| 65 | /** |
||
| 66 | * READ-ONLY: The namespace the document class is contained in. |
||
| 67 | * |
||
| 68 | * @var string |
||
| 69 | * @todo Not really needed. Usage could be localized. |
||
| 70 | */ |
||
| 71 | public $namespace; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * The name of the custom repository class used for the document class. |
||
| 75 | * (Optional). |
||
| 76 | * |
||
| 77 | * @var string |
||
| 78 | */ |
||
| 79 | public $customRepositoryClassName; |
||
| 80 | |||
| 81 | /** |
||
| 82 | * READ-ONLY: The field mappings of the class. |
||
| 83 | * Keys are field names and values are mapping definitions. |
||
| 84 | * |
||
| 85 | * The mapping definition array has the following values: |
||
| 86 | * |
||
| 87 | * - <b>fieldName</b> (string) |
||
| 88 | * The name of the field in the Document. |
||
| 89 | * |
||
| 90 | * - <b>id</b> (boolean, optional) |
||
| 91 | * Marks the field as the primary key of the document. Multiple fields of an |
||
| 92 | * document can have the id attribute, forming a composite key. |
||
| 93 | * |
||
| 94 | * @var array |
||
| 95 | */ |
||
| 96 | public $fieldMappings = array(); |
||
| 97 | |||
| 98 | /** |
||
| 99 | * An array of indexed fields, accessible through a generic view shipped with Doctrine. |
||
| 100 | * |
||
| 101 | * @var array |
||
| 102 | */ |
||
| 103 | public $indexes = array(); |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Is this class indexed? If yes, then a findAll() query can be executed for this type. |
||
| 107 | * |
||
| 108 | * @var bool |
||
| 109 | */ |
||
| 110 | public $indexed = false; |
||
| 111 | |||
| 112 | /** |
||
| 113 | * An array of json result-key-names to field-names |
||
| 114 | * |
||
| 115 | * @var array |
||
| 116 | */ |
||
| 117 | public $jsonNames = array(); |
||
| 118 | |||
| 119 | /** |
||
| 120 | * READ-ONLY: Array of fields to also load with a given method. |
||
| 121 | * |
||
| 122 | * @var array |
||
| 123 | */ |
||
| 124 | public $alsoLoadMethods = array(); |
||
| 125 | |||
| 126 | /** |
||
| 127 | * READ-ONLY: Whether this class describes the mapping of a mapped superclass. |
||
| 128 | * |
||
| 129 | * @var boolean |
||
| 130 | */ |
||
| 131 | public $isMappedSuperclass = false; |
||
| 132 | |||
| 133 | /** |
||
| 134 | * READ-ONLY: Whether this class describes the mapping of a embedded document. |
||
| 135 | * |
||
| 136 | * @var boolean |
||
| 137 | */ |
||
| 138 | public $isEmbeddedDocument = false; |
||
| 139 | |||
| 140 | /** |
||
| 141 | * READ-ONLY: Wheather the document or embedded document is read-only and will be skipped in change-tracking. |
||
| 142 | * |
||
| 143 | * This should be set to true for value objects, for example attachments. Replacing the reference with a new |
||
| 144 | * value object will trigger an update. |
||
| 145 | * |
||
| 146 | * @var bool |
||
| 147 | */ |
||
| 148 | public $isReadOnly = false; |
||
| 149 | |||
| 150 | /** |
||
| 151 | * READ-ONLY |
||
| 152 | * |
||
| 153 | * @var array |
||
| 154 | */ |
||
| 155 | public $associationsMappings = array(); |
||
| 156 | |||
| 157 | /** |
||
| 158 | * CouchDB documents are always versioned, this flag determines if this version is exposed to the userland. |
||
| 159 | * |
||
| 160 | * @var bool |
||
| 161 | */ |
||
| 162 | public $isVersioned = false; |
||
| 163 | |||
| 164 | /** |
||
| 165 | * Version Field stores the CouchDB Revision |
||
| 166 | * |
||
| 167 | * @var string |
||
| 168 | */ |
||
| 169 | public $versionField = null; |
||
| 170 | |||
| 171 | /** |
||
| 172 | * @var bool |
||
| 173 | */ |
||
| 174 | public $hasAttachments = false; |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Field that stores the attachments as a key->value array of file-names to attachment objects. |
||
| 178 | * |
||
| 179 | * @var string |
||
| 180 | */ |
||
| 181 | public $attachmentField = null; |
||
| 182 | |||
| 183 | /** |
||
| 184 | * If in an inheritance scenario the attachment field is on a super class, this is its name. |
||
| 185 | * |
||
| 186 | * @var string|null |
||
| 187 | */ |
||
| 188 | public $attachmentDeclaredClass = null; |
||
| 189 | |||
| 190 | /** |
||
| 191 | * The ReflectionClass instance of the mapped class. |
||
| 192 | * |
||
| 193 | * @var ReflectionClass |
||
| 194 | */ |
||
| 195 | public $reflClass; |
||
| 196 | |||
| 197 | /** |
||
| 198 | * The ReflectionProperty instances of the mapped class. |
||
| 199 | * |
||
| 200 | * @var array |
||
| 201 | */ |
||
| 202 | public $reflFields = array(); |
||
| 203 | |||
| 204 | /** |
||
| 205 | * @var \Doctrine\Instantiator\InstantiatorInterface |
||
| 206 | */ |
||
| 207 | private $instantiator; |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Initializes a new ClassMetadata instance that will hold the object-document mapping |
||
| 211 | * metadata of the class with the given name. |
||
| 212 | * |
||
| 213 | * @param string $documentName The name of the document class the new instance is used for. |
||
| 214 | */ |
||
| 215 | public function __construct($documentName) |
||
| 221 | |||
| 222 | /** |
||
| 223 | * Used to derive a class metadata of the current instance for a mapped child class. |
||
| 224 | * |
||
| 225 | * @param ClassMetadata $child |
||
| 226 | * @throws \InvalidArgumentException |
||
| 227 | */ |
||
| 228 | public function deriveChildMetadata($child) |
||
| 261 | |||
| 262 | /** |
||
| 263 | * Determines which fields get serialized. |
||
| 264 | * |
||
| 265 | * It is only serialized what is necessary for best unserialization performance. |
||
| 266 | * That means any metadata properties that are not set or empty or simply have |
||
| 267 | * their default value are NOT serialized. |
||
| 268 | * |
||
| 269 | * Parts that are also NOT serialized because they can not be properly unserialized: |
||
| 270 | * - reflClass (ReflectionClass) |
||
| 271 | * - reflFields (ReflectionProperty array) |
||
| 272 | * |
||
| 273 | * @return array The names of all the fields that should be serialized. |
||
| 274 | */ |
||
| 275 | public function __sleep() |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Restores some state that can not be serialized/unserialized. |
||
| 333 | */ |
||
| 334 | public function wakeupReflection($reflService) |
||
| 372 | |||
| 373 | /** |
||
| 374 | * Creates a new instance of the mapped class, without invoking the constructor. |
||
| 375 | * |
||
| 376 | * @return object |
||
| 377 | */ |
||
| 378 | public function newInstance() |
||
| 382 | |||
| 383 | /** |
||
| 384 | * Gets the ReflectionClass instance of the mapped class. |
||
| 385 | * |
||
| 386 | * @return ReflectionClass |
||
| 387 | */ |
||
| 388 | public function getReflectionClass() |
||
| 395 | |||
| 396 | /** |
||
| 397 | * Gets the ReflectionPropertys of the mapped class. |
||
| 398 | * |
||
| 399 | * @return array An array of ReflectionProperty instances. |
||
| 400 | */ |
||
| 401 | public function getReflectionProperties() |
||
| 405 | |||
| 406 | /** |
||
| 407 | * Gets a ReflectionProperty for a specific field of the mapped class. |
||
| 408 | * |
||
| 409 | * @param string $name |
||
| 410 | * @return ReflectionProperty |
||
| 411 | */ |
||
| 412 | public function getReflectionProperty($name) |
||
| 416 | |||
| 417 | |||
| 418 | /** |
||
| 419 | * Sets the document identifier of a document. |
||
| 420 | * |
||
| 421 | * @param object $document |
||
| 422 | * @param mixed $id |
||
| 423 | */ |
||
| 424 | public function setIdentifierValue($document, $id) |
||
| 428 | |||
| 429 | /** |
||
| 430 | * Gets the document identifier. |
||
| 431 | * |
||
| 432 | * @param object $document |
||
| 433 | * @return string $id |
||
| 434 | */ |
||
| 435 | public function getIdentifierValue($document) |
||
| 439 | |||
| 440 | /** |
||
| 441 | * Get identifier values of this document. |
||
| 442 | * |
||
| 443 | * Since CouchDB only allows exactly one identifier field this is a proxy |
||
| 444 | * to {@see getIdentifierValue()} and returns an array with the identifier |
||
| 445 | * field as a key. |
||
| 446 | * |
||
| 447 | * @param object $document |
||
| 448 | * @return array |
||
| 449 | */ |
||
| 450 | public function getIdentifierValues($document) |
||
| 454 | |||
| 455 | /** |
||
| 456 | * Sets the specified field to the specified value on the given document. |
||
| 457 | * |
||
| 458 | * @param object $document |
||
| 459 | * @param string $field |
||
| 460 | * @param mixed $value |
||
| 461 | */ |
||
| 462 | public function setFieldValue($document, $field, $value) |
||
| 466 | |||
| 467 | /** |
||
| 468 | * Gets the specified field's value off the given document. |
||
| 469 | * |
||
| 470 | * @param object $document |
||
| 471 | * @param string $field |
||
| 472 | */ |
||
| 473 | public function getFieldValue($document, $field) |
||
| 477 | |||
| 478 | /** |
||
| 479 | * Checks whether a field is part of the identifier/primary key field(s). |
||
| 480 | * |
||
| 481 | * @param string $fieldName The field name |
||
| 482 | * @return boolean TRUE if the field is part of the table identifier/primary key field(s), |
||
| 483 | * FALSE otherwise. |
||
| 484 | */ |
||
| 485 | public function isIdentifier($fieldName) |
||
| 489 | |||
| 490 | /** |
||
| 491 | * INTERNAL: |
||
| 492 | * Sets the mapped identifier field of this class. |
||
| 493 | * |
||
| 494 | * @param string $identifier |
||
| 495 | * @throws MappingException |
||
| 496 | */ |
||
| 497 | public function setIdentifier($identifier) |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Gets the mapped identifier field of this class. |
||
| 507 | * |
||
| 508 | * @return string $identifier |
||
| 509 | */ |
||
| 510 | public function getIdentifier() |
||
| 514 | |||
| 515 | /** |
||
| 516 | * Get identifier field names of this class.; |
||
| 517 | * |
||
| 518 | * Since CouchDB only allows exactly one identifier field this is a proxy |
||
| 519 | * to {@see getIdentifier()} and returns an array. |
||
| 520 | * |
||
| 521 | * @return array |
||
| 522 | */ |
||
| 523 | public function getIdentifierFieldNames() |
||
| 527 | |||
| 528 | /** |
||
| 529 | * Checks whether the class has a (mapped) field with a certain name. |
||
| 530 | * |
||
| 531 | * @param $fieldName |
||
| 532 | * @return boolean |
||
| 533 | */ |
||
| 534 | public function hasField($fieldName) |
||
| 538 | |||
| 539 | /** |
||
| 540 | * Registers a custom repository class for the document class. |
||
| 541 | * |
||
| 542 | * @param string $repositoryClassName The class name of the custom mapper. |
||
| 543 | */ |
||
| 544 | public function setCustomRepositoryClass($repositoryClassName) |
||
| 548 | |||
| 549 | /** |
||
| 550 | * The name of this Document class. |
||
| 551 | * |
||
| 552 | * @return string $name The Document class name. |
||
| 553 | */ |
||
| 554 | public function getName() |
||
| 558 | |||
| 559 | /** |
||
| 560 | * The namespace this Document class belongs to. |
||
| 561 | * |
||
| 562 | * @return string $namespace The namespace name. |
||
| 563 | */ |
||
| 564 | public function getNamespace() |
||
| 568 | |||
| 569 | /** |
||
| 570 | * Set the field that will contain attachments of this document. |
||
| 571 | * |
||
| 572 | * @param string $fieldName |
||
| 573 | * @throws MappingException |
||
| 574 | */ |
||
| 575 | public function mapAttachments($fieldName) |
||
| 584 | |||
| 585 | /** |
||
| 586 | * Map an embedded object |
||
| 587 | * |
||
| 588 | * - fieldName - The name of the property/field on the mapped php class |
||
| 589 | * - jsonName - JSON key name of this field in CouchDB. |
||
| 590 | * - targetDocument - Name of the target document |
||
| 591 | * - embedded - one or many embedded objects? |
||
| 592 | * |
||
| 593 | * @param array $mapping |
||
| 594 | */ |
||
| 595 | public function mapEmbedded(array $mapping) |
||
| 601 | |||
| 602 | /** |
||
| 603 | * Map a field. |
||
| 604 | * |
||
| 605 | * - type - The Doctrine Type of this field. |
||
| 606 | * - fieldName - The name of the property/field on the mapped php class |
||
| 607 | * - jsonName - JSON key name of this field in CouchDB. |
||
| 608 | * - name - The JSON key of this field in the CouchDB document |
||
| 609 | * - id - True for an ID field. |
||
| 610 | * - strategy - ID Generator strategy when the field is an id-field. |
||
| 611 | * - indexed - Is this field indexed for the Doctrine CouchDB repository view |
||
| 612 | * - isVersionField - Is this field containing the revision number of this document? |
||
| 613 | * |
||
| 614 | * @param array $mapping The mapping information. |
||
| 615 | */ |
||
| 616 | public function mapField(array $mapping) |
||
| 642 | |||
| 643 | protected function validateAndCompleteFieldMapping($mapping) |
||
| 657 | |||
| 658 | protected function validateAndCompleteReferenceMapping($mapping) |
||
| 665 | |||
| 666 | protected function validateAndCompleteAssociationMapping($mapping) |
||
| 674 | |||
| 675 | View Code Duplication | public function mapManyToOne($mapping) |
|
| 686 | |||
| 687 | View Code Duplication | public function mapManyToMany($mapping) |
|
| 696 | |||
| 697 | private function checkAndStoreIndexMapping($mapping) |
||
| 706 | |||
| 707 | private function storeAssociationMapping($mapping) |
||
| 712 | |||
| 713 | /** |
||
| 714 | * A numerically indexed list of field names of this persistent class. |
||
| 715 | * |
||
| 716 | * This array includes identifier fields if present on this class. |
||
| 717 | * |
||
| 718 | * @return array |
||
| 719 | */ |
||
| 720 | public function getFieldNames() |
||
| 724 | |||
| 725 | /** |
||
| 726 | * Gets the mapping of a field. |
||
| 727 | * |
||
| 728 | * @param string $fieldName The field name. |
||
| 729 | * @return array The field mapping. |
||
| 730 | * @throws MappingException |
||
| 731 | */ |
||
| 732 | public function getFieldMapping($fieldName) |
||
| 739 | |||
| 740 | /** |
||
| 741 | * Gets the type of a field. |
||
| 742 | * |
||
| 743 | * @param string $fieldName |
||
| 744 | * @return Type |
||
| 745 | */ |
||
| 746 | public function getTypeOfField($fieldName) |
||
| 751 | |||
| 752 | /** |
||
| 753 | * Checks if the given field is a mapped association for this class. |
||
| 754 | * |
||
| 755 | * @param string $fieldName |
||
| 756 | * @return boolean |
||
| 757 | */ |
||
| 758 | public function hasAssociation($fieldName) |
||
| 762 | |||
| 763 | public function isCollectionValuedAssociation($name) |
||
| 768 | |||
| 769 | /** |
||
| 770 | * Checks if the given field is a mapped single valued association for this class. |
||
| 771 | * |
||
| 772 | * @param string $fieldName |
||
| 773 | * @return boolean |
||
| 774 | */ |
||
| 775 | public function isSingleValuedAssociation($fieldName) |
||
| 780 | |||
| 781 | /** |
||
| 782 | * A numerically indexed list of association names of this persistent class. |
||
| 783 | * |
||
| 784 | * This array includes identifier associations if present on this class. |
||
| 785 | * |
||
| 786 | * @return array |
||
| 787 | */ |
||
| 788 | public function getAssociationNames() |
||
| 792 | |||
| 793 | /** |
||
| 794 | * Returns the target class name of the given association. |
||
| 795 | * |
||
| 796 | * @param string $assocName |
||
| 797 | * @return string |
||
| 798 | * @throws \InvalidArgumentException |
||
| 799 | */ |
||
| 800 | public function getAssociationTargetClass($assocName) |
||
| 807 | |||
| 808 | /** |
||
| 809 | * {@inheritDoc} |
||
| 810 | */ |
||
| 811 | public function getAssociationMappedByTargetField($assocName) |
||
| 815 | |||
| 816 | /** |
||
| 817 | * {@inheritDoc} |
||
| 818 | */ |
||
| 819 | public function isAssociationInverseSide($assocName) |
||
| 823 | |||
| 824 | public function isInheritedField($field) |
||
| 828 | |||
| 829 | public function isInheritedAssociation($field) |
||
| 833 | |||
| 834 | public function setParentClasses($classes) |
||
| 842 | |||
| 843 | public function markInheritanceRoot() |
||
| 850 | |||
| 851 | /** |
||
| 852 | * Initializes a new ClassMetadata instance that will hold the object-relational mapping |
||
| 853 | * metadata of the class with the given name. |
||
| 854 | * |
||
| 855 | * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service. |
||
| 856 | * |
||
| 857 | * @return void |
||
| 858 | */ |
||
| 859 | public function initializeReflection($reflService) |
||
| 868 | } |
||
| 869 | |||
| 870 |
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: