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 SearchableBehavior 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 SearchableBehavior, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class SearchableBehavior extends ModelBehavior { |
||
18 | |||
19 | /** |
||
20 | * Default settings |
||
21 | * - wildcardAny: the character used instead of % (% is a normal character then) |
||
22 | * - wildcardOne: the character used instead of _ (_ is a normal character then) |
||
23 | * - like: auto add % wildcard to beginning, end or both (both false => user can enter wildcards himself) |
||
24 | * - connectorAnd: the character between search terms to specify an "and" relationship (binds stronger than or, similar to * and + in math) |
||
25 | * - connectorOr: the character between search terms to specify an "or" relationship |
||
26 | * |
||
27 | * @var array |
||
28 | */ |
||
29 | protected $_defaults = array( |
||
30 | 'wildcardAny' => '*', //on windows/unix/mac/google/... thats the default one |
||
31 | 'wildcardOne' => '?', //on windows/unix/mac thats the default one |
||
32 | 'like' => array('before' => true, 'after' => true), |
||
33 | 'connectorAnd' => null, |
||
34 | 'connectorOr' => null, |
||
35 | ); |
||
36 | |||
37 | /** |
||
38 | * Configuration of model |
||
39 | * |
||
40 | * @param Model $Model |
||
41 | * @param array $config |
||
42 | * @return void |
||
43 | */ |
||
44 | public function setup(Model $Model, $config = array()) { |
||
62 | |||
63 | /** |
||
64 | * parseCriteria |
||
65 | * parses the GET data and returns the conditions for the find('all')/paginate |
||
66 | * we are just going to test if the params are legit |
||
67 | * |
||
68 | * @param Model $Model |
||
69 | * @param array $data Criteria of key->value pairs from post/named parameters |
||
70 | * @return array Array of conditions that express the conditions needed for the search |
||
71 | */ |
||
72 | public function parseCriteria(Model $Model, $data) { |
||
94 | |||
95 | /** |
||
96 | * Validate search |
||
97 | * |
||
98 | * @param Model $Model |
||
99 | * @param null $data |
||
100 | * @return boolean always true |
||
101 | */ |
||
102 | public function validateSearch(Model $Model, $data = null) { |
||
114 | |||
115 | /** |
||
116 | * filter retrieving variables only that present in Model::filterArgs |
||
117 | * |
||
118 | * @param Model $Model |
||
119 | * @param array $vars |
||
120 | * @return array, filtered args |
||
121 | */ |
||
122 | public function passedArgs(Model $Model, $vars) { |
||
131 | |||
132 | /** |
||
133 | * Generates a query string using the same API Model::find() uses, calling the beforeFind process for the model |
||
134 | * |
||
135 | * @param Model $Model |
||
136 | * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) |
||
137 | * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) |
||
138 | * @return array Array of records |
||
139 | * @link http://book.cakephp.org/view/1018/find |
||
140 | */ |
||
141 | public function getQuery(Model $Model, $type = 'first', $query = array()) { |
||
148 | |||
149 | /** |
||
150 | * Clear all associations |
||
151 | * |
||
152 | * @param Model $Model |
||
153 | * @param boolean $reset |
||
154 | * @return void |
||
155 | */ |
||
156 | public function unbindAllModels(Model $Model, $reset = false) { |
||
164 | |||
165 | /** |
||
166 | * For custom queries inside the model |
||
167 | * example "makePhoneCondition": $cond = array('OR' => array_merge($this->condLike('cell_number', $filter), $this->condLike('landline_number', $filter, array('before' => false)))); |
||
168 | * |
||
169 | * @param Model $Model |
||
170 | * @param $name |
||
171 | * @param $data |
||
172 | * @param array $field |
||
173 | * @return array of conditions |
||
174 | */ |
||
175 | public function condLike(Model $Model, $name, $data, $field = array()) { |
||
186 | |||
187 | /** |
||
188 | * Replace substitutions with original wildcards |
||
189 | * but first, escape the original wildcards in the text to use them as normal search text |
||
190 | * |
||
191 | * @param Model $Model |
||
192 | * @param $data |
||
193 | * @param array $options |
||
194 | * @return string queryLikeString |
||
195 | */ |
||
196 | public function formatLike(Model $Model, $data, $options = array()) { |
||
219 | |||
220 | /** |
||
221 | * Return the current chars for querying LIKE statements on this model |
||
222 | * |
||
223 | * @param Model $Model Reference to the model |
||
224 | * @param array $options |
||
225 | * @return array, [one=>..., any=>...] |
||
226 | */ |
||
227 | public function getWildcards(Model $Model, $options = array()) { |
||
231 | |||
232 | /** |
||
233 | * Add Conditions based on fuzzy comparison |
||
234 | * |
||
235 | * @param Model $Model Reference to the model |
||
236 | * @param array $conditions existing Conditions collected for the model |
||
237 | * @param array $data Array of data used in search query |
||
238 | * @param array $field Field definition information |
||
239 | * @return array Conditions |
||
240 | */ |
||
241 | protected function _addCondLike(Model $Model, &$conditions, $data, $field) { |
||
307 | |||
308 | /** |
||
309 | * Form AND/OR query array using String::tokenize to separate |
||
310 | * search terms by or/and connectors. |
||
311 | * |
||
312 | * @param mixed $value |
||
313 | * @param array $field |
||
314 | * @param string $fieldName |
||
315 | * @return array Conditions |
||
316 | */ |
||
317 | protected function _connectedLike($value, $field, $fieldName) { |
||
332 | |||
333 | /** |
||
334 | * Add Conditions based on exact comparison |
||
335 | * |
||
336 | * @param Model $Model Reference to the model |
||
337 | * @param array $conditions existing Conditions collected for the model |
||
338 | * @param array $data Array of data used in search query |
||
339 | * @param array $field Field definition information |
||
340 | * @return array of conditions |
||
341 | */ |
||
342 | protected function _addCondValue(Model $Model, &$conditions, $data, $field) { |
||
382 | |||
383 | /** |
||
384 | * Add Conditions based expressions to search conditions. |
||
385 | * |
||
386 | * @param Model $Model Instance of AppModel |
||
387 | * @param array $conditions Existing conditions. |
||
388 | * @param array $data Data for a field. |
||
389 | * @param array $field Info for field. |
||
390 | * @return array of conditions modified by this method |
||
391 | */ |
||
392 | protected function _addCondExpression(Model $Model, &$conditions, $data, $field) { |
||
405 | |||
406 | /** |
||
407 | * Add Conditions based query to search conditions. |
||
408 | * |
||
409 | * @param Model $Model Instance of AppModel |
||
410 | * @param array $conditions Existing conditions. |
||
411 | * @param array $data Data for a field. |
||
412 | * @param array $field Info for field. |
||
413 | * @return array of conditions modified by this method |
||
414 | */ |
||
415 | protected function _addCondQuery(Model $Model, &$conditions, $data, $field) { |
||
425 | |||
426 | /** |
||
427 | * Add Conditions based subquery to search conditions. |
||
428 | * |
||
429 | * @param Model $Model Instance of AppModel |
||
430 | * @param array $conditions Existing conditions. |
||
431 | * @param array $data Data for a field. |
||
432 | * @param array $field Info for field. |
||
433 | * @return array of conditions modified by this method |
||
434 | */ |
||
435 | protected function _addCondSubquery(Model $Model, &$conditions, $data, $field) { |
||
446 | |||
447 | /** |
||
448 | * Helper method for getQuery. |
||
449 | * extension of dbo source method. Create association query. |
||
450 | * |
||
451 | * @param Model $Model |
||
452 | * @param array $queryData |
||
453 | * @return string |
||
454 | */ |
||
455 | protected function _queryGet(Model $Model, $queryData = array()) { |
||
507 | |||
508 | /** |
||
509 | * Private helper method to remove query metadata in given data array. |
||
510 | * |
||
511 | * @param array $data |
||
512 | * @return array |
||
513 | */ |
||
514 | protected function _scrubQueryData($data) { |
||
521 | |||
522 | /** |
||
523 | * Check if model have some method in attached behaviors |
||
524 | * |
||
525 | * @param Model $Model |
||
526 | * @param string $method |
||
527 | * @return boolean, true if method exists in attached and enabled behaviors |
||
528 | */ |
||
529 | protected function _checkBehaviorMethods(Model $Model, $method) { |
||
544 | |||
545 | } |
||
546 |
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.