You are browsing a version that has not yet been released. |
DBAL Types
Custom DBAL types let you map a database column to a PHP value of your choice.
The recommended way to register one is the #[AsDbalType] attribute, which
declares the type directly on its class.
Registering a Type with the Attribute
Create a class that extends Doctrine\DBAL\Types\Type and add the
#[AsDbalType] attribute to it:
// src/Doctrine/Type/MoneyType.phpnamespace App\Doctrine\Type;use Doctrine\Bundle\DoctrineBundle\Attribute\AsDbalType;use Doctrine\DBAL\Platforms\AbstractPlatform;use Doctrine\DBAL\Types\Type;#[AsDbalType]class MoneyType extends Type{ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { return $platform->getDecimalTypeDeclarationSQL($column); } public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed { return $value; } public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed { return $value; }}
The type is then available in your mappings. When no name is given, it defaults to the fully-qualified class name, so you can reference the type by its class:
// src/Entity/Product.phpnamespace App\Entity;use App\Doctrine\Type\MoneyType;use Doctrine\ORM\Mapping as ORM;#[ORM\Entity]class Product{ #[ORM\Column(type: MoneyType::class)] private Money $price;}
To use a shorter name instead, pass it to the attribute and reference that name in the mapping:
#[AsDbalType(name: 'money')]class MoneyType extends Type{ // ...}
#[ORM\Column(type: 'money')]private Money $price;
|
The |
|
With DBAL >= 4.6 the type is registered on the connections instead of the
global type registry. The ORM resolves it when it reads the type provider of
the connection, which requires Doctrine ORM >= 3.8. Types that other code
registers globally with |
Dependency Injection
The type is registered as a service, so its constructor can declare dependencies that are resolved by the container:
#[AsDbalType]class MoneyType extends Type{ public function __construct(private readonly ExchangeRateProvider $rates) { } // ...}
|
Dependency injection requires DBAL >= 4.6, where the type is resolved lazily from a per-connection registry. On older DBAL versions the type is registered in the global registry and created by DBAL, so a constructor with required parameters fails when a connection is created. |
Restricting a Type to a Connection
By default the type is registered on every connection. Use the connection
parameter to restrict it to a single connection. The attribute is repeatable, so
a type can be registered on several connections with different names:
#[AsDbalType(name: 'money', connection: 'default')]#[AsDbalType(name: 'money', connection: 'reporting')]class MoneyType extends Type{ // ...}
|
Restricting a type to a connection requires DBAL >= 4.6. On older versions
the |
Registering a Type as a Service
The attribute is autoconfigured to the doctrine.dbal.type tag, so tagging a
service is equivalent to using the attribute. This is useful when
autoconfiguration is disabled, or to register the same class several times with
different dependencies. The type_name attribute sets the type name; when
omitted, the service id is used:
# config/services.yamlservices: App\Doctrine\Type\MoneyType: tags: - { name: doctrine.dbal.type, type_name: money, connection: reporting }
Registering a Type with the Configuration
When you do not need dependency injection, a type can be registered by mapping a
name to its class name under doctrine.dbal.types. This is supported on all
DBAL versions and does not turn the type into a service. With DBAL >= 4.6 the
type is registered both in the global registry and in the type provider of each
connection, so the two registrations hold separate instances of the type:
# config/packages/doctrine.yamldoctrine: dbal: types: money: App\Doctrine\Type\MoneyType
