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 #[AsDbalType] attribute and the doctrine.dbal.type tag require DoctrineBundle >= 3.3. Dependency injection, the connection argument and repeatable attributes require DoctrineBundle >= 3.4.

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 Type::addType() stay available, because the connection provider falls back to the global registry.

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 connection is ignored and the type is registered globally.

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