28 July 2015 —
Doctrine is currently the most used ORM in PHP. It makes it very easy to work with databases in an object oriented way.
It comes with a set of built-in column types that map database types with PHP types. For example, the datetime column type, persists the value of an entity column as a datetime in the database and handles it as a DateTime
object when the entity is hydrated.
Type conversions work both ways, so column types take care of casting database to PHP types and vice versa.
In this article I’m going to explain how to define custom column types so that we can persist our own objects into the database and hydrate them back.
PHP enums
One of the features that PHP lacks of, is a consistent enumerations API. You can always define a class full of constants, but PHP constants can only be scalars, so we can’t define custom methods. Also, it is not possible to limit the values of an enumeration or typehint arguments and return types when using those constants.
For this purpose I recently discovered the myclabs/php-enum package. It provides a very good way to mimic Java enumerations in PHP.
Using this package, we can limit the values of properties in doctrine entities, but we need to tell doctrine how to persist those enumerations.
Custom Doctrine type
Let’s imagine we have this enum, and we want it to be a valid doctrine type.
We also have this entity with a column of type Acelaya\Enum\Action
.
We have used the column type php_enum_action in the Doctrine\ORM\Mapping\Column
annotation for the $action
property. We now need to tell doctrine how to convert that type from PHP to database and how to hydrate the column back.
Defining a custom type in doctrine is as easy as creating a class extending Doctrine\DBAL\Types\Type
, and overwriting the methods getName
, getSQLDeclaration
, convertToPHPValue
and convertToDatabaseValue
.
This is what each method does:
getName
: It just returns the name of the type that is used in theDoctrine\ORM\Mapping\Column
annotation.getSQLDeclaration
: Returns the SQL code used to create a field that is going to store the value of the enum in the database. A simple VARCHAR is enough in our case.convertToPHPValue
: Gets the value of the database and casts it to a PHP value. In this case it is going to get a string (from the VARCHAR field) and return anAcelaya\Enum\Action
instance with the correct value. It also checks that the value from the database is valid for the enum.convertToDatabaseValue
: Finally this method casts the objects of typeAcelaya\Enum\Action
to something that can be stored in a VARCHAR column. Since objects of typeMyCLabs\Enum\Enum
implement the__toString()
method, which returns the value of the corresponding constant, this operation is as simple as casting the value to string.
We now have to register this custom type so that doctrine is able to handle it.
At your application’s bootstrap, make this call.
And that’s it! You can use your custom type wherever you want, and be sure that your entity properties will be objects properly persisted into the database.
The Doctrine enum type package
I couldn’t finish this article without mentioning a package that I published two days ago, the acelaya/doctrine-enum-type.
You have probably seen that the previous process needs to be done once per enum, since each custom type will need to return a different object type and have a different name.
This package eases that process, by providing a base abstract class, the Acelaya\Doctrine\Type\AbstractPhpEnumType
, which implements the common parts. You will just need to define the name and the enum’s object type, and then register each type.
For example, using this package, the Acelaya\Type\ActionEnumType
would have been like this:
The rest of the process is the same.