Allow to create Custom Type with metadata accessible from convertToPHPValue
Feature Request
What
Allow Custom Types to access field mapping metadata (like options or a new class parameter) in convertToPHPValue() and convertToDatabaseValue().
Example usage:
#[ORM\Column(type: 'string_vo', class: Firstname::class)]
private Firstname $firstName;
#[ORM\Column(type: 'string_vo', class: Email::class)]
private Email $email;
Why
Currently, creating Value Objects requires one Custom Type per class, even when the conversion logic is identical (string ↔ VO with __toString()).
The enumType parameter solves this elegantly for enums, but there's no equivalent for custom objects.
This leads to:
- Boilerplate: dozens of nearly identical Type classes
- Configuration overhead: one YAML entry per VO
- Maintenance burden: any change must be replicated across all types
A generic approach would allow a single StringValueObjectType to handle all string-based Value Objects, similar to how enumType works.
How
Pass field mapping metadata to Type methods:
public function convertToPHPValue(
mixed $value,
AbstractPlatform $platform,
array $fieldMapping = [] // New optional parameter
): mixed {
$class = $fieldMapping['class'] ?? null;
if ($class && $value !== null) {
return new $class($value);
}
return $value;
}
Alternatively, introduce a dedicated parameter like valueObjectClass similar to enumType.
This would maintain backward compatibility while enabling generic Type implementations for Value Objects.
Not sure if the DBAL type system is a good fit for this feature. Maybe we should implement this in the ORM instead, like we did with enumType.
Thank you for the feedback! I understand your point about the DBAL type system potentially not being the right fit. I mentioned enumType precisely because I see it as a precedent for this kind of flexibility - if implementing this at the ORM level (similar to enumType) would be the better architectural approach, I'm completely open to that solution. My core need is straightforward: I want to avoid creating multiple Type classes with identical conversion logic, differing only in the target Value Object class. Whether this is achieved through:
Field mapping metadata passed to DBAL Type methods, or An ORM-level implementation like enumType
...either approach would solve my use case. If the ORM layer is indeed the more appropriate place for this feature, I'd be happy to have the issue transferred there or to open a new one in the ORM repository. Just let me know what works best for the project architecture.
If the ORM layer is indeed the more appropriate place for this feature, I'd be happy to have the issue transferred there
Done.
Why not use embeddebales?
Thanks for the suggestion! Embeddables are great for certain use cases, but they don't fit well here for a few reasons:
- Loss of flexibility for database constraints With embeddables, the Doctrine attributes (#[Column]) must be defined inside the Value Object class itself:
class Email {
#[Column(type: 'string', unique: true)] // ⚠️ Fixed for ALL uses
public function __construct(private string $value) {}
}
This creates a major problem: sometimes email must be unique, sometimes not, depending on the entity context. With embeddables, you're forced to choose one behavior for all cases, or create multiple Value Object classes for the same logical type. With a custom Type, each entity controls its own constraints:
// In User entity
#[Column(type: 'string_vo', unique: true, class: 'Email::class')]
private Email $email;
// In ContactForm entity
#[Column(type: 'string_vo', unique: false, class: 'Email::class')]
private Email $secondEmail;
- Mapping boilerplate With embeddables, I need to explicitly map every property and each property:
#[Embedded(class: Email::class)]
private Email $email;
With a custom Type, the mapping is simpler and the same for each vo:
#[Column(type: 'string_vo', class: 'Name::class')]
private Name $name;
#[Column(type: 'string_vo', class: 'Email::class')]
private Email $email;
- Value Objects should stay persistence-agnostic Value Objects are domain concepts that shouldn't know about database concerns. With embeddables, you're forced to pollute your Value Objects with Doctrine-specific attributes:
use Doctrine\ORM\Mapping as ORM;
class Email {
#[ORM\Column(type: 'string', length: 255)] // ❌ Domain layer knows about persistence
public function __construct(private string $value) {
// validation logic
}
}
This creates tight coupling between the domain layer and the persistence layer. With custom Types, the Value Object stays clean:
- My actual need
I have dozens of single-value Value Objects that each need:
- Identical conversion logic (validate → instantiate)
- Different target classes
Currently, I must create one Type class per Value Object, duplicating the same conversion code. I'm looking for a way to have one reusable Type that can handle multiple Value Object classes based on field metadata - similar to how enumType works. Embeddables solve a different problem (multi-property value objects), not the "avoid duplicating Type classes" problem.