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 AttachmentAwareTrait 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 AttachmentAwareTrait, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 33 | trait AttachmentAwareTrait |
||
| 34 | { |
||
| 35 | /** |
||
| 36 | * A store of cached attachments, by ID. |
||
| 37 | * |
||
| 38 | * @var Attachment[] $attachmentCache |
||
| 39 | */ |
||
| 40 | protected static $attachmentCache = []; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * Store a collection of node objects. |
||
| 44 | * |
||
| 45 | * @var Collection|Attachment[] |
||
| 46 | */ |
||
| 47 | protected $attachments = []; |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Retrieve the objects associated to the current object. |
||
| 51 | * |
||
| 52 | * @param array|string|null $group Filter the attachments by a group identifier. |
||
| 53 | * When an array, filter the attachments by a options list. |
||
| 54 | * @param string|null $type Filter the attachments by type. |
||
| 55 | * @param callable|null $before Process each attachment before applying data. |
||
| 56 | * @param callable|null $after Process each attachment after applying data. |
||
| 57 | * @throws InvalidArgumentException If the $group or $type is invalid. |
||
| 58 | * @return Collection|Attachment[] |
||
| 59 | */ |
||
| 60 | public function getAttachments( |
||
| 194 | |||
| 195 | /** |
||
| 196 | * Determine if the current object has any nodes. |
||
| 197 | * |
||
| 198 | * @return boolean Whether $this has any nodes (TRUE) or not (FALSE). |
||
| 199 | */ |
||
| 200 | public function hasAttachments() |
||
| 204 | |||
| 205 | /** |
||
| 206 | * Count the number of nodes associated to the current object. |
||
| 207 | * |
||
| 208 | * @return integer |
||
| 209 | */ |
||
| 210 | public function numAttachments() |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Attach an node to the current object. |
||
| 219 | * |
||
| 220 | * @param AttachableInterface|ModelInterface $attachment An attachment or object. |
||
| 221 | * @param string $group Attachment group, defaults to contents. |
||
| 222 | * @return boolean|self |
||
| 223 | */ |
||
| 224 | public function addAttachment($attachment, $group = 'contents') |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Remove all joins linked to a specific attachment. |
||
| 248 | * |
||
| 249 | * @deprecated in favour of AttachmentAwareTrait::removeAttachmentJoins() |
||
| 250 | * @return boolean |
||
| 251 | */ |
||
| 252 | public function removeJoins() |
||
| 262 | |||
| 263 | /** |
||
| 264 | * Remove all joins linked to a specific attachment. |
||
| 265 | * |
||
| 266 | * @return boolean |
||
| 267 | */ |
||
| 268 | public function removeAttachmentJoins() |
||
| 286 | |||
| 287 | /** |
||
| 288 | * Delete the objects associated to the current object. |
||
| 289 | * |
||
| 290 | * @param array $options Filter the attachments by an option list. |
||
| 291 | * @return boolean |
||
| 292 | */ |
||
| 293 | public function deleteAttachments(array $options = []) |
||
| 301 | |||
| 302 | /** |
||
| 303 | * Available attachment obj_type related to the current object. |
||
| 304 | * This goes throught the entire forms / form groups, starting from the |
||
| 305 | * dashboard widgets. |
||
| 306 | * Returns an array of object classes by group |
||
| 307 | * [ |
||
| 308 | * group : [ |
||
| 309 | * 'object\type', |
||
| 310 | * 'object\type2', |
||
| 311 | * 'object\type3' |
||
| 312 | * ] |
||
| 313 | * ] |
||
| 314 | * @return array Attachment obj_types. |
||
| 315 | */ |
||
| 316 | public function attachmentObjTypes() |
||
| 363 | |||
| 364 | /** |
||
| 365 | * Parse a given options for loading a collection of attachments. |
||
| 366 | * |
||
| 367 | * @param array $options A list of options. |
||
| 368 | * Option keys not present in {@see self::getDefaultAttachmentOptions() default options} |
||
| 369 | * are rejected. |
||
| 370 | * @return array |
||
| 371 | */ |
||
| 372 | protected function parseAttachmentOptions(array $options) |
||
| 382 | |||
| 383 | /** |
||
| 384 | * Parse a given options for loading a collection of attachments. |
||
| 385 | * |
||
| 386 | * @param mixed $val The option value. |
||
| 387 | * @param string $key The option key. |
||
| 388 | * @return boolean Return TRUE if the value is preserved. Otherwise FALSE. |
||
| 389 | */ |
||
| 390 | protected function filterAttachmentOption($val, $key) |
||
| 407 | |||
| 408 | /** |
||
| 409 | * Retrieve the default options for loading a collection of attachments. |
||
| 410 | * |
||
| 411 | * @return array |
||
| 412 | */ |
||
| 413 | protected function getDefaultAttachmentOptions() |
||
| 423 | |||
| 424 | |||
| 425 | |||
| 426 | // Abstract Methods |
||
| 427 | // ========================================================================= |
||
| 428 | |||
| 429 | /** |
||
| 430 | * Retrieve the object's unique ID. |
||
| 431 | * |
||
| 432 | * @return mixed |
||
| 433 | */ |
||
| 434 | abstract public function id(); |
||
| 435 | |||
| 436 | /** |
||
| 437 | * Retrieve the object model factory. |
||
| 438 | * |
||
| 439 | * @return \Charcoal\Factory\FactoryInterface |
||
| 440 | */ |
||
| 441 | abstract public function modelFactory(); |
||
| 442 | |||
| 443 | /** |
||
| 444 | * Retrieve the model collection loader. |
||
| 445 | * |
||
| 446 | * @return \Charcoal\Loader\CollectionLoader |
||
| 447 | */ |
||
| 448 | abstract public function collectionLoader(); |
||
| 449 | } |
||
| 450 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: