1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace TheCodingMachine\TDBM; |
||||
6 | |||||
7 | use Doctrine\Common\Cache\Cache; |
||||
8 | use Doctrine\DBAL\Connection; |
||||
9 | use Doctrine\DBAL\Schema\ForeignKeyConstraint; |
||||
10 | use Doctrine\DBAL\Schema\Schema; |
||||
11 | use Doctrine\DBAL\Schema\Table; |
||||
12 | use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer; |
||||
13 | |||||
14 | use function hash; |
||||
15 | use function serialize; |
||||
16 | |||||
17 | /** |
||||
18 | * This class is used to analyze the schema and return valuable information / hints. |
||||
19 | */ |
||||
20 | class TDBMSchemaAnalyzer |
||||
21 | { |
||||
22 | private $connection; |
||||
23 | |||||
24 | /** |
||||
25 | * @var Schema |
||||
26 | */ |
||||
27 | private $schema; |
||||
28 | |||||
29 | /** |
||||
30 | * @var string |
||||
31 | */ |
||||
32 | private $cachePrefix; |
||||
33 | |||||
34 | /** |
||||
35 | * @var Cache |
||||
36 | */ |
||||
37 | private $cache; |
||||
38 | |||||
39 | /** |
||||
40 | * @var SchemaAnalyzer |
||||
41 | */ |
||||
42 | private $schemaAnalyzer; |
||||
43 | |||||
44 | /** |
||||
45 | * @var SchemaLockFileDumper |
||||
46 | */ |
||||
47 | private $schemaLockFileDumper; |
||||
48 | |||||
49 | /** |
||||
50 | * @param Connection $connection The DBAL DB connection to use |
||||
51 | * @param Cache $cache A cache service to be used |
||||
52 | * @param SchemaAnalyzer $schemaAnalyzer The schema analyzer that will be used to find shortest paths... |
||||
53 | * Will be automatically created if not passed |
||||
54 | */ |
||||
55 | public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer $schemaAnalyzer, SchemaLockFileDumper $schemaLockFileDumper) |
||||
56 | { |
||||
57 | $this->connection = $connection; |
||||
58 | $this->cache = $cache; |
||||
59 | $this->schemaAnalyzer = $schemaAnalyzer; |
||||
60 | $this->schemaLockFileDumper = $schemaLockFileDumper; |
||||
61 | } |
||||
62 | |||||
63 | /** |
||||
64 | * Returns a unique ID for the current connection. Useful for namespacing cache entries in the current connection. |
||||
65 | * |
||||
66 | * @return string |
||||
67 | */ |
||||
68 | public function getCachePrefix(): string |
||||
69 | { |
||||
70 | return $this->cachePrefix ??= hash('md4', serialize($this->connection->getParams())); |
||||
71 | } |
||||
72 | |||||
73 | /** |
||||
74 | * @deprecated |
||||
75 | */ |
||||
76 | public function getLockFilePath(): string |
||||
77 | { |
||||
78 | return $this->schemaLockFileDumper->getLockFilePath(); |
||||
79 | } |
||||
80 | |||||
81 | /** |
||||
82 | * @deprecated |
||||
83 | */ |
||||
84 | public function getSchema(bool $ignoreCache = false): Schema |
||||
85 | { |
||||
86 | return $this->schemaLockFileDumper->getSchema($ignoreCache); |
||||
87 | } |
||||
88 | |||||
89 | /** |
||||
90 | * @deprecated |
||||
91 | */ |
||||
92 | public function generateLockFile(): void |
||||
93 | { |
||||
94 | $this->schemaLockFileDumper->generateLockFile(); |
||||
95 | \chmod($this->getLockFilePath(), 0664); |
||||
0 ignored issues
–
show
Deprecated Code
introduced
by
![]() |
|||||
96 | } |
||||
97 | |||||
98 | /** |
||||
99 | * Returns the list of pivot tables linked to table $tableName. |
||||
100 | * |
||||
101 | * @param string $tableName |
||||
102 | * |
||||
103 | * @return string[] |
||||
104 | */ |
||||
105 | public function getPivotTableLinkedToTable(string $tableName): array |
||||
106 | { |
||||
107 | $cacheKey = $this->getCachePrefix().'_pivottables_link_'.$tableName; |
||||
108 | if ($this->cache->contains($cacheKey)) { |
||||
109 | return $this->cache->fetch($cacheKey); |
||||
110 | } |
||||
111 | |||||
112 | $pivotTables = []; |
||||
113 | |||||
114 | $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true); |
||||
115 | foreach ($junctionTables as $table) { |
||||
116 | $fks = $table->getForeignKeys(); |
||||
117 | foreach ($fks as $fk) { |
||||
118 | if ($fk->getForeignTableName() == $tableName) { |
||||
119 | $pivotTables[] = $table->getName(); |
||||
120 | break; |
||||
121 | } |
||||
122 | } |
||||
123 | } |
||||
124 | |||||
125 | $this->cache->save($cacheKey, $pivotTables); |
||||
126 | |||||
127 | return $pivotTables; |
||||
128 | } |
||||
129 | |||||
130 | /** |
||||
131 | * Returns the list of foreign keys pointing to the table represented by this bean, excluding foreign keys |
||||
132 | * from junction tables and from inheritance. |
||||
133 | * It will also suppress doubles if 2 foreign keys are using the same columns. |
||||
134 | * |
||||
135 | * @return ForeignKeyConstraint[] |
||||
136 | */ |
||||
137 | public function getIncomingForeignKeys(string $tableName): array |
||||
138 | { |
||||
139 | $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true); |
||||
140 | $junctionTableNames = array_map(function (Table $table) { |
||||
141 | return $table->getName(); |
||||
142 | }, $junctionTables); |
||||
143 | $childrenRelationships = $this->schemaAnalyzer->getChildrenRelationships($tableName); |
||||
144 | |||||
145 | $fks = []; |
||||
146 | foreach ($this->schemaLockFileDumper->getSchema()->getTables() as $table) { |
||||
147 | $uniqueForeignKeys = $this->removeDuplicates($table->getForeignKeys()); |
||||
148 | foreach ($uniqueForeignKeys as $fk) { |
||||
149 | if ($fk->getForeignTableName() === $tableName) { |
||||
150 | if (in_array($fk->getLocalTableName(), $junctionTableNames)) { |
||||
151 | continue; |
||||
152 | } |
||||
153 | foreach ($childrenRelationships as $childFk) { |
||||
154 | if ($fk->getLocalTableName() === $childFk->getLocalTableName() && $fk->getUnquotedLocalColumns() === $childFk->getUnquotedLocalColumns()) { |
||||
0 ignored issues
–
show
The function
Doctrine\DBAL\Schema\For...nt::getLocalTableName() has been deprecated: Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
155 | continue 2; |
||||
156 | } |
||||
157 | } |
||||
158 | $fks[] = $fk; |
||||
159 | } |
||||
160 | } |
||||
161 | } |
||||
162 | |||||
163 | return $fks; |
||||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * Remove duplicate foreign keys (assumes that all foreign yes are from the same local table) |
||||
168 | * |
||||
169 | * @param ForeignKeyConstraint[] $foreignKeys |
||||
170 | * @return ForeignKeyConstraint[] |
||||
171 | */ |
||||
172 | private function removeDuplicates(array $foreignKeys): array |
||||
173 | { |
||||
174 | $fks = []; |
||||
175 | foreach ($foreignKeys as $foreignKey) { |
||||
176 | $key = implode('__`__', $foreignKey->getUnquotedLocalColumns()); |
||||
177 | if (!isset($fks[$key])) { |
||||
178 | $fks[$key] = $foreignKey; |
||||
179 | } |
||||
180 | } |
||||
181 | |||||
182 | return array_values($fks); |
||||
183 | } |
||||
184 | } |
||||
185 |