minychillo
minychillo

Reputation: 515

Symfony observer pattern - How to dispatch custom event when setting entity property?

Is there a way to dispatch a custom event every time a certain entity setter is called ?

I actually need to change some value of an unrelated entity, every time a certain entity property is changed. So in order to separate concerns and to decouple objects, I wanted to do this with the observer pattern. I don't want to do this in some doctrine event like 'preUpdate' or similar, as they only fire when the entity is flushed, but I need this value to change immediately to assure these two values are always in sync.

As it is bad practice to inject any service into the entity, I don't see how I could do that ?

Any suggestions ?

Upvotes: 1

Views: 3261

Answers (2)

Alan T.
Alan T.

Reputation: 1440

If you wish to use the observer pattern, you will have to implement it yourself in some way. As you pointed out, Doctrine will compute the changeset of your entity only when a flush operation is triggered and not before. That being said, it happens that Doctrine proposes alternative tracking policies. The NOTIFY tracking policy behaviour relies exactly on what you wish to achieve.

I am not suggesting that you should change the tracking policy of your entity but you could take advantage of the existing interfaces to implement your observer pattern. To do so, as explained in this section of the documentation, your entity being observed needs to implement the NotifyPropertyChanged interface.

From there you could implement the PropertyChangedListener interface directly in the other entity (or use a specific service that would add itself as listener of your entity in the postLoad event for example ?). Here it mainly depends on the relation between your entities and how you can attach your listener to the entity implementing NotifyPropertyChanged.

Note that if you do this, the UnitOfWork of Doctrine will automatically hook itself as a listener of your entity but it will still rely on automatic changeset computation as long as you don't add the @ChangeTrackingPolicy("NOTIFY") annotation.

Upvotes: 1

goto
goto

Reputation: 8162

Using the event dispatcher:

The Event that will carry your information

class UpdateEntityEvent extends Event {
     private $myEntity;
     private $newValue;

     public function __construct(Entity $entity, Whatever $newValue){
         $this->myEntity = $entity;
         $this->newValue = $newValue;
    }
  // [...] getters
}

Your Listener

class UpdateMyEntityEventListener
{
    public function updateCertainProperty(UpdateMyEntityEvent $event)
    {
        // Do what you want here :D
    }
}

Some configuration

 kernel.listener.updateMyEntity:
     class: Acme\AppBundle\EventListener\UpdateMyEntityEventListener
     tags:
         - { name: kernel.event_listener, event: updateMyEntity, method: updateCertainProperty }

We avoid using some hardcoded string, let's put the event name in a constant

class MyEntityEvents 
{
    const UPDATE = 'updateMyEntity';
}

Then in your Controller

public function updateAction()
{
    // [...]
    $event = new UpdateMyEntityEvent($entity, $whatever);
    $dispatcher = $this->get('event_dispatcher')->dispatch( MyEntityEvents::UPDATE, $event);

Upvotes: 1

Related Questions