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: