Skip to content

Commit fcd9ab6

Browse files
committed
TableRowTypeResolver updated
1 parent 482780a commit fcd9ab6

4 files changed

Lines changed: 40 additions & 20 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Extensions for specific Nette packages use dedicated namespaces: `Nette\PHPStan\
103103

104104
### TableRowTypeResolver
105105

106-
`TableRowTypeResolver` is a shared service used by the three database extensions below. It resolves database table names to entity row class types using a configurable convention mask (e.g. `App\Entity\*Row` where `*` is replaced by PascalCase table name) and optional explicit table-to-class overrides. Checks class existence via `ReflectionProvider`. Config: `extension-nette.neon` parameters `nette.database.mapping.convention` and `nette.database.mapping.tables`.
106+
`TableRowTypeResolver` is a shared service used by the three database extensions below. It resolves database table names to entity row class types using a configurable `tables` map. Keys may contain a single `*` wildcard (e.g. `forum_*`), and a bare `*` acts as a catch-all fallback. Class names may contain `*` which is replaced with PascalCase of the captured portion (or the full table name for exact keys). Exact keys take precedence over wildcards; wildcard entries are tried in declaration order. Checks class existence via `ReflectionProvider`. Mirrors `Nette\Database\DefaultEntityMapping`. Config: `extension-nette.neon` parameter `nette.database.mapping.tables`.
107107

108108
### ExplorerTableReturnTypeExtension
109109

extension-nette.neon

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ parameters:
1111
mapping: []
1212
database:
1313
mapping:
14-
convention: ''
1514
tables: []
1615

1716

@@ -22,7 +21,6 @@ parametersSchema:
2221
])
2322
database: structure([
2423
mapping: structure([
25-
convention: string()
2624
tables: arrayOf(string(), string())
2725
])
2826
])
@@ -60,7 +58,6 @@ services:
6058
# nette/database
6159
nette.database.tableRowTypeResolver:
6260
create: Nette\PHPStan\Database\TableRowTypeResolver(
63-
convention: %nette.database.mapping.convention%
6461
tables: %nette.database.mapping.tables%
6562
)
6663
-

src/Database/TableRowTypeResolver.php

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88

99
/**
1010
* Resolves database table names to entity row class types.
11-
* Convention: table_name -> str_replace('*', PascalCase(table), mask).
12-
* Explicit overrides in $tables take precedence over convention.
11+
* Uses a table-to-class map with optional '*' wildcards in keys (e.g. 'forum_*').
12+
* Class names may contain '*' which is replaced with PascalCase of the captured portion
13+
* (or the full table name for exact keys). Exact keys take precedence; wildcard entries
14+
* are tried in declaration order.
1315
*/
1416
class TableRowTypeResolver
1517
{
1618
/**
17-
* @param string $convention mask like App\Entity\*Row, where * is replaced by PascalCase table name
18-
* @param array<string, string> $tables explicit table -> FQCN overrides
19+
* @param array<string, string> $tables table -> FQCN map; keys may contain a single '*' wildcard,
20+
* and a bare '*' acts as a catch-all fallback. Values may contain '*' which is replaced with
21+
* PascalCase of the captured portion.
1922
*/
2023
public function __construct(
2124
private ReflectionProvider $reflectionProvider,
22-
private string $convention = '',
2325
private array $tables = [],
2426
) {
2527
}
@@ -31,18 +33,11 @@ public function __construct(
3133
*/
3234
public function resolve(string $tableName): ?ObjectType
3335
{
34-
if (isset($this->tables[$tableName])) {
35-
$className = $this->tables[$tableName];
36-
return $this->reflectionProvider->hasClass($className)
37-
? new ObjectType($className)
38-
: null;
39-
}
40-
41-
if ($this->convention === '') {
36+
$className = $this->resolveClassName($tableName);
37+
if ($className === null) {
4238
return null;
4339
}
4440

45-
$className = str_replace('*', $this->snakeToPascalCase($tableName), $this->convention);
4641
return $this->reflectionProvider->hasClass($className)
4742
? new ObjectType($className)
4843
: null;
@@ -60,6 +55,34 @@ public function extractTableName(string $key): string
6055
}
6156

6257

58+
private function resolveClassName(string $tableName): ?string
59+
{
60+
if (isset($this->tables[$tableName])) {
61+
return $this->expandClass($this->tables[$tableName], $tableName);
62+
}
63+
64+
foreach ($this->tables as $pattern => $class) {
65+
if (!str_contains($pattern, '*')) {
66+
continue;
67+
}
68+
$regex = '#^' . str_replace('\*', '(.*)', preg_quote($pattern, '#')) . '$#D';
69+
if (preg_match($regex, $tableName, $m)) {
70+
return $this->expandClass($class, $m[1]);
71+
}
72+
}
73+
74+
return null;
75+
}
76+
77+
78+
private function expandClass(string $class, string $capture): string
79+
{
80+
return str_contains($class, '*')
81+
? str_replace('*', $this->snakeToPascalCase($capture), $class)
82+
: $class;
83+
}
84+
85+
6386
private function snakeToPascalCase(string $table): string
6487
{
6588
return str_replace(' ', '', ucwords(strtr($table, '_', ' ')));

tests/Database/database-row-mapping.neon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ parameters:
55
nette:
66
database:
77
mapping:
8-
convention: '*Row'
9-
tables:
8+
tables:
109
custom_table: CustomEntity
10+
*: *Row

0 commit comments

Comments
 (0)