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 Attribute 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 Attribute, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class Attribute extends Model implements AttributeContract |
||
14 | { |
||
15 | /** |
||
16 | * Attribute mutator instance. |
||
17 | * |
||
18 | * @var \Sofa\Eloquence\Contracts\Mutator |
||
19 | */ |
||
20 | protected static $attributeMutator; |
||
21 | |||
22 | /** |
||
23 | * Custom table name. |
||
24 | * |
||
25 | * @var string |
||
26 | */ |
||
27 | protected static $customTable; |
||
28 | |||
29 | /** |
||
30 | * The database table used by the model. |
||
31 | * |
||
32 | * @var string |
||
33 | */ |
||
34 | protected $table = 'meta_attributes'; |
||
35 | |||
36 | /** |
||
37 | * Indicates if the model should be timestamped. |
||
38 | * |
||
39 | * @var boolean |
||
40 | */ |
||
41 | public $timestamps = false; |
||
42 | |||
43 | /** |
||
44 | * The primary key for the model. |
||
45 | * |
||
46 | * @var string |
||
47 | */ |
||
48 | protected $primaryKey = 'meta_id'; |
||
49 | |||
50 | /** |
||
51 | * @var array |
||
52 | */ |
||
53 | protected $getterMutators = [ |
||
54 | 'array' => 'json_decode', |
||
55 | 'StdClass' => 'json_decode', |
||
56 | 'DateTime' => 'asDateTime', |
||
57 | Model::class => 'unserialize', |
||
58 | ]; |
||
59 | |||
60 | /** |
||
61 | * @var array |
||
62 | */ |
||
63 | protected $setterMutators = [ |
||
64 | 'array' => 'json_encode', |
||
65 | 'StdClass' => 'json_encode', |
||
66 | 'DateTime' => 'fromDateTime', |
||
67 | Model::class => 'serialize', |
||
68 | ]; |
||
69 | |||
70 | /** |
||
71 | * The attributes included in the model's JSON and array form. |
||
72 | * |
||
73 | * @var array |
||
74 | */ |
||
75 | protected $visible = ['meta_key', 'meta_value', 'meta_type']; |
||
76 | |||
77 | /** |
||
78 | * Create new attribute instance. |
||
79 | * |
||
80 | * @param string|array $key |
||
81 | * @param mixed $value |
||
82 | */ |
||
83 | public function __construct($key = null, $value = null) |
||
96 | |||
97 | /** |
||
98 | * Boot this model. |
||
99 | * |
||
100 | * @codeCoverageIgnore |
||
101 | * |
||
102 | * @return void |
||
103 | */ |
||
104 | protected static function boot() |
||
116 | |||
117 | /** |
||
118 | * Set the meta attribute. |
||
119 | * |
||
120 | * @param string $key |
||
121 | * @param mixed $value |
||
122 | */ |
||
123 | protected function set($key, $value) |
||
128 | |||
129 | /** |
||
130 | * Create new AttributeBag. |
||
131 | * |
||
132 | * @param array $models |
||
133 | * @return \Sofa\Eloquence\Metable\AttributeBag |
||
134 | */ |
||
135 | public function newBag(array $models = []) |
||
139 | |||
140 | /** |
||
141 | * Get the meta attribute value. |
||
142 | * |
||
143 | * @return mixed |
||
144 | */ |
||
145 | public function getValue() |
||
153 | |||
154 | /** |
||
155 | * Get the meta attribute key. |
||
156 | * |
||
157 | * @return string |
||
158 | */ |
||
159 | public function getMetaKey() |
||
163 | |||
164 | /** |
||
165 | * Cast value to proper type. |
||
166 | * |
||
167 | * @return mixed |
||
168 | */ |
||
169 | protected function castValue() |
||
181 | |||
182 | /** |
||
183 | * Set key of the meta attribute. |
||
184 | * |
||
185 | * @param string $key |
||
186 | * |
||
187 | * @throws \InvalidArgumentException |
||
188 | */ |
||
189 | protected function setMetaKey($key) |
||
197 | |||
198 | /** |
||
199 | * Set type of the meta attribute. |
||
200 | * |
||
201 | * @param mixed $value |
||
202 | */ |
||
203 | protected function setType($value) |
||
209 | |||
210 | /** |
||
211 | * Set value of the meta attribute. |
||
212 | * |
||
213 | * @param mixed $value |
||
214 | * |
||
215 | * @throws \Sofa\Eloquence\Metable\InvalidTypeException |
||
216 | */ |
||
217 | public function setValue($value) |
||
231 | |||
232 | /** |
||
233 | * Mutate attribute value. |
||
234 | * |
||
235 | * @param mixed $value |
||
236 | * @param string $dir |
||
237 | * @return mixed |
||
238 | */ |
||
239 | protected function mutateValue($value, $dir = 'setter') |
||
249 | |||
250 | /** |
||
251 | * Determine whether the value type can be set to string. |
||
252 | * |
||
253 | * @param mixed $value |
||
254 | * @return boolean |
||
255 | */ |
||
256 | protected function isStringable($value) |
||
260 | |||
261 | /** |
||
262 | * Get the value type. |
||
263 | * |
||
264 | * @param mixed $value |
||
265 | * @return string |
||
266 | */ |
||
267 | protected function getValueType($value) |
||
274 | |||
275 | /** |
||
276 | * Get the mutated type. |
||
277 | * |
||
278 | * @param mixed $value |
||
279 | * @param string $dir |
||
280 | * @return string |
||
281 | */ |
||
282 | protected function getMutatedType($value, $dir = 'setter') |
||
290 | |||
291 | /** |
||
292 | * Determine whether a mutator exists for the value type. |
||
293 | * |
||
294 | * @param mixed $value |
||
295 | * @param string $dir |
||
296 | * @return boolean |
||
297 | */ |
||
298 | protected function hasMutator($value, $dir = 'setter', $type = null) |
||
302 | |||
303 | /** |
||
304 | * Get mutator for the type. |
||
305 | * |
||
306 | * @param mixed $value |
||
307 | * @param string $dir |
||
308 | * @return string |
||
309 | */ |
||
310 | protected function getMutator($value, $dir = 'setter', $type = null) |
||
320 | |||
321 | /** |
||
322 | * Allow custom table name for meta attributes via config. |
||
323 | * |
||
324 | * @return string |
||
325 | */ |
||
326 | public function getTable() |
||
330 | |||
331 | /** |
||
332 | * Set custom table for the meta attributes. Allows doing it only once |
||
333 | * in order to mimic protected behaviour, most likely in the service |
||
334 | * provider, which in turn gets the table name from configuration. |
||
335 | * |
||
336 | * @param string $table |
||
337 | */ |
||
338 | public static function setCustomTable($table) |
||
344 | |||
345 | /** |
||
346 | * Handle casting value to string. |
||
347 | * |
||
348 | * @return string |
||
349 | */ |
||
350 | public function castToString() |
||
364 | |||
365 | /** |
||
366 | * Handle dynamic casting to string. |
||
367 | * |
||
368 | * @return string |
||
369 | */ |
||
370 | public function __toString() |
||
374 | } |
||
375 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.