Complex classes like TranslatableBootForm 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 TranslatableBootForm, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Propaganistas\LaravelTranslatableBootForms; |
||
3 | class TranslatableBootForm |
||
4 | { |
||
5 | |||
6 | /** |
||
7 | * BootForm implementation. |
||
8 | * |
||
9 | * @var \AdamWathan\BootForms\BootForm |
||
10 | */ |
||
11 | protected $form; |
||
12 | |||
13 | /** |
||
14 | * Array holding config values. |
||
15 | * |
||
16 | * @var array |
||
17 | */ |
||
18 | protected $config; |
||
19 | |||
20 | /** |
||
21 | * Array of locale keys. |
||
22 | * |
||
23 | * @var array |
||
24 | */ |
||
25 | protected $locales; |
||
26 | |||
27 | /** |
||
28 | * The current element type this class is working on. |
||
29 | * |
||
30 | * @var string |
||
31 | */ |
||
32 | protected $element; |
||
33 | |||
34 | /** |
||
35 | * The array of arguments to pass in when creating the element. |
||
36 | * |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $arguments = []; |
||
40 | |||
41 | /** |
||
42 | * A keyed array of method => arguments to call on the created input. |
||
43 | * |
||
44 | * @var array |
||
45 | */ |
||
46 | protected $methods = []; |
||
47 | |||
48 | /** |
||
49 | * Boolean indicating if the element should be cloned with corresponding translation name attributes. |
||
50 | * |
||
51 | * @var bool |
||
52 | */ |
||
53 | protected $cloneElement = false; |
||
54 | |||
55 | /** |
||
56 | * Boolean indicating if the element should have an indication that is it a translation. |
||
57 | * |
||
58 | * @var bool |
||
59 | */ |
||
60 | protected $translatableIndicator = false; |
||
61 | |||
62 | /** |
||
63 | * Array holding the mappable element arguments. |
||
64 | * |
||
65 | * @var array |
||
66 | */ |
||
67 | private $mappableArguments = [ |
||
68 | 'text' => ['label', 'name', 'value'], |
||
69 | 'textarea' => ['label', 'name'], |
||
70 | 'password' => ['label', 'name'], |
||
71 | 'date' => ['label', 'name', 'value'], |
||
72 | 'email' => ['label', 'name', 'value'], |
||
73 | 'file' => ['label', 'name', 'value'], |
||
74 | 'inputGroup' => ['label', 'name', 'value'], |
||
75 | 'radio' => ['label', 'name', 'value'], |
||
76 | 'inlineRadio' => ['label', 'name', 'value'], |
||
77 | 'checkbox' => ['label', 'name'], |
||
78 | 'inlineCheckbox' => ['label', 'name'], |
||
79 | 'select' => ['label', 'name', 'options'], |
||
80 | 'button' => ['label', 'name', 'type'], |
||
81 | 'submit' => ['value', 'type'], |
||
82 | 'hidden' => ['name'], |
||
83 | 'label' => ['label'], |
||
84 | 'open' => [], |
||
85 | 'openHorizontal' => ['columnSizes'], |
||
86 | 'bind' => ['model'], |
||
87 | 'close' => [], |
||
88 | ]; |
||
89 | |||
90 | /** |
||
91 | * Array holding the methods to call during element behavior processing. |
||
92 | * |
||
93 | * @var array |
||
94 | */ |
||
95 | private $elementBehaviors = [ |
||
96 | 'text' => ['cloneElement', 'translatableIndicator'], |
||
97 | 'textarea' => ['cloneElement', 'translatableIndicator'], |
||
98 | 'password' => ['cloneElement', 'translatableIndicator'], |
||
99 | 'date' => ['cloneElement', 'translatableIndicator'], |
||
100 | 'email' => ['cloneElement', 'translatableIndicator'], |
||
101 | 'file' => ['cloneElement', 'translatableIndicator'], |
||
102 | 'inputGroup' => ['cloneElement', 'translatableIndicator'], |
||
103 | 'radio' => ['cloneElement', 'translatableIndicator'], |
||
104 | 'inlineRadio' => ['cloneElement', 'translatableIndicator'], |
||
105 | 'checkbox' => ['cloneElement', 'translatableIndicator'], |
||
106 | 'inlineCheckbox' => ['cloneElement', 'translatableIndicator'], |
||
107 | 'select' => ['cloneElement', 'translatableIndicator'], |
||
108 | 'button' => ['cloneElement'], |
||
109 | 'submit' => ['cloneElement'], |
||
110 | 'hidden' => ['cloneElement'], |
||
111 | 'label' => [], |
||
112 | 'open' => [], |
||
113 | 'openHorizontal' => [], |
||
114 | 'close' => [], |
||
115 | ]; |
||
116 | |||
117 | /** |
||
118 | * Form constructor. |
||
119 | * |
||
120 | * @param object $form |
||
121 | */ |
||
122 | 36 | public function __construct($form) |
|
127 | |||
128 | /** |
||
129 | * Magic __call method. |
||
130 | * |
||
131 | * @param string $method |
||
132 | * @param array $parameters |
||
133 | * @return \Propaganistas\LaravelTranslatableBootForms\TranslatableBootForm |
||
134 | */ |
||
135 | 33 | public function __call($method, $parameters) |
|
153 | |||
154 | /** |
||
155 | * Magic __toString method. |
||
156 | * |
||
157 | * @return string |
||
158 | */ |
||
159 | 3 | public function __toString() |
|
163 | |||
164 | /** |
||
165 | * Resets the properties. |
||
166 | * |
||
167 | * @return $this |
||
168 | */ |
||
169 | 33 | protected function reset() |
|
179 | |||
180 | /** |
||
181 | * Get or set the available locales. |
||
182 | * |
||
183 | * @param array|null $locales |
||
184 | * @return array |
||
185 | */ |
||
186 | 36 | public function locales(array $locales = null) |
|
192 | |||
193 | /** |
||
194 | * Get or set the current element. |
||
195 | * |
||
196 | * @param string|null $element |
||
197 | * @return string |
||
198 | */ |
||
199 | 33 | protected function element($element = null) |
|
205 | |||
206 | /** |
||
207 | * Get or set the arguments. |
||
208 | * |
||
209 | * @param array|null $arguments |
||
210 | * @return array |
||
211 | */ |
||
212 | 33 | protected function arguments(array $arguments = null) |
|
218 | |||
219 | /** |
||
220 | * Get or set the methods. |
||
221 | * |
||
222 | * @param array|null $methods |
||
223 | * @return array |
||
224 | */ |
||
225 | 30 | protected function methods(array $methods = null) |
|
231 | |||
232 | /** |
||
233 | * Get or set the current element. |
||
234 | * |
||
235 | * @param bool|null $clone |
||
236 | * @return bool |
||
237 | */ |
||
238 | 33 | protected function cloneElement($clone = null) |
|
244 | |||
245 | /** |
||
246 | * Get or set the translatable indicator boolean. |
||
247 | * |
||
248 | * @param bool|null $add |
||
249 | * @return bool |
||
250 | */ |
||
251 | 15 | protected function translatableIndicator($add = null) |
|
257 | |||
258 | /** |
||
259 | * Overwrites an argument. |
||
260 | * |
||
261 | * @param string $argument |
||
262 | * @param string|array $value |
||
263 | */ |
||
264 | 15 | protected function overwriteArgument($argument, $value) |
|
272 | |||
273 | /** |
||
274 | * Adds a method. |
||
275 | * |
||
276 | * @param string $name |
||
277 | * @param string|array $parameters |
||
278 | */ |
||
279 | 15 | protected function addMethod($name, $parameters) |
|
289 | |||
290 | /** |
||
291 | * Renders the current translatable form element. |
||
292 | * |
||
293 | * @return string |
||
294 | */ |
||
295 | 33 | public function render() |
|
328 | |||
329 | /** |
||
330 | * Creates an input element using the supplied arguments and methods. |
||
331 | * |
||
332 | * @param string|null $currentLocale |
||
333 | * @return mixed |
||
334 | */ |
||
335 | 33 | protected function createInput($currentLocale = null) |
|
370 | |||
371 | /** |
||
372 | * Replaces %name recursively with the proper input name. |
||
373 | * |
||
374 | * @param $parameter |
||
375 | * @return mixed |
||
376 | */ |
||
377 | 15 | protected function replaceInputNameRecursively($parameter) |
|
387 | |||
388 | /** |
||
389 | * Add specific element behavior to the current translatable form element. |
||
390 | */ |
||
391 | 33 | protected function applyElementBehavior() |
|
399 | |||
400 | /** |
||
401 | * Maps the form element arguments to their name. |
||
402 | * |
||
403 | * @param array $arguments |
||
404 | * @return array |
||
405 | */ |
||
406 | 33 | protected function mapArguments(array $arguments) |
|
412 | |||
413 | /** |
||
414 | * Add a locale indicator to the label. |
||
415 | * |
||
416 | * @param string $locale |
||
417 | */ |
||
418 | 15 | protected function setTranslatableLabelIndicator($locale) |
|
423 | |||
424 | } |
||
425 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.