This tutorial has a new version, check it out!

Doctrine is in your Lifecycle (with Callbacks)

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Doctrine is in your Lifecycle (with Callbacks)

Remember when we used StofDoctrineExtensions to set the Event’s slug for us? That magic works by leveraging one of the most powerful features of Doctrine: events. Doctrine gives us the flexibility to have hooks that are called whenever certain things are done, like when an entity is first persisted, updated, or deleted.

Open up Event and remove the @Gedmo annotation above createdAt. Let’s see if we can set this ourselves:

// src/Yoda/EventBundle/Entity/Event.php
// ...

/**
 * @ORM\Column(type="datetime")
 */
private $createdAt;

Replace this with a new function called prePersist that sets the value:

// src/Yoda/EventBundle/Entity/Event.php
// ...

public function prePersist()
{
    if (!$this->getCreatedAt()) {
        $this->createdAt = new \DateTime();
    }
}

Hey, don’t get too excited! This won’t work yet, but if we could tell Doctrine to call this before inserting an Event, we’d be golden!

The secret is a called lifecycle callbacks: a fancy word for a function that Doctrine will call when something happens, like when an entity is first inserted.

To enable lifecycle callbacks on an entity, add the HasLifecycleCallbacks annotation:

// src/Yoda/EventBundle/Entity/Event.php
// ...

/**
 * @ORM\Table(name="yoda_event")
 * @ORM\Entity(repositoryClass="Yoda\EventBundle\Entity\EventRepository")
 * @ORM\HasLifecycleCallbacks
 */
class Event
{
    // ...
}

Now just put a PrePersist annotation above our function:

// src/Yoda/EventBundle/Entity/Event.php
// ...

/**
 * @ORM\PrePersist
 */
public function prePersist()
{
    if (!$this->getCreated()) {
        $this->createdAt = new \DateTime();
    }
}

PrePersist is called only when an entity is first inserted, and there are other lifecycle events like PreUpdate and PreRemove.

Cool, let’s give it a test! Reload your fixtures and then query to see the events:

php app/console doctrine:fixtures:load
php app/console doctrine:query:sql "SELECT * FROM yoda_event"

The createdAt column is set so this must be working.

Lifecycle callbacks are brilliant because they’re just so easy to setup.

But they have one big limitation. Because the callback is inside an entity, we don’t have access to the container or any services. This wasn’t a problem here, but what if we needed to access the router or the logger?

The solution is to use a slight spin on lifecycle callbacks: events.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "~2.4", // v2.4.2
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.0.1
        "symfony/assetic-bundle": "~2.3", // v2.3.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.5
        "symfony/monolog-bundle": "~2.4", // v2.5.0
        "sensio/distribution-bundle": "~2.3", // v2.3.4
        "sensio/framework-extra-bundle": "~3.0", // v3.0.0
        "sensio/generator-bundle": "~2.3", // v2.3.4
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
        "ircmaxell/password-compat": "~1.0.3", // 1.0.3
        "phpunit/phpunit": "~4.1", // 4.1.0
        "stof/doctrine-extensions-bundle": "~1.1.0" // v1.1.0
    }
}