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 MigrationTrait 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 MigrationTrait, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Limoncello\Data\Migrations; |
||
34 | trait MigrationTrait |
||
35 | { |
||
36 | /** |
||
37 | * @var ContainerInterface |
||
38 | */ |
||
39 | private $container; |
||
40 | |||
41 | /** |
||
42 | * @inheritdoc |
||
43 | */ |
||
44 | 2 | public function init(ContainerInterface $container): MigrationInterface |
|
53 | |||
54 | /** |
||
55 | * @return ContainerInterface |
||
56 | */ |
||
57 | 2 | protected function getContainer(): ContainerInterface |
|
61 | |||
62 | /** |
||
63 | * @return Connection |
||
64 | */ |
||
65 | 2 | protected function getConnection(): Connection |
|
71 | |||
72 | /** |
||
73 | * @return ModelSchemaInfoInterface |
||
74 | */ |
||
75 | 2 | protected function getModelSchemas(): ModelSchemaInfoInterface |
|
76 | { |
||
77 | 2 | assert($this->getContainer()->has(ModelSchemaInfoInterface::class) === true); |
|
78 | |||
79 | 2 | return $this->getContainer()->get(ModelSchemaInfoInterface::class); |
|
80 | } |
||
81 | |||
82 | /** |
||
83 | * @return AbstractSchemaManager |
||
84 | */ |
||
85 | 2 | protected function getSchemaManager(): AbstractSchemaManager |
|
89 | |||
90 | /** |
||
91 | * @param string $modelClass |
||
92 | * @param Closure[] $expressions |
||
93 | * |
||
94 | * @return Table |
||
95 | */ |
||
96 | 2 | protected function createTable(string $modelClass, array $expressions = []): Table |
|
110 | |||
111 | /** |
||
112 | * @param string $modelClass |
||
113 | * |
||
114 | * @return void |
||
115 | */ |
||
116 | 1 | protected function dropTableIfExists(string $modelClass): void |
|
125 | |||
126 | /** |
||
127 | * @param string $name |
||
128 | * |
||
129 | * @return Closure |
||
130 | */ |
||
131 | protected function primaryInt(string $name): Closure |
||
138 | |||
139 | /** |
||
140 | * @param string $name |
||
141 | * |
||
142 | * @return Closure |
||
143 | */ |
||
144 | View Code Duplication | protected function primaryString(string $name): Closure |
|
152 | |||
153 | /** |
||
154 | * @param string $name |
||
155 | * @param null|int $default |
||
156 | * |
||
157 | * @return Closure |
||
158 | */ |
||
159 | 1 | protected function unsignedInt(string $name, int $default = null): Closure |
|
163 | |||
164 | /** |
||
165 | * @param string $name |
||
166 | * @param null|int $default |
||
167 | * |
||
168 | * @return Closure |
||
169 | */ |
||
170 | 1 | protected function nullableUnsignedInt(string $name, int $default = null): Closure |
|
174 | |||
175 | /** |
||
176 | * @param string $name |
||
177 | * |
||
178 | * @return Closure |
||
179 | */ |
||
180 | protected function float(string $name): Closure |
||
188 | |||
189 | /** |
||
190 | * @param string $name |
||
191 | * |
||
192 | * @return Closure |
||
193 | */ |
||
194 | View Code Duplication | protected function string(string $name): Closure |
|
201 | |||
202 | /** |
||
203 | * @param string $name |
||
204 | * |
||
205 | * @return Closure |
||
206 | */ |
||
207 | View Code Duplication | protected function nullableString(string $name): Closure |
|
214 | |||
215 | /** |
||
216 | * @param string $name |
||
217 | * |
||
218 | * @return Closure |
||
219 | */ |
||
220 | protected function text(string $name): Closure |
||
226 | |||
227 | /** |
||
228 | * @param string $name |
||
229 | * |
||
230 | * @return Closure |
||
231 | */ |
||
232 | protected function nullableText(string $name): Closure |
||
238 | |||
239 | /** |
||
240 | * @param string $name |
||
241 | * @param null|bool $default |
||
242 | * |
||
243 | * @return Closure |
||
244 | */ |
||
245 | View Code Duplication | protected function bool(string $name, $default = null): Closure |
|
254 | |||
255 | /** |
||
256 | * @param string $name |
||
257 | * @param array $values |
||
258 | * |
||
259 | * @return Closure |
||
260 | */ |
||
261 | 1 | protected function enum(string $name, array $values): Closure |
|
265 | |||
266 | /** |
||
267 | * @param string $name |
||
268 | * @param array $values |
||
269 | * |
||
270 | * @return Closure |
||
271 | */ |
||
272 | 1 | protected function nullableEnum(string $name, array $values): Closure |
|
276 | |||
277 | /** |
||
278 | * @return Closure |
||
279 | */ |
||
280 | protected function timestamps(): Closure |
||
306 | |||
307 | /** |
||
308 | * @param string $name |
||
309 | * |
||
310 | * @return Closure |
||
311 | */ |
||
312 | protected function datetime(string $name): Closure |
||
318 | |||
319 | /** |
||
320 | * @param string $name |
||
321 | * |
||
322 | * @return Closure |
||
323 | */ |
||
324 | protected function nullableDatetime(string $name): Closure |
||
330 | |||
331 | /** |
||
332 | * @param string $name |
||
333 | * |
||
334 | * @return Closure |
||
335 | */ |
||
336 | protected function date(string $name): Closure |
||
342 | |||
343 | /** |
||
344 | * @param string $name |
||
345 | * |
||
346 | * @return Closure |
||
347 | */ |
||
348 | protected function nullableDate(string $name): Closure |
||
354 | |||
355 | /** |
||
356 | * @param string[] $names |
||
357 | * |
||
358 | * @return Closure |
||
359 | */ |
||
360 | protected function unique(array $names): Closure |
||
366 | |||
367 | /** |
||
368 | * @param string[] $names |
||
369 | * |
||
370 | * @return Closure |
||
371 | */ |
||
372 | protected function searchable(array $names): Closure |
||
378 | |||
379 | /** |
||
380 | * @param string $column |
||
381 | * @param string $referredClass |
||
382 | * @param bool $cascadeDelete |
||
383 | * |
||
384 | * @return Closure |
||
385 | * |
||
386 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||
387 | */ |
||
388 | View Code Duplication | protected function foreignRelationship( |
|
410 | |||
411 | /** |
||
412 | * @param string $column |
||
413 | * @param string $referredClass |
||
414 | * @param bool $cascadeDelete |
||
415 | * |
||
416 | * @return Closure |
||
417 | * |
||
418 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||
419 | */ |
||
420 | View Code Duplication | protected function nullableForeignRelationship( |
|
442 | |||
443 | /** |
||
444 | * @param string $localKey |
||
445 | * @param string $foreignTable |
||
446 | * @param string $foreignKey |
||
447 | * @param string $type |
||
448 | * @param bool $cascadeDelete |
||
449 | * |
||
450 | * @return Closure |
||
451 | * |
||
452 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||
453 | */ |
||
454 | 1 | protected function foreignColumn( |
|
463 | |||
464 | /** |
||
465 | * @param string $localKey |
||
466 | * @param string $foreignTable |
||
467 | * @param string $foreignKey |
||
468 | * @param string $type |
||
469 | * @param bool $cascadeDelete |
||
470 | * |
||
471 | * @return Closure |
||
472 | * |
||
473 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||
474 | */ |
||
475 | 1 | protected function nullableForeignColumn( |
|
484 | |||
485 | /** |
||
486 | * @param string $name |
||
487 | * @param bool $cascadeDelete |
||
488 | * |
||
489 | * @return Closure |
||
490 | * |
||
491 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||
492 | */ |
||
493 | 1 | protected function nullableRelationship(string $name, bool $cascadeDelete = false): Closure |
|
497 | |||
498 | /** |
||
499 | * @param string $name |
||
500 | * @param bool $cascadeDelete |
||
501 | * |
||
502 | * @return Closure |
||
503 | * |
||
504 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||
505 | */ |
||
506 | 1 | protected function relationship(string $name, bool $cascadeDelete = false): Closure |
|
510 | |||
511 | /** |
||
512 | * @param string $modelClass |
||
513 | * |
||
514 | * @return string |
||
515 | */ |
||
516 | 1 | protected function getTableNameForClass(string $modelClass): string |
|
527 | |||
528 | /** |
||
529 | * @param string $name |
||
530 | * @param bool $notNullable |
||
531 | * @param null|mixed $default |
||
532 | * |
||
533 | * @return Closure |
||
534 | */ |
||
535 | View Code Duplication | private function unsignedIntImpl($name, $notNullable, $default = null): Closure |
|
542 | |||
543 | /** |
||
544 | * @param string $name |
||
545 | * @param array $values |
||
546 | * @param bool $notNullable |
||
547 | * |
||
548 | * @return Closure |
||
549 | * |
||
550 | * @SuppressWarnings(PHPMD.StaticAccess) |
||
551 | */ |
||
552 | private function enumImpl($name, array $values, $notNullable): Closure |
||
560 | |||
561 | /** @noinspection PhpTooManyParametersInspection |
||
562 | * @param string $localKey |
||
563 | * @param string $foreignTable |
||
564 | * @param string $foreignKey |
||
565 | * @param string $type |
||
566 | * @param bool $notNullable |
||
567 | * @param bool $cascadeDelete |
||
568 | * |
||
569 | * @return Closure |
||
570 | */ |
||
571 | private function foreignColumnImpl( |
||
592 | |||
593 | /** |
||
594 | * @param string $name |
||
595 | * @param bool $notNullable |
||
596 | * @param bool $cascadeDelete |
||
597 | * |
||
598 | * @return Closure |
||
599 | */ |
||
600 | private function relationshipImpl(string $name, bool $notNullable, bool $cascadeDelete): Closure |
||
640 | } |
||
641 |
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.