Create the value object that you want to use as your custom type.
In my case the file module/Geo/src/Domain/Address/Id.php
namespace SwarmTech\Geo\Domain\Address;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
final class Id
{
private UuidInterface $value;
public function __construct(UuidInterface $value)
{
$this->value = $value;
}
public function value(): UuidInterface
{
return $this->value;
}
public static function random(): self
{
$uuid = Uuid::uuid4();
return new self($uuid);
}
public static function fromString(string $value): self
{
$uuid = Uuid::fromString($value);
return new self($uuid);
}
public function equals(self $other): bool
{
return $this->value->equals($other->value());
}
public function __toString(): string
{
return $this->value->__toString();
}
}
Create the doctrine type class.
In my case in module/Geo/src/Doctrine/Type/
namespace SwarmTech\Geo\Infrastructure\Doctrine\Type;
use SwarmTech\Geo\Domain\Address\Id as AddressId;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Type;
use InvalidArgumentException;
class AddressIdType extends Type
{
public const NAME = 'address_id';
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
return $platform->getGuidTypeDeclarationSQL($fieldDeclaration);
}
public function convertToPHPValue($value, AbstractPlatform $platform):?AddressId
{
if ($value === null || $value === '') {
return null;
}
if ($value instanceof AddressId) {
return $value;
}
try {
$addressId = AddressId::fromString($value);
} catch (InvalidArgumentException $e) {
throw ConversionException::conversionFailed($value, self::NAME);
}
return $addressId;
}
public function convertToDatabaseValue($object, AbstractPlatform $platform): ?string
{
if (null === $object || '' === $object) {
return null;
}
if ($object instanceof AddressId) {
return $object->__toString();
}
throw ConversionException::conversionFailed($object, self::NAME);
}
public function getName(): string
{
return self::NAME;
}
public function requiresSQLCommentHint(AbstractPlatform $platform): bool
{
return true;
}
}
Setup the configuration for doctrine in the ConfigProvider class of your module.
In my case module/Geo/src/ConfigProvider.php
namespace SwarmTech\Geo;
use Swarmtech\Geo\Infrastructure\Doctrine\Type\AddressIdType;
class ConfigProvider
{
public function __invoke(): array
{
return [
'doctrine'
'configuration' => [
'orm_default' => [
'types' => [
AddressIdType::NAME => AddressIdType::class,
]
]
]
]
];
}
}
Use your type in a doctrine entity.
In my case in the file module/Geo/src/Domain/Address.php
namespace SwarmTech\Geo\Domain;
use SwarmTech\Geo\Domain\Address\Id as AddressId;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="geo_address")
* @ORM\Entity
*/
class Address
{
/**
* @ORM\Id
* @ORM\Column(name="address_id", type="address_id")
*/
private AddressId $id;
public function __construct(AddressId $id)
{
$this->id = $id;
}
}