Skip to content

Commit 1db2468

Browse files
committed
Make AsDatabaseType name optional, defaulting to the service id
When no name is provided, the FQCN (service id) is used as the type name, allowing #[Column(type: MyType::class)] without declaring an explicit name. This also works for YAML/PHP service configuration: any service tagged with doctrine.dbal.type without a type attribute falls back to its service id.
1 parent a14d21d commit 1db2468

4 files changed

Lines changed: 59 additions & 24 deletions

File tree

docs/en/custom-dbal-types.rst

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,14 @@ connection:
6060
use Doctrine\DBAL\Platforms\AbstractPlatform;
6161
use Doctrine\DBAL\Types\Type;
6262
63-
#[AsDatabaseType(name: 'money')]
63+
#[AsDatabaseType]
6464
final class MoneyType extends Type
6565
{
6666
// ...
6767
}
6868
69-
You can now use the type by name in your entity mappings:
69+
When no ``name`` is given, the fully-qualified class name is used as the type
70+
name. You can then reference the type with ``::class`` in your entity mappings:
7071

7172
.. code-block:: php
7273
@@ -86,28 +87,18 @@ You can now use the type by name in your entity mappings:
8687
#[ORM\Column]
8788
private int $id;
8889
89-
#[ORM\Column(type: MoneyType::NAME)]
90+
#[ORM\Column(type: MoneyType::class)]
9091
private Money $price;
9192
}
9293
93-
It is recommended to define the type name as a constant on the type class to
94-
avoid duplicating the string:
94+
An explicit name can still be provided when the type needs to be referenced by
95+
a short string (e.g. from PHP or YAML mappings):
9596

9697
.. code-block:: php
9798
98-
<?php
99-
100-
namespace App\Doctrine\Type;
101-
102-
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDatabaseType;
103-
use Doctrine\DBAL\Platforms\AbstractPlatform;
104-
use Doctrine\DBAL\Types\Type;
105-
106-
#[AsDatabaseType(name: MoneyType::NAME)]
99+
#[AsDatabaseType(name: 'money')]
107100
final class MoneyType extends Type
108101
{
109-
public const string NAME = 'money';
110-
111102
// ...
112103
}
113104
@@ -127,8 +118,8 @@ subset of connections by repeating the attribute with a ``connection`` argument:
127118
use Doctrine\DBAL\Platforms\AbstractPlatform;
128119
use Doctrine\DBAL\Types\Type;
129120
130-
#[AsDatabaseType(name: 'money', connection: 'default')]
131-
#[AsDatabaseType(name: 'money', connection: 'reporting')]
121+
#[AsDatabaseType(connection: 'default')]
122+
#[AsDatabaseType(connection: 'reporting')]
132123
final class MoneyType extends Type
133124
{
134125
// ...

src/Attribute/AsDatabaseType.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,24 @@
66

77
use Attribute;
88

9+
/**
10+
* Registers a DBAL type as a service, with full dependency injection support.
11+
*
12+
* The type is registered in the TypeRegistry of the specified connection(s),
13+
* replacing the global static type registry for that connection.
14+
*/
915
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
1016
class AsDatabaseType
1117
{
18+
/**
19+
* @param string|null $name The DBAL type name used in column mappings (e.g. in #[Column(type: ...)]).
20+
* Defaults to the fully-qualified class name of the type when omitted,
21+
* so that #[Column(type: MyType::class)] works without declaring a name.
22+
* @param string|null $connection Restrict the type to a specific named connection.
23+
* When null (default), the type is registered for all connections.
24+
*/
1225
public function __construct(
13-
public readonly string $name,
26+
public readonly string|null $name = null,
1427
public readonly string|null $connection = null,
1528
) {
1629
}

src/DependencyInjection/Compiler/DatabaseTypePass.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,11 @@ public function process(ContainerBuilder $container): void
5858
// Service-tagged types: global (no connection restriction) or matching this connection
5959
foreach ($taggedServiceIds as $id => $tags) {
6060
foreach ($tags as $tag) {
61-
if (! isset($tag['type'])) {
62-
continue;
63-
}
64-
6561
if ($name !== ($tag['connection'] ?? $name)) {
6662
continue;
6763
}
6864

69-
$instances[$tag['type']] = new Reference($id);
65+
$instances[$tag['type'] ?? $id] = new Reference($id);
7066
}
7167
}
7268

tests/DependencyInjection/Compiler/DatabaseTypePassTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,32 @@ public function testTypeRegisteredOnMultipleConnectionsViaRepeatedAttribute(): v
8282
$this->assertTypeRegistered($container, 'conn2', 'multi_alias', MultiConnectionType::class);
8383
}
8484

85+
public function testTypeWithNoNameDefaultsToServiceId(): void
86+
{
87+
$container = $this->createContainer(static function (ContainerBuilder $container): void {
88+
$container->register(AutoconfiguredAnonymousType::class, AutoconfiguredAnonymousType::class)
89+
->setAutoconfigured(true);
90+
91+
$container->setAlias('registry_conn1', 'doctrine.dbal.conn1_connection.type_registry')->setPublic(true);
92+
$container->setAlias('registry_conn2', 'doctrine.dbal.conn2_connection.type_registry')->setPublic(true);
93+
});
94+
95+
$this->assertTypeRegistered($container, 'conn1', AutoconfiguredAnonymousType::class, AutoconfiguredAnonymousType::class);
96+
$this->assertTypeRegistered($container, 'conn2', AutoconfiguredAnonymousType::class, AutoconfiguredAnonymousType::class);
97+
}
98+
99+
public function testTaggedTypeWithNoTypeAttributeDefaultsToServiceId(): void
100+
{
101+
$container = $this->createContainer(static function (ContainerBuilder $container): void {
102+
$container->register('my_type', MoneyType::class)
103+
->addTag('doctrine.dbal.type');
104+
105+
$container->setAlias('registry_conn1', 'doctrine.dbal.conn1_connection.type_registry')->setPublic(true);
106+
});
107+
108+
$this->assertTypeRegistered($container, 'conn1', 'my_type', MoneyType::class);
109+
}
110+
85111
public function testAutoconfiguredTypeViaAttribute(): void
86112
{
87113
$container = $this->createContainer(static function (ContainerBuilder $container): void {
@@ -328,6 +354,15 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st
328354
}
329355
}
330356

357+
#[AsDatabaseType]
358+
class AutoconfiguredAnonymousType extends Type
359+
{
360+
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
361+
{
362+
return 'VARCHAR(255)';
363+
}
364+
}
365+
331366
#[AsDatabaseType(name: 'multi', connection: 'conn1')]
332367
#[AsDatabaseType(name: 'multi_alias', connection: 'conn2')]
333368
class MultiConnectionType extends Type

0 commit comments

Comments
 (0)