One of my favorite features of Ruby on Rails is that all models are timestamped by default. Ruby on Rails, of course, opts for convention over configuration. Symfony2, on the other hand, opts for configuration over convention. Anyone who has worked with Symfony2 can speak to the mass of config files that exist in a project. I can understand, therefore, why timestamped models (entities) are not a feature in Symfony2. In this post I will show how to add timestamp all your Symfony2 entities. For this post, I chose to use Doctrine as my ORM, so all code show directly relates to Doctrine and Symfony2.

Luckily, adding and updated and created fields to an entity in Symfony2 with Doctrine is quite easy:

<?php
/**
 * @var \DateTime
 *
 * @ORM\Column(name="created", type="datetime")
 */
private $created;

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

public function __construct()
{
    $this->setCreated(new \DateTime());
    $this->setUpdated(new \DateTime());
}

// getters & setters

/**
 * Auto set the updated date
 *
 * @ORM\PreUpdate
 */
public function setUpdatedValue()
{
   $this->setUpdated(new \DateTime());
}

The code should be pretty self explanitory, we have two DateTime field types that get initially set on entity creation in the contstructor. I then add a setUpdatedValue() function that runs when on PreUpdate of the entity.

The above code works very well, but it is not an ideal solution for working with multiple models. Of course, we could add the updated/created values to every entity, but that wouldn’t be very DRY of us.

The solution is to use an Abstract Class that our entity can extend. The abstract class, TimeStampedEntity, will have two entity variables $updated and $created and will look nearly identical to the code above when it is complete. The key that will make this work with Doctrine and Symfony2 is Doctrine’s MappedSuperclass declaration that tells Symfony and Doctrine that the abstract class is a Superclass with declared entity attributes. What we are left with is a Superclass that can extend any model to add updated and created attributes to your data.

<?php
/**
 * TimeStampedEntity.php
 */

namespace HarmsTyler\Common\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Post
 *
 * @ORM\MappedSuperclass
 * @ORM\HasLifecycleCallbacks
 */
abstract class TimeStampedEntity {

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

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

    /**
     * Set created
     *
     * @param \DateTime $created
     * @return Post
     */
    public function setCreated($created)
    {
        $this->created = $created;

        return $this;
    }

    /**
     * Get created
     *
     * @return \DateTime
     */
    public function getCreated()
    {
        return $this->created;
    }

    /**
     * Set updated
     *
     * @param \DateTime $updated
     * @return Post
     */
    public function setUpdated($updated)
    {
        $this->updated = $updated;

        return $this;
    }

    /**
     * Get updated
     *
     * @return \DateTime
     */
    public function getUpdated()
    {
        return $this->updated;
    }

    /**
     * Auto set the updated date
     *
     * @ORM\PreUpdate
     */
    public function setUpdatedValue()
    {
       $this->setUpdated(new \DateTime());
    }

    /**
     * Set initial value for created/updated values
     *
     * @ORM\PrePersist
     */
    public function setCreatedValues()
    {
        $this->setCreated(new \DateTime());
        $this->setUpdated(new \DateTime());
    }
}

An example in use:

<?php
/**
 * Post.php
 */

namespace Blend\Iterate\BlogBundle\Entity;

use HarmsTyler\Common\Entity\TimeStampedEntity;
use Doctrine\ORM\Mapping as ORM;

/**
 * Post
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Post extends TimeStampedEntity
{

edited to add setCreatedValues()