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 Query 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 Query, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 17 | class Query |
||
| 18 | { |
||
| 19 | const DEFAULT_LIMIT = 100; |
||
| 20 | const MAX_LIMIT = 1000; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * @var string |
||
| 24 | */ |
||
| 25 | private $model; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @var array |
||
| 29 | */ |
||
| 30 | private $joins; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @var array |
||
| 34 | */ |
||
| 35 | private $eagerLoaded; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var array |
||
| 39 | */ |
||
| 40 | private $where; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @var int |
||
| 44 | */ |
||
| 45 | private $limit; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @var int |
||
| 49 | */ |
||
| 50 | private $start; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @var array |
||
| 54 | */ |
||
| 55 | private $sort; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @param string $model model class |
||
| 59 | */ |
||
| 60 | public function __construct($model = '') |
||
| 70 | |||
| 71 | /** |
||
| 72 | * Gets the model class associated with this query. |
||
| 73 | * |
||
| 74 | * @return string |
||
| 75 | */ |
||
| 76 | public function getModel() |
||
| 80 | |||
| 81 | /** |
||
| 82 | * Sets the limit for this query. |
||
| 83 | * |
||
| 84 | * @param int $limit |
||
| 85 | * |
||
| 86 | * @return $this |
||
| 87 | */ |
||
| 88 | public function limit($limit) |
||
| 94 | |||
| 95 | /** |
||
| 96 | * Gets the limit for this query. |
||
| 97 | * |
||
| 98 | * @return int |
||
| 99 | */ |
||
| 100 | public function getLimit() |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Sets the start offset. |
||
| 107 | * |
||
| 108 | * @param int $start |
||
| 109 | * |
||
| 110 | * @return $this |
||
| 111 | */ |
||
| 112 | public function start($start) |
||
| 118 | |||
| 119 | /** |
||
| 120 | * Gets the start offset. |
||
| 121 | * |
||
| 122 | * @return int |
||
| 123 | */ |
||
| 124 | public function getStart() |
||
| 128 | |||
| 129 | /** |
||
| 130 | * Sets the sort pattern for the query. |
||
| 131 | * |
||
| 132 | * @param array|string $sort |
||
| 133 | * |
||
| 134 | * @return $this |
||
| 135 | */ |
||
| 136 | public function sort($sort) |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Gets the sort parameters. |
||
| 164 | * |
||
| 165 | * @return array |
||
| 166 | */ |
||
| 167 | public function getSort() |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Sets the where parameters. |
||
| 174 | * Accepts the following forms: |
||
| 175 | * i) where(['name' => 'Bob']) |
||
| 176 | * ii) where('name', 'Bob') |
||
| 177 | * iii) where('balance', 100, '>') |
||
| 178 | * iv) where('balance > 100'). |
||
| 179 | * |
||
| 180 | * @param array|string $where |
||
| 181 | * @param mixed $value optional value |
||
| 182 | * @param string|null $condition optional condition |
||
| 183 | * |
||
| 184 | * @return $this |
||
| 185 | */ |
||
| 186 | public function where($where, $value = null, $condition = null) |
||
| 207 | |||
| 208 | /** |
||
| 209 | * Gets the where parameters. |
||
| 210 | * |
||
| 211 | * @return array |
||
| 212 | */ |
||
| 213 | public function getWhere() |
||
| 217 | |||
| 218 | /** |
||
| 219 | * Adds a join to the query. Matches a property on this model |
||
| 220 | * to the ID of the model we are joining. |
||
| 221 | * |
||
| 222 | * @param string $model model being joined |
||
| 223 | * @param string $column name of local property |
||
| 224 | * @param string $foreignKey |
||
| 225 | * |
||
| 226 | * @return $this |
||
| 227 | */ |
||
| 228 | public function join($model, $column, $foreignKey) |
||
| 234 | |||
| 235 | /** |
||
| 236 | * Gets the joins. |
||
| 237 | * |
||
| 238 | * @return array |
||
| 239 | */ |
||
| 240 | public function getJoins() |
||
| 244 | |||
| 245 | /** |
||
| 246 | * Marks a relationship property on the model that should be eager loaded. |
||
| 247 | * |
||
| 248 | * @param string $k local property containing the relationship |
||
| 249 | * |
||
| 250 | * @return $this |
||
| 251 | */ |
||
| 252 | public function with($k) |
||
| 260 | |||
| 261 | /** |
||
| 262 | * Gets the relationship properties that are going to be eager-loaded. |
||
| 263 | * |
||
| 264 | * @return array |
||
| 265 | */ |
||
| 266 | public function getWith() |
||
| 270 | |||
| 271 | /** |
||
| 272 | * Executes the query against the model's driver. |
||
| 273 | * |
||
| 274 | * @return array results |
||
| 275 | */ |
||
| 276 | public function execute() |
||
| 349 | |||
| 350 | /** |
||
| 351 | * Creates an iterator for a search. |
||
| 352 | * |
||
| 353 | * @return Iterator |
||
| 354 | */ |
||
| 355 | public function all() |
||
| 359 | |||
| 360 | /** |
||
| 361 | * Executes the query against the model's driver and returns the first result. |
||
| 362 | * |
||
| 363 | * @param int $limit |
||
| 364 | * |
||
| 365 | * @return array|Model|null when $limit = 1, returns a single model or null, otherwise returns an array |
||
| 366 | */ |
||
| 367 | public function first($limit = 1) |
||
| 377 | |||
| 378 | /** |
||
| 379 | * Gets the number of models matching the query. |
||
| 380 | * |
||
| 381 | * @return int |
||
| 382 | */ |
||
| 383 | public function count() |
||
| 390 | |||
| 391 | /** |
||
| 392 | * @deprecated |
||
| 393 | * |
||
| 394 | * Gets the total number of records matching an optional criteria |
||
| 395 | * |
||
| 396 | * @param array $where criteria |
||
| 397 | * |
||
| 398 | * @return int |
||
| 399 | */ |
||
| 400 | public function totalRecords(array $where = []) |
||
| 404 | |||
| 405 | /** |
||
| 406 | * Gets the sum of a property matching the query. |
||
| 407 | * |
||
| 408 | * @param string $property |
||
| 409 | * |
||
| 410 | * @return int |
||
| 411 | */ |
||
| 412 | public function sum($property) |
||
| 419 | |||
| 420 | /** |
||
| 421 | * Gets the average of a property matching the query. |
||
| 422 | * |
||
| 423 | * @param string $property |
||
| 424 | * |
||
| 425 | * @return int |
||
| 426 | */ |
||
| 427 | public function average($property) |
||
| 434 | |||
| 435 | /** |
||
| 436 | * Gets the max of a property matching the query. |
||
| 437 | * |
||
| 438 | * @param string $property |
||
| 439 | * |
||
| 440 | * @return int |
||
| 441 | */ |
||
| 442 | public function max($property) |
||
| 449 | |||
| 450 | /** |
||
| 451 | * Gets the min of a property matching the query. |
||
| 452 | * |
||
| 453 | * @param string $property |
||
| 454 | * |
||
| 455 | * @return int |
||
| 456 | */ |
||
| 457 | public function min($property) |
||
| 464 | |||
| 465 | /** |
||
| 466 | * Updates all of the models matched by this query. |
||
| 467 | * |
||
| 468 | * @todo should be optimized to be done in a single call to the data layer |
||
| 469 | * |
||
| 470 | * @param array $params key-value update parameters |
||
| 471 | * |
||
| 472 | * @return int # of models updated |
||
| 473 | */ |
||
| 474 | public function set(array $params) |
||
| 484 | |||
| 485 | /** |
||
| 486 | * Deletes all of the models matched by this query. |
||
| 487 | * |
||
| 488 | * @todo should be optimized to be done in a single call to the data layer |
||
| 489 | * |
||
| 490 | * @return int # of models deleted |
||
| 491 | */ |
||
| 492 | public function delete() |
||
| 502 | |||
| 503 | /** |
||
| 504 | * Hydrates the eager-loaded relationships for a given set of IDs. |
||
| 505 | * |
||
| 506 | * @param string $modelClass |
||
| 507 | * @param array $ids |
||
| 508 | * @param string $foreignKey |
||
| 509 | * @param bool $multiple when true will condense |
||
| 510 | * |
||
| 511 | * @return array |
||
| 512 | */ |
||
| 513 | private function fetchRelationships($modelClass, array $ids, $foreignKey, $multiple) |
||
| 538 | } |
||
| 539 |
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.