Skip to content

Commit 1d9e286

Browse files
committed
added reflection for Table, Column, Index, ForeignKey
1 parent 57fa78c commit 1d9e286

9 files changed

Lines changed: 624 additions & 106 deletions

File tree

src/Database/Connection.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ public function getSupplementalDriver(): Driver
116116
}
117117

118118

119+
public function getReflection(): Reflection
120+
{
121+
return new Reflection($this->getDriver());
122+
}
123+
124+
119125
public function setRowNormalizer(?callable $normalizer): static
120126
{
121127
$this->rowNormalizer = $normalizer;

src/Database/Drivers/SqlsrvDriver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public function getForeignKeys(string $table): array
198198
fk.name AS name,
199199
cl.name AS local,
200200
tf.name AS [table],
201-
cf.name AS [column]
201+
cf.name AS [foreign]
202202
FROM
203203
sys.foreign_keys fk
204204
JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id

src/Database/Reflection.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\Database;
11+
12+
use Nette\Database\Reflection\Table;
13+
14+
15+
final class Reflection
16+
{
17+
/** @var array<string, Table> */
18+
public readonly array $tables;
19+
private ?string $schema;
20+
21+
22+
public function __construct(
23+
private readonly Driver $driver,
24+
) {
25+
$this->schema = $this->driver->isSupported(Driver::SUPPORT_SCHEMA) ? 'public' : null;
26+
unset($this->tables);
27+
}
28+
29+
30+
/** @return Table[] */
31+
public function getTables(): array
32+
{
33+
return array_values($this->tables);
34+
}
35+
36+
37+
public function getTable(string $name): Table
38+
{
39+
$name = $this->getFullName($name);
40+
return $this->tables[$name] ?? throw new \InvalidArgumentException("Table '$name' not found.");
41+
}
42+
43+
44+
public function hasTable(string $name): bool
45+
{
46+
$name = $this->getFullName($name);
47+
return isset($this->tables[$name]);
48+
}
49+
50+
51+
private function getFullName(string $name): string
52+
{
53+
return $this->schema !== null && !str_contains($name, '.')
54+
? $this->schema . '.' . $name
55+
: $name;
56+
}
57+
58+
59+
/** @internal */
60+
public function getDriver(): Driver
61+
{
62+
return $this->driver;
63+
}
64+
65+
66+
private function initTables(): void
67+
{
68+
$res = [];
69+
foreach ($this->driver->getTables() as $row) {
70+
$res[$row['fullName'] ?? $row['name']] = new Table($this, $row['name'], $row['view'], $row['fullName'] ?? null);
71+
}
72+
$this->tables = $res;
73+
}
74+
75+
76+
public function __get($name): mixed
77+
{
78+
match ($name) {
79+
'tables' => $this->initTables(),
80+
default => throw new \LogicException("Undefined property '$name'."),
81+
};
82+
return $this->$name;
83+
}
84+
}

src/Database/Reflection/Column.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\Database\Reflection;
11+
12+
13+
/**
14+
* Column reflection.
15+
*/
16+
final class Column
17+
{
18+
/** @internal */
19+
public function __construct(
20+
public readonly string $name,
21+
public readonly ?Table $table = null,
22+
public readonly string $nativeType = '',
23+
public readonly ?int $size = null,
24+
public readonly bool $nullable = false,
25+
public readonly mixed $default = null,
26+
public readonly bool $autoIncrement = false,
27+
public readonly bool $primary = false,
28+
public readonly array $vendor = [],
29+
) {
30+
}
31+
32+
33+
public function __toString(): string
34+
{
35+
return $this->name;
36+
}
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\Database\Reflection;
11+
12+
13+
/**
14+
* Foreign key reflection.
15+
*/
16+
final class ForeignKey
17+
{
18+
/** @internal */
19+
public function __construct(
20+
public readonly Table $foreignTable,
21+
/** @var Column[] */
22+
public readonly array $localColumns,
23+
/** @var Column[] */
24+
public readonly array $foreignColumns,
25+
public readonly ?string $name = null,
26+
) {
27+
}
28+
29+
30+
public function __toString(): string
31+
{
32+
return (string) $this->name;
33+
}
34+
}

src/Database/Reflection/Index.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\Database\Reflection;
11+
12+
13+
/**
14+
* Index reflection.
15+
*/
16+
final class Index
17+
{
18+
/** @internal */
19+
public function __construct(
20+
/** @var Column[] */
21+
public readonly array $columns,
22+
public readonly bool $unique = false,
23+
public readonly bool $primary = false,
24+
public readonly ?string $name = null,
25+
) {
26+
}
27+
28+
29+
public function __toString(): string
30+
{
31+
return (string) $this->name;
32+
}
33+
}

src/Database/Reflection/Table.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\Database\Reflection;
11+
12+
use Nette\Database\Reflection;
13+
14+
15+
/**
16+
* Table reflection.
17+
*/
18+
final class Table
19+
{
20+
/** @var array<string, Column> */
21+
public readonly array $columns;
22+
23+
/** @var list<Index> */
24+
public readonly array $indexes;
25+
public readonly ?Index $primaryKey;
26+
27+
/** @var list<ForeignKey> */
28+
public readonly array $foreignKeys;
29+
30+
31+
/** @internal */
32+
public function __construct(
33+
private readonly Reflection $reflection,
34+
public readonly string $name,
35+
public readonly bool $view = false,
36+
public readonly ?string $fullName = null,
37+
) {
38+
unset($this->columns, $this->indexes, $this->primaryKey, $this->foreignKeys);
39+
}
40+
41+
42+
public function getColumn(string $name): Column
43+
{
44+
return $this->columns[$name] ?? throw new \InvalidArgumentException("Column '$name' not found in table '$this->name'.");
45+
}
46+
47+
48+
private function initColumns(): void
49+
{
50+
$res = [];
51+
foreach ($this->reflection->getDriver()->getColumns($this->name) as $row) {
52+
$res[$row['name']] = new Column($row['name'], $this, $row['nativetype'], $row['size'], $row['nullable'], $row['default'], $row['autoincrement'], $row['primary'], $row['vendor']);
53+
}
54+
$this->columns = $res;
55+
}
56+
57+
58+
private function initIndexes(): void
59+
{
60+
$this->indexes = array_map(
61+
fn($row) => new Index(
62+
array_map(fn($name) => $this->getColumn($name), $row['columns']),
63+
$row['unique'],
64+
$row['primary'],
65+
is_string($row['name']) ? $row['name'] : null,
66+
),
67+
$this->reflection->getDriver()->getIndexes($this->name),
68+
);
69+
}
70+
71+
72+
private function initPrimaryKey(): void
73+
{
74+
$res = array_filter(
75+
$this->columns,
76+
fn($row) => $row->primary,
77+
);
78+
$this->primaryKey = $res ? new Index(array_values($res), true, true) : null;
79+
}
80+
81+
82+
private function initForeignKeys(): void
83+
{
84+
$tmp = [];
85+
foreach ($this->reflection->getDriver()->getForeignKeys($this->name) as $row) {
86+
$id = $row['name'];
87+
$foreignTable = $this->reflection->getTable($row['table']);
88+
$tmp[$id][0] = $foreignTable;
89+
$tmp[$id][1][] = $this->getColumn($row['local']);
90+
$tmp[$id][2][] = $foreignTable->getColumn($row['foreign']);
91+
$tmp[$id][3] = is_string($id) ? $id : null;
92+
}
93+
$this->foreignKeys = array_map(fn($row) => new ForeignKey(...$row), array_values($tmp));
94+
}
95+
96+
97+
public function __get($name): mixed
98+
{
99+
match ($name) {
100+
'columns' => $this->initColumns(),
101+
'indexes' => $this->initIndexes(),
102+
'primaryKey' => $this->initPrimaryKey(),
103+
'foreignKeys' => $this->initForeignKeys(),
104+
default => throw new \LogicException("Undefined property '$name'."),
105+
};
106+
return $this->$name;
107+
}
108+
109+
110+
public function __toString(): string
111+
{
112+
return $this->name;
113+
}
114+
}

0 commit comments

Comments
 (0)