Search code examples
symfonydoctrine-ormdoctrineentity

Symfony set `createdBy` and `modifiedBy` properties of entity


I want to automatically set the createdBy and modifiedBy properties of an entity.

To set these properties I use the doctrine lifecycle hooks prePersist and preUseras described here: https://symfony.com/doc/current/doctrine/events.html#doctrine-lifecycle-callbacks. I also created a setCreatedByValue and setModifiedByValue functions to set the fields ...

Is there any best practice on how to write the currently logged-in user into these properties? Is there a let's say "more elegant" way than injecting the security service into the entity?

How would you do that?


Solution

  • I now searched through the Symfony docs once again and chose the following approach for me:

    I created an EntitySubscriber as described here: https://symfony.com/doc/current/doctrine/events.html#doctrine-lifecycle-subscribers

    After that, I created two interfaces TimestampInterface (for createdAt and modifiedAt) and a UserAwareInterface (for createdBy and modifiedBy). I also created an AbstractEntity that implements the interfaces mentioned above. Now I can extend each entity (which needs this functionality) with the AbstractEntity class ...

    Then I simply created a doctrine.event_subscriber (as described in the docs) and checked for the interfaces to set certain fields.

    To set the createdBy and modifiedBy fields I used the Security container. Works pretty well - and with this solution, I don't rely on any third-party libs.

    <?php 
    
    namespace Foo\Bar\EventListener;
    
    use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
    use Doctrine\ORM\Event\PrePersistEventArgs;
    use Doctrine\ORM\Event\PreUpdateEventArgs;
    use Doctrine\ORM\Events;
    use Symfony\Bundle\SecurityBundle\Security;
    use Foo\Bar\Entity\TimestampInterface;
    use Foo\Bar\Entity\UserAwareInterface;
    
    class EntitySubscriber implements EventSubscriberInterface
    {
        public function __construct(
            public Security $security,
        ) {}
    
        public function getSubscribedEvents(): array
        {
            return [
                Events::prePersist,
                Events::preUpdate
            ];
        }
    
        public function prePersist(PrePersistEventArgs $prePersistEventArgs): void
        {
            $object = $prePersistEventArgs->getObject();
    
            if($object instanceof TimestampInterface){
                $object->setCreatedAt(time());
                $object->setModifiedAt(time());
            }
    
            if($object instanceof UserAwareInterface){
                $object->setCreatedBy($this->security->getUser()->getId());
                $object->setModifiedBy($this->security->getUser()->getId());
            }
        }
    
        public function preUpdate(PreUpdateEventArgs $preUpdateEventArgs): void
        {
            $object = $preUpdateEventArgs->getObject();
    
            if($object instanceof TimestampInterface){
                $object->setModifiedAt(time());
            }
    
            if($object instanceof UserAwareInterface){
                $object->setModifiedBy($this->security->getUser()->getId());
            }
        }
    }
    

    What do you think about that solution?