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 AbstractTable 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 AbstractTable, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | abstract class AbstractTable extends TableState implements TableInterface, LoggerAwareInterface |
||
52 | { |
||
53 | use LoggerTrait; |
||
54 | |||
55 | /** |
||
56 | * Indication that table is exists and current schema is fetched from database. |
||
57 | * |
||
58 | * @var bool |
||
59 | */ |
||
60 | private $exists = false; |
||
61 | |||
62 | /** |
||
63 | * Database specific tablePrefix. Required for table renames. |
||
64 | * |
||
65 | * @var string |
||
66 | */ |
||
67 | private $prefix = ''; |
||
68 | |||
69 | /** |
||
70 | * We have to remember original schema state to create set of diff based commands. |
||
71 | * |
||
72 | * @invisible |
||
73 | * |
||
74 | * @var TableState |
||
75 | */ |
||
76 | protected $initial = null; |
||
77 | |||
78 | /** |
||
79 | * Compares current and original states. |
||
80 | * |
||
81 | * @invisible |
||
82 | * |
||
83 | * @var Comparator |
||
84 | */ |
||
85 | protected $comparator = null; |
||
86 | |||
87 | /** |
||
88 | * @invisible |
||
89 | * |
||
90 | * @var Driver |
||
91 | */ |
||
92 | protected $driver = null; |
||
93 | |||
94 | /** |
||
95 | * Executes table operations. |
||
96 | * |
||
97 | * @var AbstractCommander |
||
98 | */ |
||
99 | protected $commander = null; |
||
100 | |||
101 | /** |
||
102 | * @param Driver $driver Parent driver. |
||
103 | * @param AbstractCommander $commander |
||
104 | * @param string $name Table name, must include table prefix. |
||
105 | * @param string $prefix Database specific table prefix. |
||
106 | */ |
||
107 | public function __construct(Driver $driver, AbstractCommander $commander, $name, $prefix) |
||
135 | |||
136 | /** |
||
137 | * Get associated table driver. |
||
138 | * |
||
139 | * @return Driver |
||
140 | */ |
||
141 | public function driver() |
||
145 | |||
146 | /** |
||
147 | * Get table comparator. |
||
148 | * |
||
149 | * @return Comparator |
||
150 | */ |
||
151 | public function comparator() |
||
155 | |||
156 | /** |
||
157 | * {@inheritdoc} |
||
158 | */ |
||
159 | public function exists() |
||
163 | |||
164 | /** |
||
165 | * {@inheritdoc} |
||
166 | * |
||
167 | * Automatically forces prefix value. |
||
168 | */ |
||
169 | public function setName($name) |
||
173 | |||
174 | /** |
||
175 | * {@inheritdoc} |
||
176 | * |
||
177 | * @param bool $quoted Quote name. |
||
178 | */ |
||
179 | public function getName($quoted = false) |
||
187 | |||
188 | /** |
||
189 | * Return database specific table prefix. |
||
190 | * |
||
191 | * @return string |
||
192 | */ |
||
193 | public function getPrefix() |
||
197 | |||
198 | /** |
||
199 | * {@inheritdoc} |
||
200 | */ |
||
201 | public function getDependencies() |
||
210 | |||
211 | /** |
||
212 | * Set table primary keys. Operation can only be applied for newly created tables. Now every |
||
213 | * database might support compound indexes. |
||
214 | * |
||
215 | * @param array $columns |
||
216 | * |
||
217 | * @return $this |
||
218 | * |
||
219 | * @throws SchemaException |
||
220 | */ |
||
221 | public function setPrimaryKeys(array $columns) |
||
231 | |||
232 | /** |
||
233 | * Get/create instance of AbstractColumn associated with current table. |
||
234 | * |
||
235 | * Examples: |
||
236 | * $table->column('name')->string(); |
||
237 | * |
||
238 | * @param string $name |
||
239 | * |
||
240 | * @return AbstractColumn |
||
241 | */ |
||
242 | public function column($name) |
||
253 | |||
254 | /** |
||
255 | * Get/create instance of AbstractIndex associated with current table based on list of forming |
||
256 | * column names. |
||
257 | * |
||
258 | * Example: |
||
259 | * $table->index('key'); |
||
260 | * $table->index('key', 'key2'); |
||
261 | * $table->index(['key', 'key2']); |
||
262 | * |
||
263 | * @param mixed $columns Column name, or array of columns. |
||
264 | * |
||
265 | * @return AbstractIndex |
||
266 | */ |
||
267 | public function index($columns) |
||
279 | |||
280 | /** |
||
281 | * Get/create instance of AbstractIndex associated with current table based on list of forming |
||
282 | * column names. Index type must be forced as UNIQUE. |
||
283 | * |
||
284 | * Example: |
||
285 | * $table->unique('key'); |
||
286 | * $table->unique('key', 'key2'); |
||
287 | * $table->unique(['key', 'key2']); |
||
288 | * |
||
289 | * @param mixed $columns Column name, or array of columns. |
||
290 | * |
||
291 | * @return AbstractColumn|null |
||
292 | */ |
||
293 | public function unique($columns) |
||
299 | |||
300 | /** |
||
301 | * Get/create instance of AbstractReference associated with current table based on local column |
||
302 | * name. |
||
303 | * |
||
304 | * @param string $column Column name. |
||
305 | * |
||
306 | * @return AbstractReference|null |
||
307 | */ |
||
308 | public function foreign($column) |
||
319 | |||
320 | /** |
||
321 | * Rename column (only if column exists). |
||
322 | * |
||
323 | * @param string $column |
||
324 | * @param string $name New column name. |
||
325 | * |
||
326 | * @return $this |
||
327 | */ |
||
328 | public function renameColumn($column, $name) |
||
339 | |||
340 | /** |
||
341 | * Rename index (only if index exists). |
||
342 | * |
||
343 | * @param array $columns Index forming columns. |
||
344 | * @param string $name New index name. |
||
345 | * |
||
346 | * @return $this |
||
347 | */ |
||
348 | public function renameIndex(array $columns, $name) |
||
359 | |||
360 | /** |
||
361 | * Drop column by it's name. |
||
362 | * |
||
363 | * @param string $column |
||
364 | * |
||
365 | * @return $this |
||
366 | */ |
||
367 | public function dropColumn($column) |
||
376 | |||
377 | /** |
||
378 | * Drop index by it's forming columns. |
||
379 | * |
||
380 | * @param array $columns |
||
381 | * |
||
382 | * @return $this |
||
383 | */ |
||
384 | public function dropIndex(array $columns) |
||
392 | |||
393 | /** |
||
394 | * Drop foreign key by it's name. |
||
395 | * |
||
396 | * @param string $column |
||
397 | * |
||
398 | * @return $this |
||
399 | */ |
||
400 | public function dropForeign($column) |
||
408 | |||
409 | /** |
||
410 | * Shortcut for column() method. |
||
411 | * |
||
412 | * @param string $column |
||
413 | * |
||
414 | * @return AbstractColumn |
||
415 | */ |
||
416 | public function __get($column) |
||
420 | |||
421 | /** |
||
422 | * Column creation/altering shortcut, call chain is identical to: |
||
423 | * AbstractTable->column($name)->$type($arguments). |
||
424 | * |
||
425 | * Example: |
||
426 | * $table->string("name"); |
||
427 | * $table->text("some_column"); |
||
428 | * |
||
429 | * @param string $type |
||
430 | * @param array $arguments Type specific parameters. |
||
431 | * |
||
432 | * @return AbstractColumn |
||
433 | */ |
||
434 | public function __call($type, array $arguments) |
||
441 | |||
442 | /** |
||
443 | * Declare every existed element. Method has to be called if table modification applied to |
||
444 | * existed table to prevent dropping of existed elements. |
||
445 | * |
||
446 | * @return $this |
||
447 | */ |
||
448 | public function declareExisted() |
||
464 | |||
465 | /** |
||
466 | * Calculate difference (removed columns, indexes and foreign keys). |
||
467 | * |
||
468 | * @param bool $forgetColumns |
||
469 | * @param bool $forgetIndexes |
||
470 | * @param bool $forgetForeigns |
||
471 | */ |
||
472 | public function forgetUndeclared($forgetColumns, $forgetIndexes, $forgetForeigns) |
||
496 | |||
497 | /** |
||
498 | * Save table schema including every column, index, foreign key creation/altering. If table does |
||
499 | * not exist it must be created. |
||
500 | * |
||
501 | * @param bool $forgetColumns Drop all non declared columns. |
||
502 | * @param bool $forgetIndexes Drop all non declared indexes. |
||
503 | * @param bool $forgetForeigns Drop all non declared foreign keys. |
||
504 | */ |
||
505 | public function save($forgetColumns = true, $forgetIndexes = true, $forgetForeigns = true) |
||
522 | |||
523 | /** |
||
524 | * Drop table schema in database. This operation must be applied immediately. |
||
525 | */ |
||
526 | public function drop() |
||
539 | |||
540 | /** |
||
541 | * @return AbstractColumn|string |
||
542 | */ |
||
543 | public function __toString() |
||
547 | |||
548 | /** |
||
549 | * @return array |
||
550 | */ |
||
551 | public function __debugInfo() |
||
561 | |||
562 | /** |
||
563 | * Create table. |
||
564 | */ |
||
565 | protected function createSchema() |
||
571 | |||
572 | /** |
||
573 | * Execute schema update. |
||
574 | */ |
||
575 | protected function synchroniseSchema() |
||
588 | |||
589 | /** |
||
590 | * Synchronise columns. |
||
591 | * |
||
592 | * @todo Split or isolate. |
||
593 | * @return $this |
||
594 | */ |
||
595 | protected function synchroniseColumns() |
||
633 | |||
634 | /** |
||
635 | * Drop needed indexes. |
||
636 | * |
||
637 | * @return $this |
||
638 | */ |
||
639 | View Code Duplication | protected function dropIndexes() |
|
652 | |||
653 | /** |
||
654 | * Synchronise indexes. |
||
655 | * |
||
656 | * @return $this |
||
657 | */ |
||
658 | View Code Duplication | protected function synchroniseIndexes() |
|
687 | |||
688 | /** |
||
689 | * Drop needed foreign keys. |
||
690 | * |
||
691 | * @return $this |
||
692 | */ |
||
693 | View Code Duplication | protected function dropForeigns() |
|
706 | |||
707 | /** |
||
708 | * Synchronise foreign keys. |
||
709 | * |
||
710 | * @return $this |
||
711 | */ |
||
712 | View Code Duplication | protected function synchroniseForeigns() |
|
740 | |||
741 | /** |
||
742 | * Driver specific column schema. |
||
743 | * |
||
744 | * @param string $name |
||
745 | * @param mixed $schema |
||
746 | * |
||
747 | * @return AbstractColumn |
||
748 | */ |
||
749 | abstract protected function columnSchema($name, $schema = null); |
||
750 | |||
751 | /** |
||
752 | * Driver specific index schema. |
||
753 | * |
||
754 | * @param string $name |
||
755 | * @param mixed $schema |
||
756 | * |
||
757 | * @return AbstractIndex |
||
758 | */ |
||
759 | abstract protected function indexSchema($name, $schema = null); |
||
760 | |||
761 | /** |
||
762 | * Driver specific reference schema. |
||
763 | * |
||
764 | * @param string $name |
||
765 | * @param mixed $schema |
||
766 | * |
||
767 | * @return AbstractReference |
||
768 | */ |
||
769 | abstract protected function referenceSchema($name, $schema = null); |
||
770 | |||
771 | /** |
||
772 | * Must load table columns. |
||
773 | * |
||
774 | * @see registerColumn() |
||
775 | * |
||
776 | * @return self |
||
777 | */ |
||
778 | abstract protected function loadColumns(); |
||
779 | |||
780 | /** |
||
781 | * Must load table indexes. |
||
782 | * |
||
783 | * @see registerIndex() |
||
784 | * |
||
785 | * @return self |
||
786 | */ |
||
787 | abstract protected function loadIndexes(); |
||
788 | |||
789 | /** |
||
790 | * Must load table references. |
||
791 | * |
||
792 | * @see registerReference() |
||
793 | * |
||
794 | * @return self |
||
795 | */ |
||
796 | abstract protected function loadReferences(); |
||
797 | |||
798 | /** |
||
799 | * Check if table schema has been modified. Attention, you have to execute dropUndeclared first |
||
800 | * to get valid results. |
||
801 | * |
||
802 | * @return bool |
||
803 | */ |
||
804 | protected function hasChanges() |
||
808 | |||
809 | /** |
||
810 | * Remove dependent indexes and foreign keys. |
||
811 | * |
||
812 | * @param ColumnInterface $column |
||
813 | */ |
||
814 | private function removeDependent(ColumnInterface $column) |
||
827 | |||
828 | /** |
||
829 | * Forget all elements. |
||
830 | * |
||
831 | * @return $this |
||
832 | */ |
||
833 | private function forgetElements() |
||
849 | } |
||
850 |
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.