Complex classes like SchemaAnalyzer 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 SchemaAnalyzer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class SchemaAnalyzer |
||
24 | { |
||
25 | private static $WEIGHT_FK = 1; |
||
26 | private static $WEIGHT_INHERITANCE_FK = 0.1; |
||
27 | private static $WEIGHT_JOINTURE_TABLE = 1.5; |
||
28 | |||
29 | const WEIGHT_IMPORTANT = 0.75; |
||
30 | const WEIGHT_IRRELEVANT = 2; |
||
31 | const WEIGHT_IGNORE = INF; |
||
32 | |||
33 | /** |
||
34 | * @var AbstractSchemaManager |
||
35 | */ |
||
36 | private $schemaManager; |
||
37 | |||
38 | /** |
||
39 | * @var Schema |
||
40 | */ |
||
41 | private $schema; |
||
42 | |||
43 | /** |
||
44 | * @var Cache |
||
45 | */ |
||
46 | private $cache; |
||
47 | |||
48 | /** |
||
49 | * @var string |
||
50 | */ |
||
51 | private $cachePrefix; |
||
52 | |||
53 | /** |
||
54 | * Nested arrays containing table => column => cost. |
||
55 | * |
||
56 | * @var float[][] |
||
57 | */ |
||
58 | private $alteredCosts = []; |
||
59 | |||
60 | /** |
||
61 | * Array containing table cost. |
||
62 | * |
||
63 | * @var float[] |
||
64 | */ |
||
65 | private $alteredTableCosts = []; |
||
66 | |||
67 | /** |
||
68 | * @param AbstractSchemaManager $schemaManager |
||
69 | * @param Cache|null $cache The Doctrine cache service to use to cache results (optional) |
||
70 | * @param string|null $schemaCacheKey The unique identifier for the schema manager. Compulsory if cache is set. |
||
71 | */ |
||
72 | public function __construct(AbstractSchemaManager $schemaManager, Cache $cache = null, $schemaCacheKey = null) |
||
85 | |||
86 | /** |
||
87 | * Detect all junctions tables in the schema. |
||
88 | * A table is a junction table if:. |
||
89 | * |
||
90 | * - it has exactly 2 foreign keys |
||
91 | * - it has only 2 columns (or 3 columns if the third one is an autoincremented primary key). |
||
92 | * |
||
93 | * If $ignoreReferencedTables is true, junctions table that are pointed to by a foreign key of another |
||
94 | * table are ignored. |
||
95 | * |
||
96 | * @param bool $ignoreReferencedTables |
||
97 | * |
||
98 | * @return Table[] |
||
99 | */ |
||
100 | public function detectJunctionTables($ignoreReferencedTables = false) |
||
113 | |||
114 | /** |
||
115 | * Returns true if $table is a junction table. |
||
116 | * I.e:. |
||
117 | * |
||
118 | * - it must have exactly 2 foreign keys |
||
119 | * - it must have only 2 columns (or 3 columns if the third one is an autoincremented primary key). |
||
120 | * |
||
121 | * If $ignoreReferencedTables is true, junctions table that are pointed to by a foreign key of another |
||
122 | * table are ignored. |
||
123 | * |
||
124 | * @param Table $table |
||
125 | * @param bool $ignoreReferencedTables |
||
126 | * |
||
127 | * @return bool |
||
128 | */ |
||
129 | public function isJunctionTable(Table $table, $ignoreReferencedTables = false) |
||
183 | |||
184 | /** |
||
185 | * Returns true if the table $table is referenced by another table. |
||
186 | * |
||
187 | * @param Table $table |
||
188 | * |
||
189 | * @return bool |
||
190 | */ |
||
191 | private function isTableReferenced(Table $table) |
||
204 | |||
205 | /** |
||
206 | * Get the shortest path between 2 tables. |
||
207 | * |
||
208 | * @param string $fromTable |
||
209 | * @param string $toTable |
||
210 | * |
||
211 | * @return \Doctrine\DBAL\Schema\ForeignKeyConstraint[] |
||
212 | * |
||
213 | * @throws SchemaAnalyzerException |
||
214 | */ |
||
215 | public function getShortestPath($fromTable, $toTable) |
||
221 | |||
222 | /** |
||
223 | * Get the shortest path between 2 tables. |
||
224 | * |
||
225 | * @param string $fromTable |
||
226 | * @param string $toTable |
||
227 | * |
||
228 | * @return \Doctrine\DBAL\Schema\ForeignKeyConstraint[] |
||
229 | * |
||
230 | * @throws SchemaAnalyzerException |
||
231 | */ |
||
232 | private function getShortestPathWithoutCache($fromTable, $toTable) |
||
287 | |||
288 | private function checkTableExists($tableName) |
||
296 | |||
297 | private function buildSchemaGraph() |
||
346 | |||
347 | /** |
||
348 | * Remove duplicate foreign keys (assumes that all foreign yes are from the same local table). |
||
349 | * |
||
350 | * @param ForeignKeyConstraint[] $foreignKeys |
||
351 | * @return ForeignKeyConstraint[] |
||
352 | */ |
||
353 | private function removeDuplicates(array $foreignKeys) |
||
362 | |||
363 | /** |
||
364 | * Returns the schema (from the schema manager or the cache if needed). |
||
365 | * |
||
366 | * @return Schema |
||
367 | */ |
||
368 | private function getSchema() |
||
381 | |||
382 | /** |
||
383 | * Returns the full exception message when an ambiguity arises. |
||
384 | * |
||
385 | * @param Base[][] $paths |
||
386 | * @param Vertex $startVertex |
||
387 | */ |
||
388 | private function getAmbiguityExceptionMessage(array $paths, Vertex $startVertex, Vertex $endVertex) |
||
404 | |||
405 | /** |
||
406 | * Returns the textual representation of the path. |
||
407 | * |
||
408 | * @param Base[] $path |
||
409 | * @param Vertex $startVertex |
||
410 | */ |
||
411 | private function getTextualPath(array $path, Vertex $startVertex) |
||
455 | |||
456 | /** |
||
457 | * Sets the cost of a foreign key. |
||
458 | * |
||
459 | * @param string $tableName |
||
460 | * @param string $columnName |
||
461 | * @param float $cost |
||
462 | * |
||
463 | * @return $this |
||
464 | */ |
||
465 | public function setForeignKeyCost($tableName, $columnName, $cost) |
||
469 | |||
470 | /** |
||
471 | * Sets the cost modifier of a table. |
||
472 | * |
||
473 | * @param string $tableName |
||
474 | * @param float $cost |
||
475 | * |
||
476 | * @return $this |
||
477 | */ |
||
478 | public function setTableCostModifier($tableName, $cost) |
||
482 | |||
483 | /** |
||
484 | * Sets the cost modifier of all tables at once. |
||
485 | * |
||
486 | * @param array<string, float> $tableCosts The key is the table name, the value is the cost modifier. |
||
487 | */ |
||
488 | public function setTableCostModifiers(array $tableCosts) |
||
492 | |||
493 | /** |
||
494 | * Sets the cost of all foreign keys at once. |
||
495 | * |
||
496 | * @param array<string, array<string, float>> $fkCosts First key is the table name, second key is the column name, the value is the cost. |
||
497 | */ |
||
498 | public function setForeignKeyCosts(array $fkCosts) |
||
502 | |||
503 | /** |
||
504 | * Returns true if this foreign key represents an inheritance relationship, |
||
505 | * i.e. if this foreign key is based on a primary key. |
||
506 | * |
||
507 | * @param ForeignKeyConstraint $fk |
||
508 | * |
||
509 | * @return true |
||
510 | */ |
||
511 | private function isInheritanceRelationship(ForeignKeyConstraint $fk) |
||
524 | |||
525 | /** |
||
526 | * If this table is pointing to a parent table (if its primary key is a foreign key pointing on another table), |
||
527 | * this function will return the pointed table. |
||
528 | * This function will return null if there is no parent table. |
||
529 | * |
||
530 | * @param string $tableName |
||
531 | * |
||
532 | * @return ForeignKeyConstraint|null |
||
533 | */ |
||
534 | public function getParentRelationship($tableName) |
||
540 | |||
541 | /** |
||
542 | * If this table is pointing to a parent table (if its primary key is a foreign key pointing on another table), |
||
543 | * this function will return the pointed table. |
||
544 | * This function will return null if there is no parent table. |
||
545 | * |
||
546 | * @param string $tableName |
||
547 | * |
||
548 | * @return ForeignKeyConstraint|null |
||
549 | */ |
||
550 | private function getParentRelationshipWithoutCache($tableName) |
||
561 | |||
562 | /** |
||
563 | * If this table is pointed by children tables (if other child tables have a primary key that is also a |
||
564 | * foreign key to this table), this function will return the list of child tables. |
||
565 | * This function will return an empty array if there are no children tables. |
||
566 | * |
||
567 | * @param string $tableName |
||
568 | * |
||
569 | * @return ForeignKeyConstraint[] |
||
570 | */ |
||
571 | public function getChildrenRelationships($tableName) |
||
577 | |||
578 | /** |
||
579 | * If this table is pointed by children tables (if other child tables have a primary key that is also a |
||
580 | * foreign key to this table), this function will return the list of child tables. |
||
581 | * This function will return an empty array if there are no children tables. |
||
582 | * |
||
583 | * @param string $tableName |
||
584 | * |
||
585 | * @return ForeignKeyConstraint[] |
||
586 | */ |
||
587 | private function getChildrenRelationshipsWithoutCache($tableName) |
||
605 | |||
606 | /** |
||
607 | * Returns an item from cache or computes it using $closure and puts it in cache. |
||
608 | * |
||
609 | * @param string $key |
||
610 | * @param callable $closure |
||
611 | * |
||
612 | * @return mixed |
||
613 | */ |
||
614 | private function fromCache($key, callable $closure) |
||
624 | } |
||
625 |