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 ModelStructureProperty 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 ModelStructureProperty, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 99 | class ModelStructureProperty extends StructureProperty |
||
| 100 | { |
||
| 101 | /** |
||
| 102 | * Track the state of loaded metadata for the structure. |
||
| 103 | * |
||
| 104 | * @var boolean |
||
| 105 | */ |
||
| 106 | private $isStructureFinalized = false; |
||
| 107 | |||
| 108 | /** |
||
| 109 | * The metadata interfaces to use as the structure. |
||
| 110 | * |
||
| 111 | * These are paths (PSR-4) to import. |
||
| 112 | * |
||
| 113 | * @var array |
||
| 114 | */ |
||
| 115 | private $structureInterfaces = []; |
||
| 116 | |||
| 117 | /** |
||
| 118 | * Store the property's structure. |
||
| 119 | * |
||
| 120 | * @var MetadataInterface|array|null |
||
| 121 | */ |
||
| 122 | private $structureMetadata; |
||
| 123 | |||
| 124 | /** |
||
| 125 | * Store the property's "terminal" structure. |
||
| 126 | * |
||
| 127 | * This represents the value of "structure_metadata" key on a property definition. |
||
| 128 | * This should always be merged last, after the interfaces are imported. |
||
| 129 | * |
||
| 130 | * @var MetadataInterface|array|null |
||
| 131 | */ |
||
| 132 | private $terminalStructureMetadata; |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Store the property's model prototype. |
||
| 136 | * |
||
| 137 | * @var ArrayAccess|DescribableInterface|null |
||
| 138 | */ |
||
| 139 | private $structurePrototype; |
||
| 140 | |||
| 141 | /** |
||
| 142 | * The class name of the "structure" collection to use. |
||
| 143 | * |
||
| 144 | * Must be a fully-qualified PHP namespace and an implementation of {@see ArrayAccess}. |
||
| 145 | * |
||
| 146 | * @var string |
||
| 147 | */ |
||
| 148 | private $structureModelClass = StructureModel::class; |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Store the factory instance. |
||
| 152 | * |
||
| 153 | * @var FactoryInterface |
||
| 154 | */ |
||
| 155 | protected $structureModelFactory; |
||
| 156 | |||
| 157 | /** |
||
| 158 | * Retrieve the property's type identifier. |
||
| 159 | * |
||
| 160 | * @return string |
||
| 161 | */ |
||
| 162 | public function type() |
||
| 166 | |||
| 167 | /** |
||
| 168 | * Retrieve the property's structure. |
||
| 169 | * |
||
| 170 | * @return MetadataInterface|null |
||
| 171 | */ |
||
| 172 | public function getStructureMetadata() |
||
| 173 | { |
||
| 174 | if ($this->structureMetadata === null || $this->isStructureFinalized === false) { |
||
| 175 | $this->structureMetadata = $this->loadStructureMetadata(); |
||
| 176 | } |
||
| 177 | |||
| 178 | return $this->structureMetadata; |
||
| 179 | } |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Set the property's structure. |
||
| 183 | * |
||
| 184 | * @param MetadataInterface|array|null $data The property's structure (fields, data). |
||
| 185 | * @throws InvalidArgumentException If the structure is invalid. |
||
| 186 | * @return self |
||
| 187 | */ |
||
| 188 | public function setStructureMetadata($data) |
||
| 214 | |||
| 215 | /** |
||
| 216 | * Retrieve the metadata interfaces used by the property as a structure. |
||
| 217 | * |
||
| 218 | * @return array |
||
| 219 | */ |
||
| 220 | public function getStructureInterfaces() |
||
| 221 | { |
||
| 222 | if (empty($this->structureInterfaces)) { |
||
| 223 | return $this->structureInterfaces; |
||
| 224 | } |
||
| 225 | |||
| 226 | return array_keys($this->structureInterfaces); |
||
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Set the given metadata interfaces for the property to use as a structure. |
||
| 231 | * |
||
| 232 | * @param array $interfaces One or more metadata interfaces to use. |
||
| 233 | * @return self |
||
| 234 | */ |
||
| 235 | public function setStructureInterfaces(array $interfaces) |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Add the given metadata interfaces for the property to use as a structure. |
||
| 246 | * |
||
| 247 | * @param array $interfaces One or more metadata interfaces to use. |
||
| 248 | * @return self |
||
| 249 | */ |
||
| 250 | public function addStructureInterfaces(array $interfaces) |
||
| 258 | |||
| 259 | /** |
||
| 260 | * Add the given metadata interfaces for the property to use as a structure. |
||
| 261 | * |
||
| 262 | * @param string $interface A metadata interface to use. |
||
| 263 | * @throws InvalidArgumentException If the interface is not a string. |
||
| 264 | * @return self |
||
| 265 | */ |
||
| 266 | public function addStructureInterface($interface) |
||
| 284 | |||
| 285 | /** |
||
| 286 | * Load the property's structure. |
||
| 287 | * |
||
| 288 | * @return MetadataInterface |
||
| 289 | */ |
||
| 290 | protected function loadStructureMetadata() |
||
| 324 | |||
| 325 | /** |
||
| 326 | * Retrieve a singleton of the structure model for prototyping. |
||
| 327 | * |
||
| 328 | * @return ArrayAccess|DescribableInterface |
||
| 329 | */ |
||
| 330 | public function structureProto() |
||
| 344 | |||
| 345 | /** |
||
| 346 | * Set the class name of the data-model structure. |
||
| 347 | * |
||
| 348 | * @param string $className The class name of the structure. |
||
| 349 | * @throws InvalidArgumentException If the class name is not a string. |
||
| 350 | * @return self |
||
| 351 | */ |
||
| 352 | protected function setStructureModelClass($className) |
||
| 364 | |||
| 365 | /** |
||
| 366 | * Retrieve the class name of the data-model structure. |
||
| 367 | * |
||
| 368 | * @return string |
||
| 369 | */ |
||
| 370 | public function getStructureModelClass() |
||
| 374 | |||
| 375 | /** |
||
| 376 | * Convert the given value into a structure. |
||
| 377 | * |
||
| 378 | * Options: |
||
| 379 | * - `default_data` (_boolean_|_array_) — If TRUE, the default data defined |
||
| 380 | * in the structure's metadata is merged. If an array, that is merged. |
||
| 381 | * |
||
| 382 | * @param mixed $val The value to "structurize". |
||
| 383 | * @param array|MetadataInterface $options Optional structure options. |
||
| 384 | * @throws InvalidArgumentException If the options are invalid. |
||
| 385 | * @return ModelInterface|ModelInterface[] |
||
| 386 | */ |
||
| 387 | public function structureVal($val, $options = []) |
||
| 437 | |||
| 438 | /** |
||
| 439 | * Retrieve the structure as a plain array. |
||
| 440 | * |
||
| 441 | * @return array |
||
| 442 | */ |
||
| 443 | public function toStructure() |
||
| 447 | |||
| 448 | /** |
||
| 449 | * @param null|string $model Model ident. |
||
| 450 | * @return ArrayAccess|DescribableInterface|mixed |
||
| 451 | * @throws UnexpectedValueException If the structure is invalid. |
||
| 452 | */ |
||
| 453 | View Code Duplication | public function toModel($model = null) |
|
| 471 | |||
| 472 | /** |
||
| 473 | * PropertyInterface::save(). |
||
| 474 | * @param mixed $val The value, at time of saving. |
||
| 475 | * @return mixed |
||
| 476 | */ |
||
| 477 | public function save($val) |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Inject dependencies from a DI Container. |
||
| 507 | * |
||
| 508 | * @param Container $container A dependencies container instance. |
||
| 509 | * @return void |
||
| 510 | */ |
||
| 511 | protected function setDependencies(Container $container) |
||
| 517 | |||
| 518 | /** |
||
| 519 | * Retrieve the structure model factory. |
||
| 520 | * |
||
| 521 | * @throws RuntimeException If the model factory was not previously set. |
||
| 522 | * @return FactoryInterface |
||
| 523 | */ |
||
| 524 | protected function structureModelFactory() |
||
| 535 | |||
| 536 | /** |
||
| 537 | * Set an structure model factory. |
||
| 538 | * |
||
| 539 | * @param FactoryInterface $factory The model factory, to create objects. |
||
| 540 | * @return self |
||
| 541 | */ |
||
| 542 | private function setStructureModelFactory(FactoryInterface $factory) |
||
| 548 | |||
| 549 | /** |
||
| 550 | * Parse a metadata identifier from given interface. |
||
| 551 | * |
||
| 552 | * Change `\` and `.` to `/` and force lowercase |
||
| 553 | * |
||
| 554 | * @param string $interface A metadata interface to convert. |
||
| 555 | * @return string |
||
| 556 | */ |
||
| 557 | protected function parseStructureInterface($interface) |
||
| 564 | |||
| 565 | /** |
||
| 566 | * Create a new metadata object for structures. |
||
| 567 | * |
||
| 568 | * Similar to {@see \Charcoal\Model\DescribableTrait::createMetadata()}. |
||
| 569 | * |
||
| 570 | * @return MetadataInterface |
||
| 571 | */ |
||
| 572 | protected function createStructureMetadata() |
||
| 577 | |||
| 578 | /** |
||
| 579 | * Retrieve the class name of the metadata object. |
||
| 580 | * |
||
| 581 | * @return string |
||
| 582 | */ |
||
| 583 | protected function getStructureMetadataClass() |
||
| 587 | |||
| 588 | /** |
||
| 589 | * Create a data-model structure. |
||
| 590 | * |
||
| 591 | * @todo Add support for simple {@see ArrayAccess} models. |
||
| 592 | * @throws UnexpectedValueException If the structure is invalid. |
||
| 593 | * @return ArrayAccess |
||
| 594 | */ |
||
| 595 | View Code Duplication | private function createStructureModel() |
|
| 610 | |||
| 611 | /** |
||
| 612 | * Create a data-model structure. |
||
| 613 | * |
||
| 614 | * @param MetadataInterface $metadata The model's definition. |
||
| 615 | * @param array ...$datasets The dataset(s) to modelize. |
||
| 616 | * @throws UnexpectedValueException If the structure is invalid. |
||
| 617 | * @return DescribableInterface |
||
| 618 | */ |
||
| 619 | private function createStructureModelWith( |
||
| 642 | } |
||
| 643 |