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 |
||
47 | abstract class AbstractTable extends TableState implements TableInterface, LoggerAwareInterface |
||
48 | { |
||
49 | /** |
||
50 | * Some operation better to be logged. |
||
51 | */ |
||
52 | use LoggerTrait; |
||
53 | |||
54 | /** |
||
55 | * Indication that table is exists and current schema is fetched from database. |
||
56 | * |
||
57 | * @var bool |
||
58 | */ |
||
59 | private $exists = false; |
||
60 | |||
61 | /** |
||
62 | * Database specific tablePrefix. Required for table renames. |
||
63 | * |
||
64 | * @var string |
||
65 | */ |
||
66 | private $prefix = ''; |
||
67 | |||
68 | /** |
||
69 | * We have to remember original schema state to create set of diff based commands. |
||
70 | * |
||
71 | * @invisible |
||
72 | * @var TableState |
||
73 | */ |
||
74 | protected $initial = null; |
||
75 | |||
76 | /** |
||
77 | * Compares current and original states. |
||
78 | * |
||
79 | * @invisible |
||
80 | * @var Comparator |
||
81 | */ |
||
82 | protected $comparator = null; |
||
83 | |||
84 | /** |
||
85 | * @invisible |
||
86 | * @var Driver |
||
87 | */ |
||
88 | protected $driver = null; |
||
89 | |||
90 | /** |
||
91 | * Executes table operations. |
||
92 | * |
||
93 | * @var AbstractCommander |
||
94 | */ |
||
95 | protected $commander = null; |
||
96 | |||
97 | /** |
||
98 | * @param Driver $driver Parent driver. |
||
99 | * @param AbstractCommander $commander |
||
100 | * @param string $name Table name, must include table prefix. |
||
101 | * @param string $prefix Database specific table prefix. |
||
102 | */ |
||
103 | public function __construct(Driver $driver, AbstractCommander $commander, $name, $prefix) |
||
131 | |||
132 | /** |
||
133 | * Get associated table driver. |
||
134 | * |
||
135 | * @return Driver |
||
136 | */ |
||
137 | public function driver() |
||
141 | |||
142 | /** |
||
143 | * Get table comparator. |
||
144 | * |
||
145 | * @return Comparator |
||
146 | */ |
||
147 | public function comparator() |
||
151 | |||
152 | /** |
||
153 | * {@inheritdoc} |
||
154 | */ |
||
155 | public function exists() |
||
159 | |||
160 | /** |
||
161 | * {@inheritdoc} |
||
162 | * |
||
163 | * Automatically forces prefix value. |
||
164 | */ |
||
165 | public function setName($name) |
||
169 | |||
170 | /** |
||
171 | * {@inheritdoc} |
||
172 | * |
||
173 | * @param bool $quoted Quote name. |
||
174 | */ |
||
175 | public function getName($quoted = false) |
||
183 | |||
184 | /** |
||
185 | * Return database specific table prefix. |
||
186 | * |
||
187 | * @return string |
||
188 | */ |
||
189 | public function getPrefix() |
||
193 | |||
194 | /** |
||
195 | * {@inheritdoc} |
||
196 | */ |
||
197 | public function getDependencies() |
||
206 | |||
207 | /** |
||
208 | * Set table primary keys. Operation can only be applied for newly created tables. Now every |
||
209 | * database might support compound indexes. |
||
210 | * |
||
211 | * @param array $columns |
||
212 | * @return $this |
||
213 | * @throws SchemaException |
||
214 | */ |
||
215 | public function setPrimaryKeys(array $columns) |
||
225 | |||
226 | /** |
||
227 | * Get/create instance of AbstractColumn associated with current table. |
||
228 | * |
||
229 | * Examples: |
||
230 | * $table->column('name')->string(); |
||
231 | * |
||
232 | * @param string $name |
||
233 | * @return AbstractColumn |
||
234 | */ |
||
235 | public function column($name) |
||
246 | |||
247 | /** |
||
248 | * Get/create instance of AbstractIndex associated with current table based on list of forming |
||
249 | * column names. |
||
250 | * |
||
251 | * Example: |
||
252 | * $table->index('key'); |
||
253 | * $table->index('key', 'key2'); |
||
254 | * $table->index(['key', 'key2']); |
||
255 | * |
||
256 | * @param mixed $columns Column name, or array of columns. |
||
257 | * @param bool $forceType Force index in non-unique state. |
||
258 | * @return AbstractIndex |
||
259 | */ |
||
260 | public function index($columns, $forceType = true) |
||
272 | |||
273 | /** |
||
274 | * Get/create instance of AbstractIndex associated with current table based on list of forming |
||
275 | * column names. Index type must be forced as UNIQUE. |
||
276 | * |
||
277 | * Example: |
||
278 | * $table->unique('key'); |
||
279 | * $table->unique('key', 'key2'); |
||
280 | * $table->unique(['key', 'key2']); |
||
281 | * |
||
282 | * @param mixed $columns Column name, or array of columns. |
||
283 | * @return AbstractColumn|null |
||
284 | */ |
||
285 | public function unique($columns) |
||
291 | |||
292 | /** |
||
293 | * Get/create instance of AbstractReference associated with current table based on local column |
||
294 | * name. |
||
295 | * |
||
296 | * @param string $column Column name. |
||
297 | * @return AbstractReference|null |
||
298 | */ |
||
299 | public function foreign($column) |
||
310 | |||
311 | /** |
||
312 | * Rename column (only if column exists). |
||
313 | * |
||
314 | * @param string $column |
||
315 | * @param string $name New column name. |
||
316 | * @return $this |
||
317 | */ |
||
318 | public function renameColumn($column, $name) |
||
329 | |||
330 | /** |
||
331 | * Rename index (only if index exists). |
||
332 | * |
||
333 | * @param array $columns Index forming columns. |
||
334 | * @param string $name New index name. |
||
335 | * @return $this |
||
336 | */ |
||
337 | public function renameIndex(array $columns, $name) |
||
348 | |||
349 | /** |
||
350 | * Drop column by it's name. |
||
351 | * |
||
352 | * @param string $column |
||
353 | * @return $this |
||
354 | */ |
||
355 | public function dropColumn($column) |
||
364 | |||
365 | /** |
||
366 | * Drop index by it's forming columns. |
||
367 | * |
||
368 | * @param array $columns |
||
369 | * @return $this |
||
370 | */ |
||
371 | public function dropIndex(array $columns) |
||
379 | |||
380 | /** |
||
381 | * Drop foreign key by it's name. |
||
382 | * |
||
383 | * @param string $column |
||
384 | * @return $this |
||
385 | */ |
||
386 | public function dropForeign($column) |
||
394 | |||
395 | /** |
||
396 | * Shortcut for column() method. |
||
397 | * |
||
398 | * @param string $column |
||
399 | * @return AbstractColumn |
||
400 | */ |
||
401 | public function __get($column) |
||
405 | |||
406 | /** |
||
407 | * Column creation/altering shortcut, call chain is identical to: |
||
408 | * AbstractTable->column($name)->$type($arguments) |
||
409 | * |
||
410 | * Example: |
||
411 | * $table->string("name"); |
||
412 | * $table->text("some_column"); |
||
413 | * |
||
414 | * @param string $type |
||
415 | * @param array $arguments Type specific parameters. |
||
416 | * @return AbstractColumn |
||
417 | */ |
||
418 | public function __call($type, array $arguments) |
||
425 | |||
426 | /** |
||
427 | * Declare every existed element. Method has to be called if table modification applied to |
||
428 | * existed table to prevent dropping of existed elements. |
||
429 | * |
||
430 | * @return $this |
||
431 | */ |
||
432 | public function declareExisted() |
||
448 | |||
449 | /** |
||
450 | * Save table schema including every column, index, foreign key creation/altering. If table does |
||
451 | * not exist it must be created. |
||
452 | * |
||
453 | * @param bool $forgetColumns Drop all non declared columns. |
||
454 | * @param bool $forgetIndexes Drop all non declared indexes. |
||
455 | * @param bool $forgetForeigns Drop all non declared foreign keys. |
||
456 | */ |
||
457 | public function save($forgetColumns = true, $forgetIndexes = true, $forgetForeigns = true) |
||
474 | |||
475 | /** |
||
476 | * Drop table schema in database. This operation must be applied immediately. |
||
477 | */ |
||
478 | public function drop() |
||
491 | |||
492 | /** |
||
493 | * @return AbstractColumn|string |
||
494 | */ |
||
495 | public function __toString() |
||
499 | |||
500 | /** |
||
501 | * @return object |
||
502 | */ |
||
503 | public function __debugInfo() |
||
513 | |||
514 | /** |
||
515 | * Create table. |
||
516 | */ |
||
517 | protected function createSchema() |
||
523 | |||
524 | /** |
||
525 | * Execute schema update. |
||
526 | */ |
||
527 | protected function synchroniseSchema() |
||
540 | |||
541 | /** |
||
542 | * Synchronise columns. |
||
543 | * |
||
544 | * @return $this |
||
545 | */ |
||
546 | protected function synchroniseColumns() |
||
584 | |||
585 | /** |
||
586 | * Drop needed indexes. |
||
587 | * |
||
588 | * @return $this |
||
589 | */ |
||
590 | View Code Duplication | protected function dropIndexes() |
|
603 | |||
604 | /** |
||
605 | * Synchronise indexes. |
||
606 | * |
||
607 | * @return $this |
||
608 | */ |
||
609 | View Code Duplication | protected function synchroniseIndexes() |
|
638 | |||
639 | /** |
||
640 | * Drop needed foreign keys. |
||
641 | * |
||
642 | * @return $this |
||
643 | */ |
||
644 | View Code Duplication | protected function dropForeigns() |
|
657 | |||
658 | /** |
||
659 | * Synchronise foreign keys. |
||
660 | * |
||
661 | * @return $this |
||
662 | */ |
||
663 | View Code Duplication | protected function synchroniseForeigns() |
|
691 | |||
692 | /** |
||
693 | * Driver specific column schema. |
||
694 | * |
||
695 | * @param string $name |
||
696 | * @param mixed $schema |
||
697 | * @return AbstractColumn |
||
698 | */ |
||
699 | abstract protected function columnSchema($name, $schema = null); |
||
700 | |||
701 | /** |
||
702 | * Driver specific index schema. |
||
703 | * |
||
704 | * @param string $name |
||
705 | * @param mixed $schema |
||
706 | * @return AbstractIndex |
||
707 | */ |
||
708 | abstract protected function indexSchema($name, $schema = null); |
||
709 | |||
710 | /** |
||
711 | * Driver specific reference schema. |
||
712 | * |
||
713 | * @param string $name |
||
714 | * @param mixed $schema |
||
715 | * @return AbstractReference |
||
716 | */ |
||
717 | abstract protected function referenceSchema($name, $schema = null); |
||
718 | |||
719 | |||
720 | /** |
||
721 | * Must load table columns. |
||
722 | * |
||
723 | * @see registerColumn() |
||
724 | * @return self |
||
725 | */ |
||
726 | abstract protected function loadColumns(); |
||
727 | |||
728 | /** |
||
729 | * Must load table indexes. |
||
730 | * |
||
731 | * @see registerIndex() |
||
732 | * @return self |
||
733 | */ |
||
734 | abstract protected function loadIndexes(); |
||
735 | |||
736 | /** |
||
737 | * Must load table references. |
||
738 | * |
||
739 | * @see registerReference() |
||
740 | * @return self |
||
741 | */ |
||
742 | abstract protected function loadReferences(); |
||
743 | |||
744 | /** |
||
745 | * Check if table schema has been modified. Attention, you have to execute dropUndeclared first |
||
746 | * to get valid results. |
||
747 | * |
||
748 | * @return bool |
||
749 | */ |
||
750 | protected function hasChanges() |
||
754 | |||
755 | /** |
||
756 | * Calculate difference (removed columns, indexes and foreign keys). |
||
757 | * |
||
758 | * @param bool $forgetColumns |
||
759 | * @param bool $forgetIndexes |
||
760 | * @param bool $forgetForeigns |
||
761 | */ |
||
762 | protected function forgetUndeclared($forgetColumns, $forgetIndexes, $forgetForeigns) |
||
786 | |||
787 | /** |
||
788 | * Remove dependent indexes and foreign keys. |
||
789 | * |
||
790 | * @param ColumnInterface $column |
||
791 | */ |
||
792 | private function removeDependent(ColumnInterface $column) |
||
805 | |||
806 | /** |
||
807 | * Forget all elements. |
||
808 | * |
||
809 | * @return $this |
||
810 | */ |
||
811 | private function forgetElements() |
||
827 | } |
This check looks for a call to a parent method whose name is different than the method from which it is called.
Consider the following code:
The
getFirstName()
method in theSon
calls the wrong method in the parent class.