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 Relation 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 Relation, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | abstract class Relation |
||
22 | { |
||
23 | |||
24 | /** |
||
25 | * @var |
||
26 | */ |
||
27 | protected $name; |
||
28 | |||
29 | /** |
||
30 | * @var string |
||
31 | */ |
||
32 | protected $type = 'relation'; |
||
33 | |||
34 | /** |
||
35 | * @var Record |
||
36 | */ |
||
37 | protected $item; |
||
38 | |||
39 | /** |
||
40 | * @var RecordManager |
||
41 | */ |
||
42 | protected $manager = null; |
||
43 | |||
44 | |||
45 | /** |
||
46 | * @var RecordManager |
||
47 | */ |
||
48 | protected $with = null; |
||
49 | |||
50 | /** |
||
51 | * @var null|string |
||
52 | */ |
||
53 | protected $table = null; |
||
54 | |||
55 | /** |
||
56 | * @var null|string |
||
57 | */ |
||
58 | protected $fk = null; |
||
59 | |||
60 | /** |
||
61 | * @var Query |
||
62 | */ |
||
63 | protected $query; |
||
64 | |||
65 | /** |
||
66 | * @var bool |
||
67 | */ |
||
68 | protected $populated = false; |
||
69 | |||
70 | /** |
||
71 | * @var array |
||
72 | */ |
||
73 | protected $params = []; |
||
74 | |||
75 | /** |
||
76 | * @var null|Collection|Record |
||
77 | */ |
||
78 | protected $results = null; |
||
79 | |||
80 | /** |
||
81 | * @return Query |
||
82 | */ |
||
83 | public function getQuery() |
||
91 | |||
92 | /** |
||
93 | * @param $query |
||
94 | * @return static |
||
95 | */ |
||
96 | public function setQuery($query) |
||
102 | |||
103 | public function initQuery() |
||
110 | |||
111 | /** |
||
112 | * @return Query |
||
113 | */ |
||
114 | public function newQuery() |
||
118 | |||
119 | /** |
||
120 | * @return RecordManager |
||
121 | */ |
||
122 | 3 | public function getWith() |
|
130 | |||
131 | /** |
||
132 | * @param RecordManager|HasRelationsRecordsTrait $object |
||
133 | * @return $this |
||
134 | */ |
||
135 | 3 | public function setWith($object) |
|
141 | |||
142 | 1 | public function initWith() |
|
147 | |||
148 | /** |
||
149 | * @return mixed |
||
150 | */ |
||
151 | 1 | public function getWithClass() |
|
155 | |||
156 | /** |
||
157 | * @return mixed |
||
158 | */ |
||
159 | 1 | public function getName() |
|
163 | |||
164 | /** |
||
165 | * @param mixed $name |
||
166 | */ |
||
167 | 2 | public function setName($name) |
|
171 | |||
172 | /** |
||
173 | * @param string $name |
||
174 | * @throws Exception |
||
175 | */ |
||
176 | 1 | public function setWithClass($name) |
|
187 | |||
188 | /** |
||
189 | * @return RecordManager |
||
190 | */ |
||
191 | 1 | public function getManager() |
|
199 | |||
200 | /** |
||
201 | * @param RecordManager|HasRelationsRecordsTrait $manager |
||
202 | */ |
||
203 | 3 | public function setManager($manager) |
|
207 | |||
208 | public function initManager() |
||
212 | |||
213 | /** |
||
214 | * @return Record |
||
215 | */ |
||
216 | 2 | public function getItem() |
|
220 | |||
221 | /** |
||
222 | * @param Record $item |
||
223 | * @return $this |
||
224 | */ |
||
225 | 3 | public function setItem(Record $item) |
|
231 | |||
232 | /** |
||
233 | * @param AbstractQuery $query |
||
234 | */ |
||
235 | public function populateQuerySpecific(AbstractQuery $query) |
||
238 | |||
239 | /** |
||
240 | * @return \Nip\Database\Query\Delete |
||
241 | */ |
||
242 | public function getDeleteQuery() |
||
249 | |||
250 | /** |
||
251 | * @return Connection |
||
252 | */ |
||
253 | 1 | public function getDB() |
|
257 | |||
258 | /** |
||
259 | * @param $key |
||
260 | * @return mixed |
||
261 | */ |
||
262 | 1 | public function getParam($key) |
|
266 | |||
267 | /** |
||
268 | * @param $key |
||
269 | * @return mixed |
||
270 | */ |
||
271 | 1 | public function hasParam($key) |
|
275 | |||
276 | /** |
||
277 | * @param $params |
||
278 | */ |
||
279 | 1 | public function addParams($params) |
|
287 | |||
288 | /** |
||
289 | * @param $params |
||
290 | */ |
||
291 | 1 | public function checkParamClass($params) |
|
298 | |||
299 | /** |
||
300 | * @param $params |
||
301 | */ |
||
302 | 1 | public function checkParamWith($params) |
|
309 | |||
310 | /** |
||
311 | * @param $params |
||
312 | */ |
||
313 | 1 | public function checkParamTable($params) |
|
320 | |||
321 | /** |
||
322 | * @param $params |
||
323 | */ |
||
324 | 1 | public function checkParamFk($params) |
|
331 | |||
332 | /** |
||
333 | * @param $params |
||
334 | */ |
||
335 | 1 | public function setParams($params) |
|
339 | |||
340 | /** |
||
341 | * @return string |
||
342 | */ |
||
343 | 1 | public function getTable() |
|
351 | |||
352 | /** |
||
353 | * @param $name |
||
354 | */ |
||
355 | 1 | public function setTable($name) |
|
359 | |||
360 | 1 | protected function initTable() |
|
364 | |||
365 | /** |
||
366 | * @return string |
||
367 | */ |
||
368 | protected function generateTable() |
||
372 | |||
373 | /** |
||
374 | * Get the results of the relationship. |
||
375 | * @return Record|RecordCollection |
||
376 | */ |
||
377 | 1 | public function getResults() |
|
385 | |||
386 | /** |
||
387 | * @param $results |
||
388 | * @return null |
||
389 | */ |
||
390 | 1 | public function setResults($results) |
|
397 | |||
398 | /** |
||
399 | * @return bool |
||
400 | */ |
||
401 | 1 | public function isPopulated() |
|
405 | |||
406 | abstract public function initResults(); |
||
407 | |||
408 | /** |
||
409 | * @param RecordCollection $collection |
||
410 | * @return RecordCollection |
||
411 | */ |
||
412 | public function getEagerResults($collection) |
||
421 | |||
422 | /** |
||
423 | * @param RecordCollection $collection |
||
424 | * @return Query |
||
425 | */ |
||
426 | View Code Duplication | public function getEagerQuery(RecordCollection $collection) |
|
434 | |||
435 | /** |
||
436 | * @param RecordCollection $collection |
||
437 | * @return array |
||
438 | */ |
||
439 | View Code Duplication | public function getEagerFkList(RecordCollection $collection) |
|
447 | |||
448 | /** |
||
449 | * @return string |
||
450 | */ |
||
451 | 1 | public function getFK() |
|
459 | |||
460 | /** |
||
461 | * @param $name |
||
462 | */ |
||
463 | 1 | public function setFK($name) |
|
467 | |||
468 | 1 | protected function initFK() |
|
472 | |||
473 | /** |
||
474 | * @return string |
||
475 | */ |
||
476 | protected function generateFK() |
||
480 | |||
481 | /** |
||
482 | * @return string |
||
483 | */ |
||
484 | public function getWithPK() |
||
488 | |||
489 | /** |
||
490 | * @param RecordCollection $collection |
||
491 | * @param RecordCollection $records |
||
492 | * |
||
493 | * @return RecordCollection |
||
494 | */ |
||
495 | public function match(RecordCollection $collection, RecordCollection $records) |
||
507 | |||
508 | /** |
||
509 | * Build model dictionary keyed by the relation's foreign key. |
||
510 | * |
||
511 | * @param RecordCollection $collection |
||
512 | * @return array |
||
513 | */ |
||
514 | abstract protected function buildDictionary(RecordCollection $collection); |
||
515 | |||
516 | /** |
||
517 | * @param $dictionary |
||
518 | * @param $collection |
||
519 | * @param $record |
||
520 | * @return mixed |
||
521 | */ |
||
522 | abstract public function getResultsFromCollectionDictionary($dictionary, $collection, $record); |
||
523 | |||
524 | public function save() |
||
527 | |||
528 | /** |
||
529 | * @return string |
||
530 | */ |
||
531 | public function getType() |
||
535 | } |
||
536 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.