Complex classes like HasAttributes 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 HasAttributes, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | trait HasAttributes |
||
16 | { |
||
17 | /** |
||
18 | * The model's attributes. |
||
19 | * |
||
20 | * @var array |
||
21 | */ |
||
22 | protected $attributes = []; |
||
23 | |||
24 | /** |
||
25 | * The model attribute's original state. |
||
26 | * |
||
27 | * @var array |
||
28 | */ |
||
29 | protected $original = []; |
||
30 | |||
31 | /** |
||
32 | * The changed model attributes. |
||
33 | * |
||
34 | * @var array |
||
35 | */ |
||
36 | protected $changes = []; |
||
37 | |||
38 | /** |
||
39 | * The attributes that should be cast to native types. |
||
40 | * |
||
41 | * @var array |
||
42 | */ |
||
43 | protected $casts = []; |
||
44 | |||
45 | /** |
||
46 | * The attributes that should be mutated to dates. |
||
47 | * |
||
48 | * @var array |
||
49 | */ |
||
50 | protected $dates = []; |
||
51 | |||
52 | /** |
||
53 | * The storage format of the model's date columns. |
||
54 | * |
||
55 | * @var string |
||
56 | */ |
||
57 | protected $dateFormat; |
||
58 | |||
59 | /** |
||
60 | * The accessors to append to the model's array form. |
||
61 | * |
||
62 | * @var array |
||
63 | */ |
||
64 | protected $appends = []; |
||
65 | |||
66 | /** |
||
67 | * Indicates whether attributes are snake cased on arrays. |
||
68 | * |
||
69 | * @var bool |
||
70 | */ |
||
71 | public static $snakeAttributes = true; |
||
72 | |||
73 | /** |
||
74 | * The cache of the mutated attributes for each class. |
||
75 | * |
||
76 | * @var array |
||
77 | */ |
||
78 | protected static $mutatorCache = []; |
||
79 | |||
80 | /** |
||
81 | * Convert the model's attributes to an array. |
||
82 | * |
||
83 | * @return array |
||
84 | */ |
||
85 | public function attributesToArray() |
||
114 | |||
115 | /** |
||
116 | * Add the date attributes to the attributes array. |
||
117 | * |
||
118 | * @param array $attributes |
||
119 | * @return array |
||
120 | */ |
||
121 | protected function addDateAttributesToArray(array $attributes) |
||
135 | |||
136 | /** |
||
137 | * Add the mutated attributes to the attributes array. |
||
138 | * |
||
139 | * @param array $attributes |
||
140 | * @param array $mutatedAttributes |
||
141 | * @return array |
||
142 | */ |
||
143 | protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes) |
||
163 | |||
164 | /** |
||
165 | * Add the casted attributes to the attributes array. |
||
166 | * |
||
167 | * @param array $attributes |
||
168 | * @param array $mutatedAttributes |
||
169 | * @return array |
||
170 | */ |
||
171 | protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes) |
||
200 | |||
201 | /** |
||
202 | * Get an attribute array of all arrayable attributes. |
||
203 | * |
||
204 | * @return array |
||
205 | */ |
||
206 | protected function getArrayableAttributes() |
||
210 | |||
211 | /** |
||
212 | * Get all of the appendable values that are arrayable. |
||
213 | * |
||
214 | * @return array |
||
215 | */ |
||
216 | protected function getArrayableAppends() |
||
226 | |||
227 | /** |
||
228 | * Get the model's relationships in array form. |
||
229 | * |
||
230 | * @return array |
||
231 | */ |
||
232 | public function relationsToArray() |
||
270 | |||
271 | /** |
||
272 | * Get an attribute array of all arrayable relations. |
||
273 | * |
||
274 | * @return array |
||
275 | */ |
||
276 | protected function getArrayableRelations() |
||
280 | |||
281 | /** |
||
282 | * Get an attribute array of all arrayable values. |
||
283 | * |
||
284 | * @param array $values |
||
285 | * @return array |
||
286 | */ |
||
287 | protected function getArrayableItems(array $values) |
||
299 | |||
300 | /** |
||
301 | * Get an attribute from the model. |
||
302 | * |
||
303 | * @param string $key |
||
304 | * @return mixed |
||
305 | */ |
||
306 | public function getAttribute($key) |
||
329 | |||
330 | /** |
||
331 | * Get a plain attribute (not a relationship). |
||
332 | * |
||
333 | * @param string $key |
||
334 | * @return mixed |
||
335 | */ |
||
336 | public function getAttributeValue($key) |
||
364 | |||
365 | /** |
||
366 | * Get an attribute from the $attributes array. |
||
367 | * |
||
368 | * @param string $key |
||
369 | * @return mixed |
||
370 | */ |
||
371 | protected function getAttributeFromArray($key) |
||
377 | |||
378 | /** |
||
379 | * Get a relationship. |
||
380 | * |
||
381 | * @param string $key |
||
382 | * @return mixed |
||
383 | */ |
||
384 | public function getRelationValue($key) |
||
400 | |||
401 | /** |
||
402 | * Get a relationship value from a method. |
||
403 | * |
||
404 | * @param string $method |
||
405 | * @return mixed |
||
406 | * |
||
407 | * @throws \LogicException |
||
408 | */ |
||
409 | protected function getRelationshipFromMethod($method) |
||
423 | |||
424 | /** |
||
425 | * Determine if a get mutator exists for an attribute. |
||
426 | * |
||
427 | * @param string $key |
||
428 | * @return bool |
||
429 | */ |
||
430 | public function hasGetMutator($key) |
||
434 | |||
435 | /** |
||
436 | * Get the value of an attribute using its mutator. |
||
437 | * |
||
438 | * @param string $key |
||
439 | * @param mixed $value |
||
440 | * @return mixed |
||
441 | */ |
||
442 | protected function mutateAttribute($key, $value) |
||
446 | |||
447 | /** |
||
448 | * Get the value of an attribute using its mutator for array conversion. |
||
449 | * |
||
450 | * @param string $key |
||
451 | * @param mixed $value |
||
452 | * @return mixed |
||
453 | */ |
||
454 | protected function mutateAttributeForArray($key, $value) |
||
460 | |||
461 | /** |
||
462 | * Cast an attribute to a native PHP type. |
||
463 | * |
||
464 | * @param string $key |
||
465 | * @param mixed $value |
||
466 | * @return mixed |
||
467 | */ |
||
468 | protected function castAttribute($key, $value) |
||
505 | |||
506 | /** |
||
507 | * Get the type of cast for a model attribute. |
||
508 | * |
||
509 | * @param string $key |
||
510 | * @return string |
||
511 | */ |
||
512 | protected function getCastType($key) |
||
520 | |||
521 | /** |
||
522 | * Determine if the cast type is a custom date time cast. |
||
523 | * |
||
524 | * @param string $cast |
||
525 | * @return bool |
||
526 | */ |
||
527 | protected function isCustomDateTimeCast($cast) |
||
528 | { |
||
529 | return strncmp($cast, 'date:', 5) === 0 || |
||
530 | strncmp($cast, 'datetime:', 9) === 0; |
||
531 | } |
||
532 | |||
533 | /** |
||
534 | * Set a given attribute on the model. |
||
535 | * |
||
536 | * @param string $key |
||
537 | * @param mixed $value |
||
538 | * @return mixed |
||
539 | */ |
||
540 | public function setAttribute($key, $value) |
||
571 | |||
572 | /** |
||
573 | * Determine if a set mutator exists for an attribute. |
||
574 | * |
||
575 | * @param string $key |
||
576 | * @return bool |
||
577 | */ |
||
578 | public function hasSetMutator($key) |
||
582 | |||
583 | /** |
||
584 | * Set the value of an attribute using its mutator. |
||
585 | * |
||
586 | * @param string $key |
||
587 | * @param mixed $value |
||
588 | * @return mixed |
||
589 | */ |
||
590 | protected function setMutatedAttributeValue($key, $value) |
||
594 | |||
595 | /** |
||
596 | * Determine if the given attribute is a date or date castable. |
||
597 | * |
||
598 | * @param string $key |
||
599 | * @return bool |
||
600 | */ |
||
601 | protected function isDateAttribute($key) |
||
606 | |||
607 | /** |
||
608 | * Set a given JSON attribute on the model. |
||
609 | * |
||
610 | * @param string $key |
||
611 | * @param mixed $value |
||
612 | * @return $this |
||
613 | */ |
||
614 | public function fillJsonAttribute($key, $value) |
||
624 | |||
625 | /** |
||
626 | * Get an array attribute with the given key and value set. |
||
627 | * |
||
628 | * @param string $path |
||
629 | * @param string $key |
||
630 | * @param mixed $value |
||
631 | * @return $this |
||
632 | */ |
||
633 | protected function getArrayAttributeWithValue($path, $key, $value) |
||
639 | |||
640 | /** |
||
641 | * Get an array attribute or return an empty array if it is not set. |
||
642 | * |
||
643 | * @param string $key |
||
644 | * @return array |
||
645 | */ |
||
646 | protected function getArrayAttributeByKey($key) |
||
651 | |||
652 | /** |
||
653 | * Cast the given attribute to JSON. |
||
654 | * |
||
655 | * @param string $key |
||
656 | * @param mixed $value |
||
657 | * @return string |
||
658 | */ |
||
659 | protected function castAttributeAsJson($key, $value) |
||
671 | |||
672 | /** |
||
673 | * Encode the given value as JSON. |
||
674 | * |
||
675 | * @param mixed $value |
||
676 | * @return string |
||
677 | */ |
||
678 | protected function asJson($value) |
||
682 | |||
683 | /** |
||
684 | * Decode the given JSON back into an array or object. |
||
685 | * |
||
686 | * @param string $value |
||
687 | * @param bool $asObject |
||
688 | * @return mixed |
||
689 | */ |
||
690 | public function fromJson($value, $asObject = false) |
||
694 | |||
695 | /** |
||
696 | * Return a timestamp as DateTime object with time set to 00:00:00. |
||
697 | * |
||
698 | * @param mixed $value |
||
699 | * @return \Illuminate\Support\Carbon |
||
700 | */ |
||
701 | protected function asDate($value) |
||
705 | |||
706 | /** |
||
707 | * Return a timestamp as DateTime object. |
||
708 | * |
||
709 | * @param mixed $value |
||
710 | * @return \Illuminate\Support\Carbon |
||
711 | */ |
||
712 | protected function asDateTime($value) |
||
751 | |||
752 | /** |
||
753 | * Determine if the given value is a standard date format. |
||
754 | * |
||
755 | * @param string $value |
||
756 | * @return bool |
||
757 | */ |
||
758 | protected function isStandardDateFormat($value) |
||
762 | |||
763 | /** |
||
764 | * Convert a DateTime to a storable string. |
||
765 | * |
||
766 | * @param \DateTime|int $value |
||
767 | * @return string |
||
768 | */ |
||
769 | public function fromDateTime($value) |
||
775 | |||
776 | /** |
||
777 | * Return a timestamp as unix timestamp. |
||
778 | * |
||
779 | * @param mixed $value |
||
780 | * @return int |
||
781 | */ |
||
782 | protected function asTimestamp($value) |
||
786 | |||
787 | /** |
||
788 | * Prepare a date for array / JSON serialization. |
||
789 | * |
||
790 | * @param \DateTimeInterface $date |
||
791 | * @return string |
||
792 | */ |
||
793 | protected function serializeDate(DateTimeInterface $date) |
||
797 | |||
798 | /** |
||
799 | * Get the attributes that should be converted to dates. |
||
800 | * |
||
801 | * @return array |
||
802 | */ |
||
803 | public function getDates() |
||
811 | |||
812 | /** |
||
813 | * Get the format for database stored dates. |
||
814 | * |
||
815 | * @return string |
||
816 | */ |
||
817 | public function getDateFormat() |
||
821 | |||
822 | /** |
||
823 | * Set the date format used by the model. |
||
824 | * |
||
825 | * @param string $format |
||
826 | * @return $this |
||
827 | */ |
||
828 | public function setDateFormat($format) |
||
834 | |||
835 | /** |
||
836 | * Determine whether an attribute should be cast to a native type. |
||
837 | * |
||
838 | * @param string $key |
||
839 | * @param array|string|null $types |
||
840 | * @return bool |
||
841 | */ |
||
842 | public function hasCast($key, $types = null) |
||
850 | |||
851 | /** |
||
852 | * Get the casts array. |
||
853 | * |
||
854 | * @return array |
||
855 | */ |
||
856 | public function getCasts() |
||
864 | |||
865 | /** |
||
866 | * Determine whether a value is Date / DateTime castable for inbound manipulation. |
||
867 | * |
||
868 | * @param string $key |
||
869 | * @return bool |
||
870 | */ |
||
871 | protected function isDateCastable($key) |
||
875 | |||
876 | /** |
||
877 | * Determine whether a value is JSON castable for inbound manipulation. |
||
878 | * |
||
879 | * @param string $key |
||
880 | * @return bool |
||
881 | */ |
||
882 | protected function isJsonCastable($key) |
||
886 | |||
887 | /** |
||
888 | * Get all of the current attributes on the model. |
||
889 | * |
||
890 | * @return array |
||
891 | */ |
||
892 | public function getAttributes() |
||
896 | |||
897 | /** |
||
898 | * Set the array of model attributes. No checking is done. |
||
899 | * |
||
900 | * @param array $attributes |
||
901 | * @param bool $sync |
||
902 | * @return $this |
||
903 | */ |
||
904 | public function setRawAttributes(array $attributes, $sync = false) |
||
914 | |||
915 | /** |
||
916 | * Get the model's original attribute values. |
||
917 | * |
||
918 | * @param string|null $key |
||
919 | * @param mixed $default |
||
920 | * @return mixed|array |
||
921 | */ |
||
922 | public function getOriginal($key = null, $default = null) |
||
926 | |||
927 | /** |
||
928 | * Get a subset of the model's attributes. |
||
929 | * |
||
930 | * @param array|mixed $attributes |
||
931 | * @return array |
||
932 | */ |
||
933 | public function only($attributes) |
||
943 | |||
944 | /** |
||
945 | * Sync the original attributes with the current. |
||
946 | * |
||
947 | * @return $this |
||
948 | */ |
||
949 | public function syncOriginal() |
||
955 | |||
956 | /** |
||
957 | * Sync a single original attribute with its current value. |
||
958 | * |
||
959 | * @param string $attribute |
||
960 | * @return $this |
||
961 | */ |
||
962 | public function syncOriginalAttribute($attribute) |
||
968 | |||
969 | /** |
||
970 | * Sync the changed attributes. |
||
971 | * |
||
972 | * @return $this |
||
973 | */ |
||
974 | public function syncChanges() |
||
980 | |||
981 | /** |
||
982 | * Determine if the model or given attribute(s) have been modified. |
||
983 | * |
||
984 | * @param array|string|null $attributes |
||
985 | * @return bool |
||
986 | */ |
||
987 | public function isDirty($attributes = null) |
||
993 | |||
994 | /** |
||
995 | * Determine if the model or given attribute(s) have remained the same. |
||
996 | * |
||
997 | * @param array|string|null $attributes |
||
998 | * @return bool |
||
999 | */ |
||
1000 | public function isClean($attributes = null) |
||
1004 | |||
1005 | /** |
||
1006 | * Determine if the model or given attribute(s) have been modified. |
||
1007 | * |
||
1008 | * @param array|string|null $attributes |
||
1009 | * @return bool |
||
1010 | */ |
||
1011 | public function wasChanged($attributes = null) |
||
1017 | |||
1018 | /** |
||
1019 | * Determine if the given attributes were changed. |
||
1020 | * |
||
1021 | * @param array $changes |
||
1022 | * @param array|string|null $attributes |
||
1023 | * @return bool |
||
1024 | */ |
||
1025 | protected function hasChanges($changes, $attributes = null) |
||
1045 | |||
1046 | /** |
||
1047 | * Get the attributes that have been changed since last sync. |
||
1048 | * |
||
1049 | * @return array |
||
1050 | */ |
||
1051 | public function getDirty() |
||
1063 | |||
1064 | /** |
||
1065 | * Get the attributes that were changed. |
||
1066 | * |
||
1067 | * @return array |
||
1068 | */ |
||
1069 | public function getChanges() |
||
1073 | |||
1074 | /** |
||
1075 | * Determine if the new and old values for a given key are equivalent. |
||
1076 | * |
||
1077 | * @param string $key |
||
1078 | * @param mixed $current |
||
1079 | * @return bool |
||
1080 | */ |
||
1081 | protected function originalIsEquivalent($key, $current) |
||
1104 | |||
1105 | /** |
||
1106 | * Append attributes to query when building a query. |
||
1107 | * |
||
1108 | * @param array|string $attributes |
||
1109 | * @return $this |
||
1110 | */ |
||
1111 | public function append($attributes) |
||
1119 | |||
1120 | /** |
||
1121 | * Set the accessors to append to model arrays. |
||
1122 | * |
||
1123 | * @param array $appends |
||
1124 | * @return $this |
||
1125 | */ |
||
1126 | public function setAppends(array $appends) |
||
1132 | |||
1133 | /** |
||
1134 | * Get the mutated attributes for a given instance. |
||
1135 | * |
||
1136 | * @return array |
||
1137 | */ |
||
1138 | public function getMutatedAttributes() |
||
1148 | |||
1149 | /** |
||
1150 | * Extract and cache all the mutated attributes of a class. |
||
1151 | * |
||
1152 | * @param string $class |
||
1153 | * @return void |
||
1154 | */ |
||
1155 | public static function cacheMutatedAttributes($class) |
||
1161 | |||
1162 | /** |
||
1163 | * Get all of the attribute mutator methods. |
||
1164 | * |
||
1165 | * @param mixed $class |
||
1166 | * @return array |
||
1167 | */ |
||
1168 | protected static function getMutatorMethods($class) |
||
1174 | } |
||
1175 |
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: