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 PrgComponent 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 PrgComponent, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class PrgComponent extends Component { |
||
20 | |||
21 | /** |
||
22 | * Actions used to fetch the post data |
||
23 | * |
||
24 | * Maps the action that takes the post data and processes it by using this |
||
25 | * component and maps it to another action that is accessed by a redirect which |
||
26 | * has the post data attached as get data now |
||
27 | * |
||
28 | * array('search' => 'results'); |
||
29 | * array('search' => array('controller' => 'results'); |
||
30 | * |
||
31 | * @var array actions |
||
32 | */ |
||
33 | public $actions = array(); |
||
34 | |||
35 | /** |
||
36 | * Enables encoding on all presetVar fields |
||
37 | * |
||
38 | * @var boolean |
||
39 | */ |
||
40 | public $encode = false; |
||
41 | |||
42 | /** |
||
43 | * If the current request is an actual search (at least one search value present) |
||
44 | * |
||
45 | * @var boolean |
||
46 | */ |
||
47 | public $isSearch = false; |
||
48 | |||
49 | /** |
||
50 | * Parsed params of current request |
||
51 | * |
||
52 | * @var array |
||
53 | */ |
||
54 | protected $_parsedParams = array(); |
||
55 | |||
56 | /** |
||
57 | * Default options |
||
58 | * |
||
59 | * @var array |
||
60 | */ |
||
61 | protected $_defaults = array( |
||
62 | 'commonProcess' => array( |
||
63 | 'formName' => null, |
||
64 | 'keepPassed' => true, |
||
65 | 'action' => null, |
||
66 | 'modelMethod' => 'validateSearch', |
||
67 | 'allowedParams' => array(), |
||
68 | 'paramType' => 'named', |
||
69 | 'filterEmpty' => false |
||
70 | ), |
||
71 | 'presetForm' => array( |
||
72 | 'model' => null, |
||
73 | 'paramType' => 'named' |
||
74 | ) |
||
75 | ); |
||
76 | |||
77 | /** |
||
78 | * Constructor |
||
79 | * |
||
80 | * @param ComponentCollection $collection |
||
81 | * @param array $settings |
||
82 | */ |
||
83 | public function __construct(ComponentCollection $collection, $settings) { |
||
89 | |||
90 | /** |
||
91 | * Called before the Controller::beforeFilter(). |
||
92 | * |
||
93 | * @param Controller $controller Controller with components to initialize |
||
94 | * @return void |
||
95 | */ |
||
96 | public function initialize(Controller $controller) { |
||
137 | |||
138 | /** |
||
139 | * Populates controller->request->data with allowed values from the named/passed get params |
||
140 | * |
||
141 | * Fields in $controller::$presetVars that have a type of 'lookup' the foreignKey value will be inserted |
||
142 | * |
||
143 | * 1) 'lookup' |
||
144 | * Is used for autocomplete selectors |
||
145 | * For autocomplete we have hidden field with value and autocomplete text box |
||
146 | * Component fills text part on id from hidden field |
||
147 | * 2) 'value' |
||
148 | * The value as it is entered in form |
||
149 | * 3) 'checkbox' |
||
150 | * Allows to pass several values internaly encoded as string |
||
151 | * |
||
152 | * 1 use field, model, formField, and modelField |
||
153 | * 2, 3 need only field parameter |
||
154 | * |
||
155 | * @param array $options |
||
156 | * @return void |
||
157 | */ |
||
158 | public function presetForm($options) { |
||
221 | |||
222 | /** |
||
223 | * Return the parsed params of the current search request |
||
224 | * |
||
225 | * @return array Params |
||
226 | */ |
||
227 | public function parsedParams() { |
||
230 | |||
231 | /** |
||
232 | * Restores form params for checkboxes and other url encoded params |
||
233 | * |
||
234 | * @param array |
||
235 | * @return array |
||
236 | */ |
||
237 | public function serializeParams(array &$data) { |
||
261 | |||
262 | /** |
||
263 | * Connect named arguments |
||
264 | * |
||
265 | * @param array $data |
||
266 | * @param array $exclude |
||
267 | * @return void |
||
268 | */ |
||
269 | public function connectNamed($data = null, array $exclude = array()) { |
||
284 | |||
285 | /** |
||
286 | * Exclude |
||
287 | * |
||
288 | * Removes key/values from $array based on $exclude |
||
289 | * |
||
290 | * @param array Array of data to be filtered |
||
291 | * @param array Array of keys to exclude from other $array |
||
292 | * @return array |
||
293 | */ |
||
294 | public function exclude(array $array, array $exclude) { |
||
303 | |||
304 | /** |
||
305 | * Common search method |
||
306 | * |
||
307 | * Handles processes common to all PRG forms |
||
308 | * |
||
309 | * - Handles validation of post data |
||
310 | * - converting post data into named params |
||
311 | * - Issuing redirect(), and connecting named parameters before redirect |
||
312 | * - Setting named parameter form data to view |
||
313 | * |
||
314 | * @param string $modelName - Name of the model class being used for the prg form |
||
315 | * @param array $options Optional parameters: |
||
316 | * - string formName - name of the form involved in the prg |
||
317 | * - string action - The action to redirect to. Defaults to the current action |
||
318 | * - mixed modelMethod - If not false a string that is the model method that will be used to process the data |
||
319 | * - array allowedParams - An array of additional top level route params that should be included in the params processed |
||
320 | * - array excludedParams - An array of named/query params that should be excluded from the redirect url |
||
321 | * - string paramType - 'named' if you want to used named params or 'querystring' is you want to use query string |
||
322 | * @return void |
||
323 | */ |
||
324 | public function commonProcess($modelName = null, array $options = array()) { |
||
411 | |||
412 | /** |
||
413 | * Parse the configs from the Model (to keep things dry) |
||
414 | * |
||
415 | * @param array $arg |
||
416 | * @param mixed $key |
||
417 | * @return array |
||
418 | */ |
||
419 | protected function _parseFromModel(array $arg, $key = null) { |
||
442 | |||
443 | } |
||
444 |
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: