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 Table 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 Table, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class Table |
||
35 | { |
||
36 | /** |
||
37 | * @var \Phinx\Db\Table\Table |
||
38 | */ |
||
39 | protected $table; |
||
40 | |||
41 | /** |
||
42 | * @var \Phinx\Db\Adapter\AdapterInterface |
||
43 | */ |
||
44 | protected $adapter; |
||
45 | |||
46 | /** |
||
47 | * @var \Phinx\Db\Plan\Intent |
||
48 | */ |
||
49 | protected $actions; |
||
50 | |||
51 | /** |
||
52 | * @var array |
||
53 | */ |
||
54 | protected $data = []; |
||
55 | |||
56 | /** |
||
57 | * @param string $name Table Name |
||
58 | * @param array $options Options |
||
59 | * @param \Phinx\Db\Adapter\AdapterInterface|null $adapter Database Adapter |
||
60 | */ |
||
61 | public function __construct($name, $options = [], AdapterInterface $adapter = null) |
||
62 | { |
||
63 | $this->table = new TableValue($name, $options); |
||
64 | $this->actions = new Intent(); |
||
65 | |||
66 | if ($adapter !== null) { |
||
67 | $this->setAdapter($adapter); |
||
68 | } |
||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Gets the table name. |
||
73 | * |
||
74 | * @return string|null |
||
75 | */ |
||
76 | public function getName() |
||
77 | { |
||
78 | return $this->table->getName(); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Gets the table options. |
||
83 | * |
||
84 | 239 | * @return array |
|
85 | */ |
||
86 | 239 | public function getOptions() |
|
87 | 239 | { |
|
88 | return $this->table->getOptions(); |
||
89 | 239 | } |
|
90 | 231 | ||
91 | 231 | /** |
|
92 | 239 | * Gets the table name and options as an object |
|
93 | * |
||
94 | * @return \Phinx\Db\Table\Table |
||
95 | */ |
||
96 | public function getTable() |
||
97 | { |
||
98 | return $this->table; |
||
99 | } |
||
100 | 239 | ||
101 | /** |
||
102 | 239 | * Sets the database adapter. |
|
103 | 239 | * |
|
104 | * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter |
||
105 | * |
||
106 | * @return $this |
||
107 | */ |
||
108 | public function setAdapter(AdapterInterface $adapter) |
||
109 | { |
||
110 | $this->adapter = $adapter; |
||
111 | 215 | ||
112 | return $this; |
||
113 | 215 | } |
|
114 | |||
115 | /** |
||
116 | * Gets the database adapter. |
||
117 | * |
||
118 | * @throws \RuntimeException |
||
119 | * |
||
120 | * @return \Phinx\Db\Adapter\AdapterInterface|null |
||
121 | */ |
||
122 | 239 | public function getAdapter() |
|
123 | { |
||
124 | 239 | if (!$this->adapter) { |
|
125 | 239 | throw new RuntimeException('There is no database adapter set yet, cannot proceed'); |
|
126 | } |
||
127 | |||
128 | return $this->adapter; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Does the table have pending actions? |
||
133 | 189 | * |
|
134 | * @return bool |
||
135 | 189 | */ |
|
136 | public function hasPendingActions() |
||
137 | { |
||
138 | return count($this->actions->getActions()) > 0 || count($this->data) > 0; |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * Does the table exist? |
||
143 | * |
||
144 | 231 | * @return bool |
|
145 | */ |
||
146 | 231 | public function exists() |
|
147 | 231 | { |
|
148 | return $this->getAdapter()->hasTable($this->getName()); |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Drops the database table. |
||
153 | * |
||
154 | * @return $this |
||
155 | 225 | */ |
|
156 | public function drop() |
||
157 | 225 | { |
|
158 | $this->actions->addAction(new DropTable($this->table)); |
||
159 | |||
160 | return $this; |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Renames the database table. |
||
165 | 195 | * |
|
166 | * @param string $newTableName New Table Name |
||
167 | 195 | * |
|
168 | * @return $this |
||
169 | */ |
||
170 | public function rename($newTableName) |
||
171 | { |
||
172 | $this->actions->addAction(new RenameTable($this->table, $newTableName)); |
||
173 | |||
174 | return $this; |
||
175 | 1 | } |
|
176 | |||
177 | 1 | /** |
|
178 | 1 | * Changes the primary key of the database table. |
|
179 | * |
||
180 | * @param string|array|null $columns Column name(s) to belong to the primary key, or null to drop the key |
||
181 | * |
||
182 | * @return $this |
||
183 | */ |
||
184 | public function changePrimaryKey($columns) |
||
185 | { |
||
186 | 3 | $this->actions->addAction(new ChangePrimaryKey($this->table, $columns)); |
|
187 | |||
188 | 3 | return $this; |
|
189 | 3 | } |
|
190 | 3 | ||
191 | /** |
||
192 | * Changes the comment of the database table. |
||
193 | * |
||
194 | * @param string|null $comment New comment string, or null to drop the comment |
||
195 | * |
||
196 | * @return $this |
||
197 | */ |
||
198 | public function changeComment($comment) |
||
199 | { |
||
200 | $this->actions->addAction(new ChangeComment($this->table, $comment)); |
||
201 | |||
202 | return $this; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Gets an array of the table columns. |
||
207 | * |
||
208 | * @return \Phinx\Db\Table\Column[] |
||
209 | */ |
||
210 | public function getColumns() |
||
211 | 10 | { |
|
212 | return $this->getAdapter()->getColumns($this->getName()); |
||
213 | 10 | } |
|
214 | |||
215 | /** |
||
216 | * Gets a table column if it exists. |
||
217 | * |
||
218 | * @param string $name Column name |
||
219 | * |
||
220 | * @return \Phinx\Db\Table\Column|null |
||
221 | */ |
||
222 | 196 | public function getColumn($name) |
|
223 | { |
||
224 | 196 | $columns = array_filter( |
|
225 | 196 | $this->getColumns(), |
|
226 | function ($column) use ($name) { |
||
227 | return $column->getName() === $name; |
||
228 | } |
||
229 | ); |
||
230 | |||
231 | return array_pop($columns); |
||
232 | } |
||
233 | 204 | ||
234 | /** |
||
235 | 204 | * Sets an array of data to be inserted. |
|
236 | * |
||
237 | * @param array $data Data |
||
238 | * |
||
239 | * @return $this |
||
240 | */ |
||
241 | public function setData($data) |
||
242 | { |
||
243 | $this->data = $data; |
||
244 | 196 | ||
245 | return $this; |
||
246 | 196 | } |
|
247 | 196 | ||
248 | /** |
||
249 | * Gets the data waiting to be inserted. |
||
250 | * |
||
251 | * @return array |
||
252 | */ |
||
253 | public function getData() |
||
254 | { |
||
255 | 191 | return $this->data; |
|
256 | } |
||
257 | 191 | ||
258 | /** |
||
259 | * Resets all of the pending data to be inserted |
||
260 | * |
||
261 | * @return void |
||
262 | */ |
||
263 | public function resetData() |
||
264 | { |
||
265 | $this->setData([]); |
||
266 | 196 | } |
|
267 | |||
268 | 196 | /** |
|
269 | 196 | * Resets all of the pending table changes. |
|
270 | * |
||
271 | * @return void |
||
272 | */ |
||
273 | public function reset() |
||
274 | { |
||
275 | $this->actions = new Intent(); |
||
276 | $this->resetData(); |
||
277 | 192 | } |
|
278 | |||
279 | 192 | /** |
|
280 | * Add a table column. |
||
281 | * |
||
282 | * Type can be: string, text, integer, float, decimal, datetime, timestamp, |
||
283 | * time, date, binary, boolean. |
||
284 | * |
||
285 | * Valid options can be: limit, default, null, precision or scale. |
||
286 | * |
||
287 | * @param string|\Phinx\Db\Table\Column $columnName Column Name |
||
288 | 196 | * @param string|\Phinx\Util\Literal|null $type Column Type |
|
289 | * @param array $options Column Options |
||
290 | 196 | * |
|
291 | 196 | * @throws \InvalidArgumentException |
|
292 | * |
||
293 | * @return $this |
||
294 | */ |
||
295 | public function addColumn($columnName, $type = null, $options = []) |
||
296 | { |
||
297 | if ($columnName instanceof Column) { |
||
298 | $action = new AddColumn($this->table, $columnName); |
||
299 | 197 | } else { |
|
300 | if ('sqlite' == $this->getAdapter()->getAdapterType() || 'sqlite' == (getenv('PHINX_TESTING_ADAPTER'))) { |
||
301 | 197 | array_key_exists('null', $options) ?: $options['null'] = true; |
|
302 | } |
||
303 | $action = AddColumn::build($this->table, $columnName, $type, $options); |
||
304 | } |
||
305 | |||
306 | // Delegate to Adapters to check column type |
||
307 | if (!$this->getAdapter()->isValidColumnType($action->getColumn())) { |
||
308 | throw new InvalidArgumentException(sprintf( |
||
309 | 196 | 'An invalid column type "%s" was specified for column "%s".', |
|
310 | $type, |
||
311 | 196 | $action->getColumn()->getName() |
|
312 | 196 | )); |
|
313 | 196 | } |
|
314 | 196 | ||
315 | 196 | $this->actions->addAction($action); |
|
316 | |||
317 | return $this; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * Remove a table column. |
||
322 | * |
||
323 | * @param string $columnName Column Name |
||
324 | * |
||
325 | * @return $this |
||
326 | */ |
||
327 | public function removeColumn($columnName) |
||
334 | |||
335 | 210 | /** |
|
336 | 1 | * Rename a table column. |
|
337 | * |
||
338 | * @param string $oldName Old Column Name |
||
339 | * @param string $newName New Column Name |
||
340 | 209 | * |
|
341 | 207 | * @return $this |
|
342 | 207 | */ |
|
343 | 207 | public function renameColumn($oldName, $newName) |
|
344 | 207 | { |
|
345 | 207 | $action = RenameColumn::build($this->table, $oldName, $newName); |
|
346 | 2 | $this->actions->addAction($action); |
|
347 | |||
348 | return $this; |
||
349 | } |
||
350 | 209 | ||
351 | 1 | /** |
|
352 | 1 | * Change a table column type. |
|
353 | 1 | * |
|
354 | 1 | * @param string $columnName Column Name |
|
355 | 1 | * @param string|\Phinx\Db\Table\Column|\Phinx\Util\Literal $newColumnType New Column Type |
|
356 | * @param array $options Options |
||
357 | * |
||
358 | 208 | * @return $this |
|
359 | 208 | */ |
|
360 | public function changeColumn($columnName, $newColumnType, array $options = []) |
||
361 | { |
||
362 | if ($newColumnType instanceof Column) { |
||
363 | $action = new ChangeColumn($this->table, $columnName, $newColumnType); |
||
364 | } else { |
||
365 | $action = ChangeColumn::build($this->table, $columnName, $newColumnType, $options); |
||
366 | } |
||
367 | $this->actions->addAction($action); |
||
368 | 1 | ||
369 | return $this; |
||
370 | 1 | } |
|
371 | 1 | ||
372 | /** |
||
373 | * Checks to see if a column exists. |
||
374 | * |
||
375 | * @param string $columnName Column Name |
||
376 | * |
||
377 | * @return bool |
||
378 | */ |
||
379 | public function hasColumn($columnName) |
||
380 | { |
||
381 | 4 | return $this->getAdapter()->hasColumn($this->getName(), $columnName); |
|
382 | } |
||
383 | 4 | ||
384 | 4 | /** |
|
385 | * Add an index to a database table. |
||
386 | * |
||
387 | * In $options you can specific unique = true/false or name (index name). |
||
388 | * |
||
389 | * @param string|array|\Phinx\Db\Table\Index $columns Table Column(s) |
||
390 | * @param array $options Index Options |
||
391 | * |
||
392 | * @return $this |
||
393 | */ |
||
394 | public function addIndex($columns, array $options = []) |
||
395 | 17 | { |
|
396 | $action = AddIndex::build($this->table, $columns, $options); |
||
397 | $this->actions->addAction($action); |
||
398 | 17 | ||
399 | 4 | return $this; |
|
400 | 4 | } |
|
401 | 4 | ||
402 | 4 | /** |
|
403 | 13 | * Removes the given index from a table. |
|
404 | * |
||
405 | * @param string|array $columns Columns |
||
406 | * |
||
407 | 17 | * @return $this |
|
408 | 15 | */ |
|
409 | 15 | public function removeIndex($columns) |
|
410 | { |
||
411 | 17 | $action = DropIndex::build($this->table, is_string($columns) ? [$columns] : $columns); |
|
412 | 17 | $this->actions->addAction($action); |
|
413 | |||
414 | return $this; |
||
415 | } |
||
416 | |||
417 | /** |
||
418 | * Removes the given index identified by its name from a table. |
||
419 | * |
||
420 | * @param string $name Index name |
||
421 | 89 | * |
|
422 | * @return $this |
||
423 | 89 | */ |
|
424 | public function removeIndexByName($name) |
||
425 | { |
||
426 | $action = DropIndex::buildFromName($this->table, $name); |
||
427 | $this->actions->addAction($action); |
||
428 | |||
429 | return $this; |
||
430 | } |
||
431 | |||
432 | /** |
||
433 | * Checks to see if an index exists. |
||
434 | * |
||
435 | 29 | * @param string|array $columns Columns |
|
436 | * |
||
437 | * @return bool |
||
438 | 29 | */ |
|
439 | 28 | public function hasIndex($columns) |
|
440 | 28 | { |
|
441 | 22 | return $this->getAdapter()->hasIndex($this->getName(), $columns); |
|
|
|||
442 | 22 | } |
|
443 | 28 | ||
444 | 28 | /** |
|
445 | 28 | * Checks to see if an index specified by name exists. |
|
446 | 1 | * |
|
447 | * @param string $indexName |
||
448 | * |
||
449 | 29 | * @return bool |
|
450 | 29 | */ |
|
451 | public function hasIndexByName($indexName) |
||
452 | { |
||
453 | return $this->getAdapter()->hasIndexByName($this->getName(), $indexName); |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Add a foreign key to a database table. |
||
458 | * |
||
459 | 1 | * In $options you can specify on_delete|on_delete = cascade|no_action .., |
|
460 | * on_update, constraint = constraint name. |
||
461 | 1 | * |
|
462 | 1 | * @param string|array $columns Columns |
|
463 | * @param string|\Phinx\Db\Table $referencedTable Referenced Table |
||
464 | * @param string|array $referencedColumns Referenced Columns |
||
465 | * @param array $options Options |
||
466 | * |
||
467 | * @return $this |
||
468 | */ |
||
469 | View Code Duplication | public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], $options = []) |
|
470 | { |
||
471 | 1 | $action = AddForeignKey::build($this->table, $columns, $referencedTable, $referencedColumns, $options); |
|
472 | $this->actions->addAction($action); |
||
473 | 1 | ||
474 | 1 | return $this; |
|
475 | } |
||
476 | |||
477 | /** |
||
478 | * Add a foreign key to a database table with a given name. |
||
479 | * |
||
480 | * In $options you can specify on_delete|on_delete = cascade|no_action .., |
||
481 | * on_update, constraint = constraint name. |
||
482 | * |
||
483 | * @param string $name The constraint name |
||
484 | 12 | * @param string|array $columns Columns |
|
485 | * @param string|\Phinx\Db\Table $referencedTable Referenced Table |
||
486 | 12 | * @param string|array $referencedColumns Referenced Columns |
|
487 | * @param array $options Options |
||
488 | * |
||
489 | * @return $this |
||
490 | */ |
||
491 | View Code Duplication | public function addForeignKeyWithName($name, $columns, $referencedTable, $referencedColumns = ['id'], $options = []) |
|
505 | 4 | ||
506 | 8 | /** |
|
507 | 8 | * Removes the given foreign key from the table. |
|
508 | * |
||
509 | * @param string|array $columns Column(s) |
||
510 | 8 | * @param string|null $constraint Constraint names |
|
511 | * |
||
512 | 8 | * @return $this |
|
513 | 8 | */ |
|
514 | 8 | public function dropForeignKey($columns, $constraint = null) |
|
515 | 8 | { |
|
516 | $action = DropForeignKey::build($this->table, $columns, $constraint); |
||
517 | 8 | $this->actions->addAction($action); |
|
518 | |||
519 | return $this; |
||
520 | } |
||
521 | |||
522 | /** |
||
523 | * Checks to see if a foreign key exists. |
||
524 | * |
||
525 | * @param string|array $columns Column(s) |
||
526 | * @param string|null $constraint Constraint names |
||
527 | 1 | * |
|
528 | * @return bool |
||
529 | 1 | */ |
|
530 | 1 | public function hasForeignKey($columns, $constraint = null) |
|
531 | 1 | { |
|
532 | 1 | return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint); |
|
533 | } |
||
534 | |||
535 | 1 | /** |
|
536 | * Add timestamp columns created_at and updated_at to the table. |
||
537 | * |
||
538 | 1 | * @param string|null $createdAt Alternate name for the created_at column |
|
539 | * @param string|null $updatedAt Alternate name for the updated_at column |
||
540 | * @param bool $withTimezone Whether to set the timezone option on the added columns |
||
541 | * |
||
542 | * @return $this |
||
543 | */ |
||
544 | public function addTimestamps($createdAt = 'created_at', $updatedAt = 'updated_at', $withTimezone = false) |
||
562 | |||
563 | 15 | /** |
|
564 | 15 | * Alias that always sets $withTimezone to true |
|
565 | 15 | * |
|
566 | 15 | * @see addTimestamps |
|
567 | * |
||
568 | 15 | * @param string|null $createdAt Alternate name for the created_at column |
|
569 | 15 | * @param string|null $updatedAt Alternate name for the updated_at column |
|
570 | 15 | * |
|
571 | * @return $this |
||
572 | 15 | */ |
|
573 | public function addTimestampsWithTimezone($createdAt = null, $updatedAt = null) |
||
579 | |||
580 | /** |
||
581 | * Insert data into the table. |
||
582 | * |
||
583 | * @param array $data array of data in the form: |
||
584 | * array( |
||
585 | * array("col1" => "value1", "col2" => "anotherValue1"), |
||
586 | * array("col2" => "value2", "col2" => "anotherValue2"), |
||
587 | * ) |
||
588 | * or array("col1" => "value1", "col2" => "anotherValue1") |
||
589 | 17 | * |
|
590 | * @return $this |
||
591 | */ |
||
592 | 17 | public function insert($data) |
|
611 | 196 | ||
612 | 196 | /** |
|
613 | * Creates a table from the object instance. |
||
614 | * |
||
615 | * @return void |
||
616 | */ |
||
617 | public function create() |
||
623 | |||
624 | /** |
||
625 | * Updates a table from the object instance. |
||
626 | * |
||
627 | 46 | * @return void |
|
628 | 38 | */ |
|
629 | 46 | public function update() |
|
635 | 46 | ||
636 | 3 | /** |
|
637 | 46 | * Commit the pending data waiting for insertion. |
|
638 | * |
||
639 | 46 | * @return void |
|
640 | 46 | */ |
|
641 | 46 | public function saveData() |
|
669 | 1 | ||
670 | 1 | /** |
|
671 | 1 | * Immediately truncates the table. This operation cannot be undone |
|
672 | * |
||
673 | 12 | * @return void |
|
674 | */ |
||
675 | public function truncate() |
||
676 | { |
||
677 | $this->getAdapter()->truncateTable($this->getName()); |
||
678 | } |
||
679 | |||
680 | 2 | /** |
|
681 | * Commits the table changes. |
||
682 | 2 | * |
|
683 | 2 | * If the table doesn't exist it is created otherwise it is updated. |
|
684 | * |
||
685 | * @return void |
||
686 | */ |
||
687 | public function save() |
||
695 | 45 | ||
696 | 45 | /** |
|
697 | 195 | * Executes all the pending actions for this table |
|
698 | * |
||
699 | * @param bool $exists Whether or not the table existed prior to executing this method |
||
700 | 195 | * |
|
701 | 195 | * @return void |
|
702 | */ |
||
703 | protected function executeActions($exists) |
||
727 | } |
||
728 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.