Implementing a TypedFieldMapper¶
New in version 2.14.
You can specify custom typed field mapping between PHP type and DBAL type using Doctrine\ORM\Configuration
and a custom Doctrine\ORM\Mapping\TypedFieldMapper
implementation.
<?php
$configuration->setTypedFieldMapper(new CustomTypedFieldMapper());
DefaultTypedFieldMapper¶
By default the Doctrine\ORM\Mapping\DefaultTypedFieldMapper
is used, and you can pass an array of
PHP type => DBAL type mappings into its constructor to override the default behavior or add new mappings.
<?php
use App\CustomIds\CustomIdObject;
use App\DBAL\Type\CustomIdObjectType;
use Doctrine\ORM\Mapping\DefaultTypedFieldMapper;
$configuration->setTypedFieldMapper(new DefaultTypedFieldMapper([
CustomIdObject::class => CustomIdObjectType::class,
]));
Then, an entity using the CustomIdObject
typed field will be correctly assigned its DBAL type
(CustomIdObjectType
) without the need of explicit declaration.
It is perfectly valid to override even the “automatic” mapping rules mentioned above:
<?php
use App\DBAL\Type\CustomIntType;
use Doctrine\ORM\Mapping\DefaultTypedFieldMapper;
$configuration->setTypedFieldMapper(new DefaultTypedFieldMapper([
'int' => CustomIntType::class,
]));
Note
If chained, once the first TypedFieldMapper
assigns a type to a field, the DefaultTypedFieldMapper
will
ignore its mapping and not override it anymore (if it is later in the chain). See below for chaining type mappers.
TypedFieldMapper interface¶
The interface Doctrine\ORM\Mapping\TypedFieldMapper
allows you to implement your own
typed field mapping logic. It consists of just one function
<?php
/**
* Validates & completes the given field mapping based on typed property.
*
* @param array{fieldName: string, enumType?: string, type?: mixed} $mapping The field mapping to validate & complete.
* @param \ReflectionProperty $field
*
* @return array{fieldName: string, enumType?: string, type?: mixed} The updated mapping.
*/
public function validateAndComplete(array $mapping, ReflectionProperty $field): array;
ChainTypedFieldMapper¶
The class Doctrine\ORM\Mapping\ChainTypedFieldMapper
allows you to chain multiple TypedFieldMapper
instances.
When being evaluated, the TypedFieldMapper::validateAndComplete
is called in the order in which
the instances were supplied to the ChainTypedFieldMapper
constructor.
<?php
use App\DBAL\Type\CustomIntType;
use Doctrine\ORM\Mapping\ChainTypedFieldMapper;
use Doctrine\ORM\Mapping\DefaultTypedFieldMapper;
$configuration->setTypedFieldMapper(
new ChainTypedFieldMapper(
new DefaultTypedFieldMapper(['int' => CustomIntType::class,]),
new CustomTypedFieldMapper()
)
);
Implementing a TypedFieldMapper¶
If you want to assign all BackedEnum
fields to your custom BackedEnumDBALType
or you want to use different
DBAL types based on whether the entity field is nullable or not, you can achieve this by implementing your own
typed field mapper.
You need to create a class which implements Doctrine\ORM\Mapping\TypedFieldMapper
.
<?php
final class CustomEnumTypedFieldMapper implements TypedFieldMapper
{
/**
* {@inheritdoc}
*/
public function validateAndComplete(array $mapping, ReflectionProperty $field): array
{
$type = $field->getType();
if (
! isset($mapping['type'])
&& ($type instanceof ReflectionNamedType)
) {
if (! $type->isBuiltin() && enum_exists($type->getName())) {
$mapping['type'] = BackedEnumDBALType::class;
}
}
return $mapping;
}
}
Note
Note that this case checks whether the mapping is already assigned, and if yes, it skips it. This is up to your
implementation. You can make a “greedy” mapper which will always override the mapping with its own type, or one
that behaves like the DefaultTypedFieldMapper
and does not modify the type once its set prior in the chain.