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.