Skip to content

Commit

Permalink
drivers: getForeignKeys() works with multi-column foreign keys
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 27, 2024
1 parent bbf6ccb commit ea3600d
Show file tree
Hide file tree
Showing 11 changed files with 39 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/Database/Drivers/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function getColumns(string $table): array;
/** @return list<array{name: string, columns: list<string>, unique: bool, primary: bool}> */
function getIndexes(string $table): array;

/** @return list<array{name: string, local: string, table: string, foreign: string}> */
/** @return list<array{name: string, local: list<string>, table: string, foreign: list<string>}> */
function getForeignKeys(string $table): array;

function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure;
Expand Down
8 changes: 4 additions & 4 deletions src/Database/Drivers/Engines/MSSQLEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,12 @@ public function getForeignKeys(string $table): array
tab1.name = ?
X, [$table_name]);

$id = 0;
while ($row = $rows->fetch()) {
$keys[$id]['name'] = $row['fk_name'];
$keys[$id]['local'] = $row['column'];
$id = $row['fk_name'];
$keys[$id]['name'] = $id;
$keys[$id]['local'][] = $row['column'];
$keys[$id]['table'] = $table_schema . '.' . $row['referenced_table'];
$keys[$id++]['foreign'] = $row['referenced_column'];
$keys[$id]['foreign'][] = $row['referenced_column'];
}

return array_values($keys);
Expand Down
8 changes: 4 additions & 4 deletions src/Database/Drivers/Engines/MySQLEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ public function getForeignKeys(string $table): array
AND TABLE_NAME = ?
X, [$table]);

$id = 0;
while ($row = $rows->fetch()) {
$keys[$id]['name'] = $row['CONSTRAINT_NAME'];
$keys[$id]['local'] = $row['COLUMN_NAME'];
$id = $row['CONSTRAINT_NAME'];
$keys[$id]['name'] = $id;
$keys[$id]['local'][] = $row['COLUMN_NAME'];
$keys[$id]['table'] = $row['REFERENCED_TABLE_NAME'];
$keys[$id++]['foreign'] = $row['REFERENCED_COLUMN_NAME'];
$keys[$id]['foreign'][] = $row['REFERENCED_COLUMN_NAME'];
}

return array_values($keys);
Expand Down
9 changes: 7 additions & 2 deletions src/Database/Drivers/Engines/PostgreSQLEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,14 @@ public function getForeignKeys(string $table): array
X, [$this->delimiteFQN($table)]);

while ($row = $rows->fetch()) {
$keys[] = $row;
$id = $row['name'];
$keys[$id]['name'] = $id;
$keys[$id]['local'][] = $row['local'];
$keys[$id]['table'] = $row['table'];
$keys[$id]['foreign'][] = $row['foreign'];
}
return $keys;

return array_values($keys);
}


Expand Down
6 changes: 5 additions & 1 deletion src/Database/Drivers/Engines/SQLServerEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ public function getForeignKeys(string $table): array
X, [$table]);

while ($row = $rows->fetch()) {
$keys[$row['name']] = $row;
$id = $row['name'];
$keys[$id]['name'] = $id;
$keys[$id]['local'][] = $row['local'];
$keys[$id]['table'] = $row['table'];
$keys[$id]['foreign'][] = $row['foreign'];
}

return array_values($keys);
Expand Down
7 changes: 5 additions & 2 deletions src/Database/Drivers/Engines/SQLiteEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,12 @@ public function getForeignKeys(string $table): array
while ($row = $rows->fetch()) {
$id = $row['id'];
$keys[$id]['name'] = $id;
$keys[$id]['local'] = $row['from'];
$keys[$id]['local'][] = $row['from'];
$keys[$id]['table'] = $row['table'];
$keys[$id]['foreign'] = $row['to'];
$keys[$id]['foreign'][] = $row['to'];
if ($keys[$id]['foreign'][0] == null) {
$keys[$id]['foreign'] = [];
}
}

return array_values($keys);
Expand Down
4 changes: 2 additions & 2 deletions src/Database/Reflection/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ private function initForeignKeys(): void
$id = $row['name'];
$foreignTable = $this->reflection->getTable($row['table']);
$tmp[$id][0] = $foreignTable;
$tmp[$id][1][] = $this->getColumn($row['local']);
$tmp[$id][2][] = $foreignTable->getColumn($row['foreign']);
$tmp[$id][1] = array_map(fn($name) => $this->getColumn($name), $row['local']);
$tmp[$id][2] = array_map(fn($name) => $foreignTable->getColumn($name), $row['foreign']);
$tmp[$id][3] = is_string($id) ? $id : null;
}
$this->foreignKeys = array_map(fn($row) => new ForeignKey(...$row), array_values($tmp));
Expand Down
12 changes: 3 additions & 9 deletions src/Database/Structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,17 +235,11 @@ protected function analyzeForeignKeys(array &$structure, string $table): void

$foreignKeys = $this->connection->getDatabaseEngine()->getForeignKeys($table);

$fksColumnsCounts = [];
foreach ($foreignKeys as $foreignKey) {
$tmp = &$fksColumnsCounts[$foreignKey['name']];
$tmp++;
}

usort($foreignKeys, fn($a, $b): int => $fksColumnsCounts[$b['name']] <=> $fksColumnsCounts[$a['name']]);
usort($foreignKeys, fn($a, $b): int => count($b['local']) <=> count($a['local']));

foreach ($foreignKeys as $row) {
$structure['belongsTo'][$lowerTable][$row['local']] = $row['table'];
$structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'];
$structure['belongsTo'][$lowerTable][$row['local'][0]] = $row['table'];
$structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'][0];
}

if (isset($structure['belongsTo'][$lowerTable])) {
Expand Down
4 changes: 2 additions & 2 deletions tests/Database/Engine.postgre.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ test('Tables in schema', function () use ($connection) {
$foreign = $engine->getForeignKeys('one.slave');
Assert::same([
'name' => 'one_slave_fk',
'local' => 'one_id',
'local' => ['one_id'],
'table' => 'one.master',
'foreign' => 'one_id',
'foreign' => ['one_id'],
], (array) $foreign[0]);


Expand Down
8 changes: 4 additions & 4 deletions tests/Database/Structure.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ class StructureTestCase extends TestCase
$this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine);
$this->engine->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]);
$this->engine->shouldReceive('getForeignKeys')->with('Books')->once()->andReturn([
['local' => 'author_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk1'],
['local' => 'translator_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk2'],
['local' => ['author_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk1'],
['local' => ['translator_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk2'],
]);
$this->engine->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]);
$this->engine->shouldReceive('getForeignKeys')->with('books_x_tags')->once()->andReturn([
['local' => 'book_id', 'table' => 'Books', 'foreign' => 'id', 'name' => 'books_x_tags_fk1'],
['local' => 'tag_id', 'table' => 'tags', 'foreign' => 'id', 'name' => 'books_x_tags_fk2'],
['local' => ['book_id'], 'table' => 'Books', 'foreign' => ['id'], 'name' => 'books_x_tags_fk1'],
['local' => ['tag_id'], 'table' => 'tags', 'foreign' => ['id'], 'name' => 'books_x_tags_fk2'],
]);

$this->structure = new StructureMock($this->connection, $this->storage);
Expand Down
4 changes: 2 additions & 2 deletions tests/Database/Structure.schemas.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class StructureSchemasTestCase extends TestCase
$this->connection->shouldReceive('getDatabaseEngine')->times(2)->andReturn($this->engine);
$this->engine->shouldReceive('getForeignKeys')->with('authors.authors')->once()->andReturn([]);
$this->engine->shouldReceive('getForeignKeys')->with('books.books')->once()->andReturn([
['local' => 'author_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk1'],
['local' => 'translator_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk2'],
['local' => ['author_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk1'],
['local' => ['translator_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk2'],
]);

$this->structure = new StructureMock($this->connection, $this->storage);
Expand Down

0 comments on commit ea3600d

Please sign in to comment.