Skip to content

Commit 966a70f

Browse files
committed
Merge pull request #5 from moufmouf/1.0
Adding a parameter to ignore junction tables pointed by foreign keys
2 parents 86b05db + 472fec6 commit 966a70f

File tree

3 files changed

+85
-11
lines changed

3 files changed

+85
-11
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ A **junction table** is a table:
4747
- that has **exactly 2 foreign keys**
4848
- that has **only 2 columns** (or **3 columns** if the one of those is an *autoincremented primary key*).
4949

50+
There is an optional parameter you can use with `detectJunctionTables` that will automatically ignore any junction
51+
table that is referenced by a foreign key of another table.
52+
53+
```php
54+
// Get all junction tables except the ones that are references by a foreign key.
55+
$tables = $schemaAnalyzer->detectJunctionTables(true);
56+
```
57+
58+
5059
## Detecting inheritance relationship between tables
5160

5261
### About inheritance relationships

src/SchemaAnalyzer.php

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,21 @@ public function __construct(AbstractSchemaManager $schemaManager, Cache $cache =
8989
* - it has exactly 2 foreign keys
9090
* - it has only 2 columns (or 3 columns if the third one is an autoincremented primary key).
9191
*
92+
* If $ignoreReferencedTables is true, junctions table that are pointed to by a foreign key of another
93+
* table are ignored.
94+
*
95+
* @param bool $ignoreReferencedTables
9296
*
9397
* @return Table[]
9498
*/
95-
public function detectJunctionTables()
99+
public function detectJunctionTables($ignoreReferencedTables = false)
96100
{
97-
$junctionTablesKey = $this->cachePrefix.'_junctiontables';
101+
$junctionTablesKey = $this->cachePrefix.'_junctiontables_'.($ignoreReferencedTables ? 'true' : 'false');
98102
$junctionTables = $this->cache->fetch($junctionTablesKey);
99103
if ($junctionTables === false) {
100-
$junctionTables = array_filter($this->getSchema()->getTables(), [$this, 'isJunctionTable']);
104+
$junctionTables = array_filter($this->getSchema()->getTables(), function (Table $table) use ($ignoreReferencedTables) {
105+
return $this->isJunctionTable($table, $ignoreReferencedTables);
106+
});
101107
$this->cache->save($junctionTablesKey, $junctionTables);
102108
}
103109

@@ -111,14 +117,18 @@ public function detectJunctionTables()
111117
* - it must have exactly 2 foreign keys
112118
* - it must have only 2 columns (or 3 columns if the third one is an autoincremented primary key).
113119
*
120+
* If $ignoreReferencedTables is true, junctions table that are pointed to by a foreign key of another
121+
* table are ignored.
122+
*
114123
* @param Table $table
124+
* @param bool $ignoreReferencedTables
115125
*
116126
* @return bool
117127
*/
118-
private function isJunctionTable(Table $table)
128+
private function isJunctionTable(Table $table, $ignoreReferencedTables = false)
119129
{
120130
$foreignKeys = $table->getForeignKeys();
121-
if (count($foreignKeys) != 2) {
131+
if (count($foreignKeys) !== 2) {
122132
return false;
123133
}
124134

@@ -133,24 +143,24 @@ private function isJunctionTable(Table $table)
133143
$pkColumns = [];
134144
}
135145

136-
if (count($pkColumns) == 1 && count($columns) == 2) {
146+
if (count($pkColumns) === 1 && count($columns) === 2) {
137147
return false;
138148
}
139149

140-
if (count($pkColumns) != 1 && count($columns) == 3) {
150+
if (count($pkColumns) !== 1 && count($columns) === 3) {
141151
return false;
142152
}
143153

144154
$fkColumnNames = [];
145155
foreach ($foreignKeys as $foreignKey) {
146156
$fkColumns = $foreignKey->getColumns();
147-
if (count($fkColumns) != 1) {
157+
if (count($fkColumns) !== 1) {
148158
return false;
149159
}
150160
$fkColumnNames[$fkColumns[0]] = true;
151161
}
152162

153-
if (count($columns) == 3) {
163+
if (count($columns) === 3) {
154164
// Let's check that the third column (the ID is NOT a foreign key)
155165
if (isset($fkColumnNames[$pkColumns[0]])) {
156166
return false;
@@ -162,9 +172,34 @@ private function isJunctionTable(Table $table)
162172
}
163173
}
164174

175+
if ($ignoreReferencedTables && $this->isTableReferenced($table)) {
176+
return false;
177+
}
178+
165179
return true;
166180
}
167181

182+
/**
183+
* Returns true if the table $table is referenced by another table.
184+
*
185+
* @param Table $table
186+
*
187+
* @return bool
188+
*/
189+
private function isTableReferenced(Table $table)
190+
{
191+
$tableName = $table->getName();
192+
foreach ($this->getSchema()->getTables() as $tableIter) {
193+
foreach ($tableIter->getForeignKeys() as $fk) {
194+
if ($fk->getForeignTableName() === $tableName) {
195+
return true;
196+
}
197+
}
198+
}
199+
200+
return false;
201+
}
202+
168203
/**
169204
* Get the shortest path between 2 tables.
170205
*
@@ -488,7 +523,7 @@ private function getParentRelationshipWithoutCache($tableName)
488523
}
489524
}
490525

491-
return null;
526+
return;
492527
}
493528

494529
/**

tests/SchemaAnalyzerTest.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,36 @@ public function testJointureTableDetectionWith3ColumnsWithPkIsFk()
206206
$junctionTables = $schemaAnalyzer->detectJunctionTables();
207207

208208
$this->assertCount(0, $junctionTables);
209+
210+
$junctionTables = $schemaAnalyzer->detectJunctionTables(true);
211+
$this->assertCount(0, $junctionTables);
212+
}
213+
214+
public function testJointureTableDetectionWithForeignKeyPointingOnJointureTable()
215+
{
216+
$schema = $this->getBaseSchema();
217+
218+
$role_right = $schema->createTable('role_right');
219+
$role_right->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true));
220+
$role_right->addColumn('role_id', 'integer', array('unsigned' => true));
221+
$role_right->addColumn('right_id', 'integer', array('unsigned' => true));
222+
223+
$role_right->addForeignKeyConstraint($schema->getTable('role'), array('role_id'), array('id'), array('onUpdate' => 'CASCADE'));
224+
$role_right->addForeignKeyConstraint($schema->getTable('right'), array('right_id'), array('id'), array('onUpdate' => 'CASCADE'));
225+
$role_right->setPrimaryKey(['id']);
226+
227+
$other_table = $schema->createTable('other_table');
228+
$other_table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true));
229+
$other_table->addColumn('role_right_id', 'integer', array('unsigned' => true));
230+
$other_table->addForeignKeyConstraint($schema->getTable('role_right'), array('role_right_id'), array('id'), array('onUpdate' => 'CASCADE'));
231+
$other_table->setPrimaryKey(['id']);
232+
233+
$schemaAnalyzer = new SchemaAnalyzer(new StubSchemaManager($schema));
234+
$junctionTables = $schemaAnalyzer->detectJunctionTables();
235+
$this->assertCount(1, $junctionTables);
236+
237+
$junctionTables = $schemaAnalyzer->detectJunctionTables(true);
238+
$this->assertCount(0, $junctionTables);
209239
}
210240

211241
public function testShortestPathInJointure()
@@ -283,7 +313,7 @@ public function testCache()
283313
$schemaAnalyzer->detectJunctionTables();
284314

285315
$this->assertNotFalse($cache->fetch('mykey_schema'));
286-
$this->assertNotFalse($cache->fetch('mykey_junctiontables'));
316+
$this->assertNotFalse($cache->fetch('mykey_junctiontables_false'));
287317
$r1 = $schemaAnalyzer->getShortestPath('role_right', 'role');
288318
$r2 = $schemaAnalyzer->getShortestPath('role_right', 'role');
289319
$this->assertTrue($r1 === $r2);

0 commit comments

Comments
 (0)