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 Ajde_Collection 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 Ajde_Collection, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
3 | class Ajde_Collection extends Ajde_Object_Standard implements Iterator, Countable |
||
4 | { |
||
5 | |||
6 | /** |
||
7 | * @var string |
||
8 | */ |
||
9 | protected $_modelName; |
||
10 | |||
11 | /** |
||
12 | * @var PDO |
||
13 | */ |
||
14 | protected $_connection; |
||
15 | |||
16 | /** |
||
17 | * @var PDOStatement |
||
18 | */ |
||
19 | protected $_statement; |
||
20 | |||
21 | /** |
||
22 | * @var Ajde_Query |
||
23 | */ |
||
24 | protected $_query; |
||
25 | |||
26 | protected $_link = []; |
||
27 | |||
28 | /** |
||
29 | * @var Ajde_Db_Table |
||
30 | */ |
||
31 | protected $_table; |
||
32 | |||
33 | protected $_filters = []; |
||
34 | public $_filterValues = []; |
||
35 | |||
36 | /** |
||
37 | * @var Ajde_Collection_View |
||
38 | */ |
||
39 | protected $_view; |
||
40 | |||
41 | // For Iterator |
||
42 | protected $_items = null; |
||
43 | protected $_position = 0; |
||
44 | |||
45 | private $_sqlInitialized = false; |
||
46 | private $_queryCount; |
||
47 | |||
48 | public static function extendController(Ajde_Controller $controller, $method, $arguments) |
||
59 | |||
60 | public static function getCollection($name) |
||
66 | |||
67 | public function __construct() |
||
68 | { |
||
69 | $this->_modelName = str_replace('Collection', '', get_class($this)) . 'Model'; |
||
70 | $this->_connection = Ajde_Db::getInstance()->getConnection(); |
||
71 | |||
72 | $tableNameCC = str_replace('Collection', '', get_class($this)); |
||
73 | $tableName = $this->fromCamelCase($tableNameCC); |
||
74 | |||
75 | $this->_table = Ajde_Db::getInstance()->getTable($tableName); |
||
76 | $this->_query = new Ajde_Query(); |
||
77 | } |
||
78 | |||
79 | public function reset() |
||
80 | { |
||
81 | parent::reset(); |
||
82 | $this->_query = new Ajde_Query(); |
||
83 | $this->_filters = []; |
||
84 | $this->_filterValues = []; |
||
85 | $this->_items = null; |
||
86 | $this->_position = 0; |
||
87 | $this->_queryCount = null; |
||
88 | $this->_sqlInitialized = false; |
||
89 | } |
||
90 | |||
91 | public function __sleep() |
||
95 | |||
96 | public function __wakeup() |
||
99 | |||
100 | public function rewind() |
||
107 | |||
108 | public function current() |
||
112 | |||
113 | public function key() |
||
117 | |||
118 | public function next() |
||
122 | |||
123 | public function count($query = false) |
||
149 | |||
150 | /** |
||
151 | * |
||
152 | * @param string $field |
||
153 | * @param mixed $value |
||
154 | * @return Ajde_Model | boolean |
||
155 | */ |
||
156 | public function find($field, $value) |
||
166 | |||
167 | function valid() |
||
171 | |||
172 | /** |
||
173 | * @return Ajde_Db_PDO |
||
174 | */ |
||
175 | public function getConnection() |
||
179 | |||
180 | /** |
||
181 | * @return Ajde_Db_Table |
||
182 | */ |
||
183 | public function getTable() |
||
187 | |||
188 | /** |
||
189 | * @return PDOStatement |
||
190 | */ |
||
191 | public function getStatement() |
||
195 | |||
196 | /** |
||
197 | * @return Ajde_Query |
||
198 | */ |
||
199 | public function getQuery() |
||
203 | |||
204 | public function populate($array) |
||
209 | |||
210 | public function getLink($modelName, $value) |
||
219 | |||
220 | // Chainable collection methods |
||
221 | |||
222 | public function addFilter(Ajde_Filter $filter) |
||
228 | |||
229 | public function orderBy($field, $direction = Ajde_Query::ORDER_ASC) |
||
235 | |||
236 | public function limit($count, $start = 0) |
||
242 | |||
243 | public function filter($field, $value, $comparison = Ajde_Filter::FILTER_EQUALS, $operator = Ajde_Query::OP_AND) |
||
249 | |||
250 | // View functions |
||
251 | |||
252 | public function setView(Ajde_Collection_View $view) |
||
256 | |||
257 | /** |
||
258 | * @return Ajde_Collection_View |
||
259 | */ |
||
260 | public function getView() |
||
264 | |||
265 | /** |
||
266 | * @return boolean |
||
267 | */ |
||
268 | public function hasView() |
||
272 | |||
273 | public function applyView(Ajde_Collection_View $view = null) |
||
274 | { |
||
275 | if (!$this->hasView() && !isset($view)) { |
||
276 | // TODO: |
||
277 | throw new Ajde_Exception('No view set'); |
||
278 | } |
||
279 | |||
280 | if (isset($view)) { |
||
281 | $this->setView($view); |
||
282 | } else { |
||
283 | $view = $this->getView(); |
||
284 | } |
||
285 | |||
286 | // LIMIT |
||
287 | $this->limit($view->getPageSize(), $view->getRowStart()); |
||
288 | |||
289 | // ORDER BY |
||
290 | if (!$view->isEmpty('orderBy')) { |
||
291 | $oldOrderBy = $this->getQuery()->orderBy; |
||
292 | $this->getQuery()->orderBy = []; |
||
293 | if (in_array($view->getOrderBy(), $this->getTable()->getFieldNames())) { |
||
294 | $this->orderBy((string)$this->getTable() . '.' . $view->getOrderBy(), $view->getOrderDir()); |
||
295 | } else { |
||
296 | // custom column, make sure to add it to the query first! |
||
297 | $this->orderBy($view->getOrderBy(), $view->getOrderDir()); |
||
298 | } |
||
299 | foreach ($oldOrderBy as $orderBy) { |
||
300 | $this->orderBy($orderBy['field'], $orderBy['direction']); |
||
301 | } |
||
302 | } |
||
303 | |||
304 | // FILTER |
||
305 | if (!$view->isEmpty('filter')) { |
||
306 | foreach ($view->getFilter() as $fieldName => $filterValue) { |
||
307 | if ($filterValue != '') { |
||
308 | $fieldType = $this->getTable()->getFieldProperties($fieldName, 'type'); |
||
309 | if ($fieldType == Ajde_Db::FIELD_TYPE_DATE) { |
||
310 | // date fields |
||
311 | $start = $filterValue['start'] ? date('Y-m-d H:i:s', |
||
312 | strtotime($filterValue['start'] . ' 00:00:00')) : false; |
||
313 | $end = $filterValue['end'] ? date('Y-m-d H:i:s', |
||
314 | strtotime($filterValue['end'] . ' 23:59:59')) : false; |
||
315 | View Code Duplication | if ($start) { |
|
316 | $this->addFilter(new Ajde_Filter_Where((string)$this->getTable() . '.' . $fieldName, |
||
317 | Ajde_Filter::FILTER_GREATEROREQUAL, $start)); |
||
318 | } |
||
319 | View Code Duplication | if ($end) { |
|
320 | $this->addFilter(new Ajde_Filter_Where((string)$this->getTable() . '.' . $fieldName, |
||
321 | Ajde_Filter::FILTER_LESSOREQUAL, $end)); |
||
322 | } |
||
323 | } else { |
||
324 | if ($fieldType == Ajde_Db::FIELD_TYPE_TEXT) { |
||
325 | // text fields (fuzzy) |
||
326 | $this->addFilter(new Ajde_Filter_Where((string)$this->getTable() . '.' . $fieldName, |
||
327 | Ajde_Filter::FILTER_LIKE, '%' . $filterValue . '%')); |
||
328 | } else { |
||
329 | // non-date fields (exact match) |
||
330 | $this->addFilter(new Ajde_Filter_Where((string)$this->getTable() . '.' . $fieldName, |
||
331 | Ajde_Filter::FILTER_EQUALS, $filterValue)); |
||
332 | } |
||
333 | } |
||
334 | } |
||
335 | } |
||
336 | } |
||
337 | |||
338 | // SEARCH |
||
339 | if (!$view->isEmpty('search')) { |
||
340 | $this->addTextFilter($view->getSearch()); |
||
341 | } |
||
342 | } |
||
343 | |||
344 | public function addTextFilter($text, $operator = Ajde_Query::OP_AND, $condition = Ajde_Filter::CONDITION_WHERE) |
||
353 | |||
354 | public function getTextFilterGroup($text, $operator = Ajde_Query::OP_AND, $condition = Ajde_Filter::CONDITION_WHERE) |
||
379 | |||
380 | public function getSql() |
||
408 | |||
409 | public function getCountSql() |
||
410 | { |
||
411 | // Make sure to load the filters |
||
412 | $this->getSql(); |
||
413 | $query = clone $this->getQuery(); |
||
414 | /* @var $query Ajde_Query */ |
||
415 | $query->select = []; |
||
416 | $query->orderBy = []; |
||
417 | $query->limit = ['start' => null, 'count' => null]; |
||
418 | $query->addSelect('COUNT(*) AS count'); |
||
419 | |||
420 | return $query->getSql(); |
||
421 | } |
||
422 | |||
423 | public function getEmulatedSql() |
||
427 | |||
428 | public function getFilter($queryPart) |
||
446 | |||
447 | public function getFilterValues() |
||
451 | |||
452 | // Load the collection |
||
453 | public function load() |
||
470 | |||
471 | public function loadParents() |
||
479 | |||
480 | public function length() |
||
488 | |||
489 | public function hash() |
||
499 | |||
500 | public function toArray() |
||
509 | |||
510 | public function items() |
||
518 | |||
519 | public function add($item) |
||
523 | |||
524 | public function combine(Ajde_Collection $collection) |
||
525 | { |
||
526 | foreach ($collection as $item) { |
||
527 | $this->add($item); |
||
528 | } |
||
529 | } |
||
530 | |||
531 | public function deleteAll() |
||
537 | } |
||
538 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.