Skip to content

Commit 860a033

Browse files
committed
drivers: getForeignKeys() works with multi-column foreign keys
1 parent 071d442 commit 860a033

File tree

9 files changed

+42
-30
lines changed

9 files changed

+42
-30
lines changed

src/Database/Drivers/MsSqlDriver.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,12 @@ public function getForeignKeys(string $table): array
182182
tab1.name = {$this->pdo->quote($table_name)}
183183
X;
184184

185-
foreach ($this->pdo->query($query) as $id => $row) {
186-
$keys[$id]['name'] = $row['fk_name'];
187-
$keys[$id]['local'] = $row['column'];
185+
foreach ($this->pdo->query($query) as $row) {
186+
$id = $row['fk_name'];
187+
$keys[$id]['name'] = $id;
188+
$keys[$id]['local'][] = $row['column'];
188189
$keys[$id]['table'] = $table_schema . '.' . $row['referenced_table'];
189-
$keys[$id]['foreign'] = $row['referenced_column'];
190+
$keys[$id]['foreign'][] = $row['referenced_column'];
190191
}
191192

192193
return array_values($keys);

src/Database/Drivers/MySqlDriver.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,12 @@ public function getForeignKeys(string $table): array
199199
WHERE TABLE_SCHEMA = DATABASE()
200200
AND REFERENCED_TABLE_NAME IS NOT NULL
201201
AND TABLE_NAME = {$this->pdo->quote($table)}
202-
X) as $id => $row) {
203-
$keys[$id]['name'] = $row['CONSTRAINT_NAME'];
204-
$keys[$id]['local'] = $row['COLUMN_NAME'];
202+
X) as $row) {
203+
$id = $row['CONSTRAINT_NAME'];
204+
$keys[$id]['name'] = $id;
205+
$keys[$id]['local'][] = $row['COLUMN_NAME'];
205206
$keys[$id]['table'] = $row['REFERENCED_TABLE_NAME'];
206-
$keys[$id]['foreign'] = $row['REFERENCED_COLUMN_NAME'];
207+
$keys[$id]['foreign'][] = $row['REFERENCED_COLUMN_NAME'];
207208
}
208209

209210
return array_values($keys);

src/Database/Drivers/PgSqlDriver.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ public function getIndexes(string $table): array
184184
public function getForeignKeys(string $table): array
185185
{
186186
/* Doesn't work with multi-column foreign keys */
187-
return $this->pdo->query(<<<X
187+
$keys = [];
188+
foreach ($this->pdo->query(<<<X
188189
SELECT
189190
co.conname::varchar AS name,
190191
al.attname::varchar AS local,
@@ -201,7 +202,15 @@ public function getForeignKeys(string $table): array
201202
co.contype = 'f'
202203
AND cl.oid = {$this->pdo->quote($this->delimiteFQN($table))}::regclass
203204
AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE))
204-
X)->fetchAll(\PDO::FETCH_ASSOC);
205+
X) as $row) {
206+
$id = $row['name'];
207+
$keys[$id]['name'] = $id;
208+
$keys[$id]['local'][] = $row['local'];
209+
$keys[$id]['table'] = $row['table'];
210+
$keys[$id]['foreign'][] = $row['foreign'];
211+
}
212+
213+
return array_values($keys);
205214
}
206215

207216

src/Database/Drivers/SqliteDriver.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,12 @@ public function getForeignKeys(string $table): array
221221
foreach ($this->pdo->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) {
222222
$id = $row['id'];
223223
$keys[$id]['name'] = $id;
224-
$keys[$id]['local'] = $row['from'];
224+
$keys[$id]['local'][] = $row['from'];
225225
$keys[$id]['table'] = $row['table'];
226-
$keys[$id]['foreign'] = $row['to'];
226+
$keys[$id]['foreign'][] = $row['to'];
227+
if ($keys[$id]['foreign'][0] == null) {
228+
$keys[$id]['foreign'] = [];
229+
}
227230
}
228231

229232
return array_values($keys);

src/Database/Drivers/SqlsrvDriver.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,11 @@ public function getForeignKeys(string $table): array
209209
WHERE
210210
tl.name = {$this->pdo->quote($table)}
211211
X, \PDO::FETCH_ASSOC) as $row) {
212-
$keys[$row['name']] = $row;
212+
$id = $row['name'];
213+
$keys[$id]['name'] = $id;
214+
$keys[$id]['local'][] = $row['local'];
215+
$keys[$id]['table'] = $row['table'];
216+
$keys[$id]['foreign'][] = $row['column'];
213217
}
214218

215219
return array_values($keys);

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->getDriver()->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/Reflection.postgre.phpt

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

7171

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('getDriver')->times(4)->andReturn($this->driver);
7575
$this->driver->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]);
7676
$this->driver->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->driver->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]);
8181
$this->driver->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);

tests/Database/Structure.schemas.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ class StructureSchemasTestCase extends TestCase
6060
$this->connection->shouldReceive('getDriver')->times(2)->andReturn($this->driver);
6161
$this->driver->shouldReceive('getForeignKeys')->with('authors.authors')->once()->andReturn([]);
6262
$this->driver->shouldReceive('getForeignKeys')->with('books.books')->once()->andReturn([
63-
['local' => 'author_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk1'],
64-
['local' => 'translator_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk2'],
63+
['local' => ['author_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk1'],
64+
['local' => ['translator_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk2'],
6565
]);
6666

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

0 commit comments

Comments
 (0)