Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle SQL triggers in Symfony2?

I have a User entity in my Symfony2/Doctrine2 webapp. This user has an attribute last_updated to identify the latest time, anything has changed. I set this attribute to NOT NULL in my database. So far, so good.

I would consider it to be good practice to create a SQL trigger in the database, that sets this last_updated to NOW() on every INSERT or UPDATE. So you don't have to care about this in your application. So that's what I did, I implemented this trigger in my database.

But if I now create a user in my app

$user = new User();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();

I get an error message by Symfony:

An exception occurred while executing 'INSERT INTO User (username, ..., last_updated) VALUES (?, ..., ?)' with params ["johndoe", ..., null]:

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'last_updated' cannot be null

The problem is clear: Symfony is trying to fire an INSERT-statement to the database with the parameter null for last_updated, which is not allowed - as this attribute may not be null.

I could quickly think of two workarounds:

  • One workaround would be to take the last_updated field out of the entity description. Then Symfony would not try to pass anything to the database for this column, and the trigger would set the appropriate value. But I don't think this is a good way, because as soon as I would try to update the db schema (doctrine:schema:update --force) I would loose my last_updated-column.
  • Another workaround: Simply do $user->setLastUpdated(new \DateTime()) before I persist() and flush(). But this would minimize the advantage of using a trigger on my database to avoid having to care about it in my application.

Is there any way to let Symfony/Doctrine know that there is a trigger running on my database? If not, (how) can I hook into Symfony/Doctrine to implement a proper workaround?

like image 973
Gottlieb Notschnabel Avatar asked Aug 22 '13 10:08

Gottlieb Notschnabel


1 Answers

To quote a response to this question from a google group:

Database side code (such as Triggers and Functions) tend to break the benefits of developing software using an ORM like Propel or Doctrine as one of the biggest advantages of using ORM's is to be database agnostic. By having database side Triggers and Functions you are tying yourself to the database and therefore gain little to no benefit using an ORM. -GarethMc

https://groups.google.com/forum/#!topic/symfony-users/MH_ML9Dy0Rw

For this it is best to use the Life Cycle Callbacks as Faery suggests. One simple function will handle updating that field so that you dont have to worry about it if you decide to change databases in the future.

//In Your Entity File EX: SomeClass.php
/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks()
 */
class SomeClass
{
    ....

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function prePersistPreUpdate()
    {
        $this->last_modified = new \DateTime();
    }
}

See also references for lifecycle callbacks

  • Symfony reference
  • Doctrine reference

In your case you would add the lifecycle call back function and annotation to your User entity class. SomeClass is simply an example class showing that lifecycle callbacks are good for more than just your User entity.

like image 200
Chase Avatar answered Sep 21 '22 12:09

Chase