Skip to content

Commit e1692fb

Browse files
committed
drivers: getForeignKeys() works with multi-column foreign keys
1 parent 6723b87 commit e1692fb

11 files changed

Lines changed: 39 additions & 33 deletions

File tree

src/Database/Drivers/Engine.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ function getColumns(string $table): array;
6666
/** @return list<array{name: string, columns: list<string>, unique: bool, primary: bool}> */
6767
function getIndexes(string $table): array;
6868

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

7272
function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure;

src/Database/Drivers/Engines/MSSQLEngine.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,12 @@ public function getForeignKeys(string $table): array
202202
tab1.name = ?
203203
X, [$table_name]);
204204

205-
$id = 0;
206205
while ($row = $rows->fetch()) {
207-
$keys[$id]['name'] = $row['fk_name'];
208-
$keys[$id]['local'] = $row['column'];
206+
$id = $row['fk_name'];
207+
$keys[$id]['name'] = $id;
208+
$keys[$id]['local'][] = $row['column'];
209209
$keys[$id]['table'] = $table_schema . '.' . $row['referenced_table'];
210-
$keys[$id++]['foreign'] = $row['referenced_column'];
210+
$keys[$id]['foreign'][] = $row['referenced_column'];
211211
}
212212

213213
return array_values($keys);

src/Database/Drivers/Engines/MySQLEngine.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,12 @@ public function getForeignKeys(string $table): array
152152
AND TABLE_NAME = ?
153153
X, [$table]);
154154

155-
$id = 0;
156155
while ($row = $rows->fetch()) {
157-
$keys[$id]['name'] = $row['CONSTRAINT_NAME'];
158-
$keys[$id]['local'] = $row['COLUMN_NAME'];
156+
$id = $row['CONSTRAINT_NAME'];
157+
$keys[$id]['name'] = $id;
158+
$keys[$id]['local'][] = $row['COLUMN_NAME'];
159159
$keys[$id]['table'] = $row['REFERENCED_TABLE_NAME'];
160-
$keys[$id++]['foreign'] = $row['REFERENCED_COLUMN_NAME'];
160+
$keys[$id]['foreign'][] = $row['REFERENCED_COLUMN_NAME'];
161161
}
162162

163163
return array_values($keys);

src/Database/Drivers/Engines/PostgreSQLEngine.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,14 @@ public function getForeignKeys(string $table): array
222222
X, [$this->delimiteFQN($table)]);
223223

224224
while ($row = $rows->fetch()) {
225-
$keys[] = $row;
225+
$id = $row['name'];
226+
$keys[$id]['name'] = $id;
227+
$keys[$id]['local'][] = $row['local'];
228+
$keys[$id]['table'] = $row['table'];
229+
$keys[$id]['foreign'][] = $row['foreign'];
226230
}
227-
return $keys;
231+
232+
return array_values($keys);
228233
}
229234

230235

src/Database/Drivers/Engines/SQLServerEngine.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,11 @@ public function getForeignKeys(string $table): array
211211
X, [$table]);
212212

213213
while ($row = $rows->fetch()) {
214-
$keys[$row['name']] = $row;
214+
$id = $row['name'];
215+
$keys[$id]['name'] = $id;
216+
$keys[$id]['local'][] = $row['local'];
217+
$keys[$id]['table'] = $row['table'];
218+
$keys[$id]['foreign'][] = $row['foreign'];
215219
}
216220

217221
return array_values($keys);

src/Database/Drivers/Engines/SQLiteEngine.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,12 @@ public function getForeignKeys(string $table): array
218218
while ($row = $rows->fetch()) {
219219
$id = $row['id'];
220220
$keys[$id]['name'] = $id;
221-
$keys[$id]['local'] = $row['from'];
221+
$keys[$id]['local'][] = $row['from'];
222222
$keys[$id]['table'] = $row['table'];
223-
$keys[$id]['foreign'] = $row['to'];
223+
$keys[$id]['foreign'][] = $row['to'];
224+
if ($keys[$id]['foreign'][0] == null) {
225+
$keys[$id]['foreign'] = [];
226+
}
224227
}
225228

226229
return array_values($keys);

src/Database/Reflection/Table.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ private function initForeignKeys(): void
8787
$id = $row['name'];
8888
$foreignTable = $this->reflection->getTable($row['table']);
8989
$tmp[$id][0] = $foreignTable;
90-
$tmp[$id][1][] = $this->getColumn($row['local']);
91-
$tmp[$id][2][] = $foreignTable->getColumn($row['foreign']);
90+
$tmp[$id][1] = array_map(fn($name) => $this->getColumn($name), $row['local']);
91+
$tmp[$id][2] = array_map(fn($name) => $foreignTable->getColumn($name), $row['foreign']);
9292
$tmp[$id][3] = is_string($id) ? $id : null;
9393
}
9494
$this->foreignKeys = array_map(fn($row) => new ForeignKey(...$row), array_values($tmp));

src/Database/Structure.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,11 @@ protected function analyzeForeignKeys(array &$structure, string $table): void
235235

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

238-
$fksColumnsCounts = [];
239-
foreach ($foreignKeys as $foreignKey) {
240-
$tmp = &$fksColumnsCounts[$foreignKey['name']];
241-
$tmp++;
242-
}
243-
244-
usort($foreignKeys, fn($a, $b): int => $fksColumnsCounts[$b['name']] <=> $fksColumnsCounts[$a['name']]);
238+
usort($foreignKeys, fn($a, $b): int => count($b['local']) <=> count($a['local']));
245239

246240
foreach ($foreignKeys as $row) {
247-
$structure['belongsTo'][$lowerTable][$row['local']] = $row['table'];
248-
$structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'];
241+
$structure['belongsTo'][$lowerTable][$row['local'][0]] = $row['table'];
242+
$structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'][0];
249243
}
250244

251245
if (isset($structure['belongsTo'][$lowerTable])) {

tests/Database/Engine.postgre.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ test('Tables in schema', function () use ($connection) {
6565
$foreign = $engine->getForeignKeys('one.slave');
6666
Assert::same([
6767
'name' => 'one_slave_fk',
68-
'local' => 'one_id',
68+
'local' => ['one_id'],
6969
'table' => 'one.master',
70-
'foreign' => 'one_id',
70+
'foreign' => ['one_id'],
7171
], (array) $foreign[0]);
7272

7373

tests/Database/Structure.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ class StructureTestCase extends TestCase
7474
$this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine);
7575
$this->engine->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]);
7676
$this->engine->shouldReceive('getForeignKeys')->with('Books')->once()->andReturn([
77-
['local' => 'author_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk1'],
78-
['local' => 'translator_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk2'],
77+
['local' => ['author_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk1'],
78+
['local' => ['translator_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk2'],
7979
]);
8080
$this->engine->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]);
8181
$this->engine->shouldReceive('getForeignKeys')->with('books_x_tags')->once()->andReturn([
82-
['local' => 'book_id', 'table' => 'Books', 'foreign' => 'id', 'name' => 'books_x_tags_fk1'],
83-
['local' => 'tag_id', 'table' => 'tags', 'foreign' => 'id', 'name' => 'books_x_tags_fk2'],
82+
['local' => ['book_id'], 'table' => 'Books', 'foreign' => ['id'], 'name' => 'books_x_tags_fk1'],
83+
['local' => ['tag_id'], 'table' => 'tags', 'foreign' => ['id'], 'name' => 'books_x_tags_fk2'],
8484
]);
8585

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

0 commit comments

Comments
 (0)