Automating timestamps with Doctrine ORM

The Doctrine ORM includes a robust Event system enabling timestamp fields to be set automatically without any explicit methods calls during object instantiation. This also works great when utilising the many smart RESTful design patterns for Symfony, Laravel and other frameworks which can implement Doctrine.

For efficiency, Doctrine doesn’t currently default to look for event hooks so the following annotation must be added to the Entity class:

[code language=”php”]
* @ORM\HasLifecycleCallbacks()
[/code]

The following columns will be utilised (either add to Database manually, using the relevant Doctrine console commands, or even better, a Doctrine Migration):

[code language=”php”]
/**
* @var \DateTime
*
* @ORM\Column(name="created", type="datetime")
*/
private $created;

/**
* @var \DateTime
*
* @ORM\Column(name="updated", type="datetime", nullable=true)
*/
private $updated;

/**
* @var \DateTime
*
* @ORM\Column(name="deleted", type="datetime", nullable=true)
*/
private $deleted;
[/code]

These functions provide the event listening (using annotations):

[code language=”php”]
/**
* Triggered on insert

* @ORM\PrePersist
*/
public function onPrePersist()
{
$this->created = new \DateTime("now");
}

/**
* Triggered on update

* @ORM\PreUpdate
*/
public function onPreUpdate()
{
$this->updated = new \DateTime("now");
}

/**
* Triggered on update

* @ORM\PreRemove
*/
public function onPreRemove()
{
$this->deleted = new \DateTime("now");
}
[/code]

Now, all Product objects which get persisted to the Doctrine Entity Manager will have the correct timestamp set when created, updated or deleted.

The complete Entity class (with added id & name fields for testing) is as follows:

After creating, persisting and flushing a new instance, the created timetamp gets automatically set. After selecting the previous row, changing the name and flushing, the updated timestamp gets automatically set. After selecting the previous row and deleting it, a deleted timestamp get automatically set.

Please note, the deleted timestamp assumes your database is retaining the data in a “deleted” state, using triggers or other such database functionality to handle Doctrine’s instruction to delete the row. If you are not using this, either ignore the code or remove it.

Advertisements

Simpler Doctrine classes by convention

Doctrine convention adherence leads to simpler entity classes, no picky join column specification. A standard bi-directional one-to-many join becomes simply:

The “One” Entity:

The “Many” Entity:

Nice and straight forward.

The getters & setters can be auto-generated with the following console commands:

$ php app/console doctrine:generate:entities AppBundle:Product

and

$ php app/console doctrine:generate:entities AppBundle:Feature

If desired, now is the time to add any Doctrine Events such as LifeCycle Callbacks, which we do manually until they get better integrated into Doctrine’s console generator commands.

After everything is added, it can be a good style convention to leave a few lines between the field declarations and member functions (including the constructor) as these are just boring plumbing, the field declarations should be the focus.