How To Use Lifecycle Callbacks in Symfony 2


In this video we look at the basics of Doctrine Lifecycle Callbacks. These are a set of annotations which allow you to take some action just before, or just after an entity has been inserted, updated, or deleted from your system.

Events in PHP land are not as immediately obvious as they might be in JavaScript for instance. In JavaScript we might react to a user clicking a button, or pressing a key, or some other front end type scenario.

In Symfony 2 / Doctrine, events are still happening all the time, but it's not as obvious that they are doing so.

We can look at all the events that Doctrine can trigger by opening the Doctrine\ORM\Events class. By adding in the right annotations to our Entity classes, we can then begin to respond to these events.

Firstly, we need to add an annotation to the Entity itself:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 */
class BlogPost
{
    // ...
}

Then, depending on what event we are listening for, we need to add in the correct annotation above the method we want to call when that event takes place. Remember, if you're copy / pasting from the Doctrine docs, don't forget to add in the ORM\ to the annotation given - e.g:

<?php
class UserListener
{
    /** @PrePersist */
    public function prePersistHandler(User $user, LifecycleEventArgs $event) { // ... }

    // becomes

    /** @ORM\PrePersist */
    public function prePersistHandler(User $user, LifecycleEventArgs $event) { // ... }

With our method(s) annotated, there's not a lot left to do.

Updated At

There is one situation that may leave you scratching your head. When written down it seems obvious, but it may catch you out when you have been buried in your IDE all morning.

If you add one of the 'update' related annotations, if your entity is loaded, handled in some way, but nothing on the entity changes, you won't trigger the update callback.

This is particularly evident when dealing with uploads / files.

Reference Entity

The following is a basic reference for a simple lifecycle callback implementation.

I emphasise simple because according to the Doctrine best practices, you really should be keeping them as simple as possible.

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
// some more use statements here

/**
 * BlogPost
 *
 * @ORM\Table(name="my_table_name")
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 */
class BlogPost
{
    // snip snip snip

    /**
     * created Time/Date
     *
     * @var \DateTime
     *
     * @ORM\Column(name="created_at", type="datetime", nullable=false)
     */
    protected $createdAt;

    /**
     * updated Time/Date
     *
     * @var \DateTime
     *
     * @ORM\Column(name="updated_at", type="datetime", nullable=false)
     */
    protected $updatedAt;

    // snip snip snip

    /**
     * Set createdAt
     *
     * @ORM\PrePersist
     */
    public function setCreatedAt()
    {
        $this->createdAt = new \DateTime();
        $this->updatedAt = new \DateTime();
    }

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

    /**
     * Set updatedAt
     *
     * @ORM\PreUpdate
     */
    public function setUpdatedAt()
    {
        $this->updatedAt = new \DateTime();
    }

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

As shown in the video, this implementation may actually cause more trouble than it solves. I show a solution to that, but have left this as-is to illustrate the annotation usage more than anything else.

In the next video we will cover some of the more creative ways to use Doctrine's lifecycle callbacks.

Episodes

# Title Duration
1 How To Use Lifecycle Callbacks in Symfony 2 05:23
2 Going Further With Lifecycle Callbacks 05:29