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 AbstractSource 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 AbstractSource, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | abstract class AbstractSource implements |
||
32 | SourceInterface, |
||
33 | ConfigurableInterface, |
||
34 | LoggerAwareInterface |
||
35 | { |
||
36 | use ConfigurableTrait; |
||
37 | use LoggerAwareTrait; |
||
38 | |||
39 | /** |
||
40 | * @var ModelInterface $model |
||
41 | */ |
||
42 | private $model = null; |
||
43 | |||
44 | /** |
||
45 | * @var array $properties |
||
46 | */ |
||
47 | private $properties = []; |
||
48 | |||
49 | /** |
||
50 | * Array of `Filter` objects |
||
51 | * @var array $filters |
||
52 | */ |
||
53 | protected $filters = []; |
||
54 | |||
55 | /** |
||
56 | * Array of `Order` object |
||
57 | * @var array $orders |
||
58 | */ |
||
59 | protected $orders = []; |
||
60 | |||
61 | /** |
||
62 | * The `Pagination` object |
||
63 | * @var Pagination|null $pagination |
||
64 | */ |
||
65 | protected $pagination = null; |
||
66 | |||
67 | /** |
||
68 | * @param array|\ArrayAccess $dependencies The class dependencies. |
||
69 | * @return void |
||
|
|||
70 | */ |
||
71 | public function __construct($dependencies) |
||
75 | |||
76 | /** |
||
77 | * Reset everything but the model. |
||
78 | * |
||
79 | * @return AbstractSource Chainable |
||
80 | */ |
||
81 | public function reset() |
||
89 | |||
90 | /** |
||
91 | * Initialize the source's properties with an array of data. |
||
92 | * |
||
93 | * @param array $data The source data. |
||
94 | * @return AbstractSource Chainable |
||
95 | */ |
||
96 | public function setData(array $data) |
||
109 | |||
110 | /** |
||
111 | * Set the source's Model. |
||
112 | * |
||
113 | * @param ModelInterface $model The source's model. |
||
114 | * @return AbstractSource Chainable |
||
115 | */ |
||
116 | public function setModel(ModelInterface $model) |
||
121 | |||
122 | /** |
||
123 | * Return the source's Model. |
||
124 | * |
||
125 | * @throws Exception If not model was previously set. |
||
126 | * @return ModelInterface |
||
127 | */ |
||
128 | public function model() |
||
137 | |||
138 | /** |
||
139 | * @return boolean |
||
140 | */ |
||
141 | public function hasModel() |
||
145 | |||
146 | /** |
||
147 | * Set the properties of the source to fetch. |
||
148 | * |
||
149 | * This method accepts an array of property identifiers (property ident, as string) |
||
150 | * that will, if supported, be fetched from the source. |
||
151 | * |
||
152 | * If no properties are set, it is assumed that all the Model's properties are to be fetched. |
||
153 | * |
||
154 | * @param array $properties The properties. |
||
155 | * @return ColelectionLoader Chainable |
||
156 | */ |
||
157 | public function setProperties(array $properties) |
||
165 | |||
166 | /** |
||
167 | * @return array |
||
168 | */ |
||
169 | public function properties() |
||
173 | |||
174 | /** |
||
175 | * @param string $property Property ident. |
||
176 | * @throws InvalidArgumentException If property is not a string or empty. |
||
177 | * @return SourceInterface Chainable |
||
178 | */ |
||
179 | View Code Duplication | public function addProperty($property) |
|
194 | |||
195 | /** |
||
196 | * @param array $filters The filters to set. |
||
197 | * @return SourceInterface Chainable |
||
198 | */ |
||
199 | public function setFilters(array $filters) |
||
207 | |||
208 | /** |
||
209 | * @param array $filters The filters to add. |
||
210 | * @return SourceInterface Chainable |
||
211 | */ |
||
212 | public function addFilters(array $filters) |
||
219 | |||
220 | /** |
||
221 | * @return array |
||
222 | */ |
||
223 | public function filters() |
||
227 | |||
228 | /** |
||
229 | * Add a collection filter to the loader. |
||
230 | * |
||
231 | * There are 3 different ways of adding a filter: |
||
232 | * - as a `Filter` object, in which case it will be added directly. |
||
233 | * - `addFilter($obj);` |
||
234 | * - as an array of options, which will be used to build the `Filter` object |
||
235 | * - `addFilter(['property' => 'foo', 'val' => 42, 'operator' => '<=']);` |
||
236 | * - as 3 parameters: `property`, `val` and `options` |
||
237 | * - `addFilter('foo', 42, ['operator' => '<=']);` |
||
238 | * |
||
239 | * @param string|array|Filter $param The filter property, or a Filter object / array. |
||
240 | * @param mixed $val Optional: Only used if the first argument is a string. |
||
241 | * @param array $options Optional: Only used if the first argument is a string. |
||
242 | * @throws InvalidArgumentException If property is not a string or empty. |
||
243 | * @return SourceInterface (Chainable) |
||
244 | */ |
||
245 | public function addFilter($param, $val = null, array $options = null) |
||
285 | |||
286 | /** |
||
287 | * @return FilterInterface |
||
288 | */ |
||
289 | protected function createFilter() |
||
294 | |||
295 | /** |
||
296 | * @param array $orders The orders to set. |
||
297 | * @return AbstractSource Chainable |
||
298 | */ |
||
299 | public function setOrders(array $orders) |
||
307 | |||
308 | /** |
||
309 | * @param array $orders The orders to add. |
||
310 | * @return AbstractSource Chainable |
||
311 | */ |
||
312 | public function addOrders(array $orders) |
||
319 | |||
320 | /** |
||
321 | * @return array |
||
322 | */ |
||
323 | public function orders() |
||
327 | |||
328 | /** |
||
329 | * @param string|array|Order $param The order property, or an Order object / array. |
||
330 | * @param string $mode Optional. |
||
331 | * @param array $orderOptions Optional. |
||
332 | * @throws InvalidArgumentException If the param argument is invalid. |
||
333 | * @return SourceInterface Chainable |
||
334 | */ |
||
335 | public function addOrder($param, $mode = 'asc', array $orderOptions = null) |
||
371 | |||
372 | /** |
||
373 | * @return OrderInterface |
||
374 | */ |
||
375 | protected function createOrder() |
||
380 | |||
381 | /** |
||
382 | * @param mixed $param The pagination object or array. |
||
383 | * @throws InvalidArgumentException If the argument is not an object or array. |
||
384 | * @return SourceInterface Chainable |
||
385 | */ |
||
386 | public function setPagination($param) |
||
401 | |||
402 | /** |
||
403 | * Get the pagination object. |
||
404 | * |
||
405 | * If the pagination wasn't set previously, a new (default / blank) Pagination object will be created. |
||
406 | * (Always return a `PaginationInterface` object) |
||
407 | * |
||
408 | * @return PaginationInterface |
||
409 | */ |
||
410 | public function pagination() |
||
417 | |||
418 | /** |
||
419 | * @return PaginationInterface |
||
420 | */ |
||
421 | protected function createPagination() |
||
426 | |||
427 | /** |
||
428 | * @param integer $page The page number. |
||
429 | * @throws InvalidArgumentException If the page argument is not numeric. |
||
430 | * @return AbstractSource Chainable |
||
431 | */ |
||
432 | public function setPage($page) |
||
442 | |||
443 | /** |
||
444 | * @return integer |
||
445 | */ |
||
446 | public function page() |
||
450 | |||
451 | /** |
||
452 | * @param integer $num The number of items to retrieve per page. |
||
453 | * @throws InvalidArgumentException If the num per page argument is not numeric. |
||
454 | * @return AbstractSource Chainable |
||
455 | */ |
||
456 | public function setNumPerPage($num) |
||
466 | |||
467 | /** |
||
468 | * @return integer |
||
469 | */ |
||
470 | public function numPerPage() |
||
474 | |||
475 | /** |
||
476 | * ConfigurableTrait > createConfig() |
||
477 | * |
||
478 | * @param array $data Optional. |
||
479 | * @return SourceConfig |
||
480 | */ |
||
481 | public function createConfig(array $data = null) |
||
489 | |||
490 | /** |
||
491 | * @param mixed $ident The ID of the item to load. |
||
492 | * @param StorableInterface $item Optional item to load into. |
||
493 | * @return StorableInterface |
||
494 | */ |
||
495 | abstract public function loadItem($ident, StorableInterface $item = null); |
||
496 | |||
497 | /** |
||
498 | * @param StorableInterface|null $item The model to load items from. |
||
499 | * @return array |
||
500 | */ |
||
501 | abstract public function loadItems(StorableInterface $item = null); |
||
502 | |||
503 | /** |
||
504 | * Save an item (create a new row) in storage. |
||
505 | * |
||
506 | * @param StorableInterface $item The object to save. |
||
507 | * @return mixed The created item ID, or false in case of an error. |
||
508 | */ |
||
509 | abstract public function saveItem(StorableInterface $item); |
||
510 | |||
511 | /** |
||
512 | * Update an item in storage. |
||
513 | * |
||
514 | * @param StorableInterface $item The object to update. |
||
515 | * @param array $properties The list of properties to update, if not all. |
||
516 | * @return boolean Success / Failure |
||
517 | */ |
||
518 | abstract public function updateItem(StorableInterface $item, array $properties = null); |
||
519 | |||
520 | /** |
||
521 | * Delete an item from storage |
||
522 | * |
||
523 | * @param StorableInterface $item Optional item to delete. If none, the current model object will be used.. |
||
524 | * @return boolean Success / Failure |
||
525 | */ |
||
526 | abstract public function deleteItem(StorableInterface $item = null); |
||
527 | |||
528 | /** |
||
529 | * Allow an object to define how the key getter are called. |
||
530 | * |
||
531 | * @param string $key The key to get the getter from. |
||
532 | * @param string $case Optional. The type of case to return. camel, pascal or snake. |
||
533 | * @return string The getter method name, for a given key. |
||
534 | */ |
||
535 | View Code Duplication | protected function getter($key, $case = 'camel') |
|
547 | |||
548 | /** |
||
549 | * Allow an object to define how the key setter are called. |
||
550 | * |
||
551 | * @param string $key The key to get the setter from. |
||
552 | * @param string $case Optional. The type of case to return. camel, pascal or snake. |
||
553 | * @return string The setter method name, for a given key. |
||
554 | */ |
||
555 | View Code Duplication | protected function setter($key, $case = 'camel') |
|
567 | |||
568 | /** |
||
569 | * Transform a snake_case string to camelCase. |
||
570 | * |
||
571 | * @param string $str The snake_case string to camelize. |
||
572 | * @return string The camelCase string. |
||
573 | */ |
||
574 | private function camelize($str) |
||
578 | |||
579 | /** |
||
580 | * Transform a snake_case string to PamelCase. |
||
581 | * |
||
582 | * @param string $str The snake_case string to pascalize. |
||
583 | * @return string The PamelCase string. |
||
584 | */ |
||
585 | private function pascalize($str) |
||
589 | } |
||
590 |
Adding a
@return
annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.Please refer to the PHP core documentation on constructors.