Doctrine Entity Event

In my first post on Single Table Inheritance I had some example code that made use of a $tax variable to demonstrate where tax is added or removed. This simplification hides some complexity of the actual implementation.

In the actual implementation I needed to have access to a TaxCalculator object in all Price entities. The issue I had was how to ensure this happens without having to remember to manually inject the TaxCalculator everywhere Price entities are retrieved or persisted.

To do this I used a Doctrine feature called Entity Listeners. The Listener functionality allows you to access entities during a number of Doctrine operations.  Once configured you are able to hook into a number of different events before or after various CRUD events, they are:

  • preFlush
  • postLoad
  • PrePersist
  • PostPersist
  • PreUpdate
  • PostUpdate
  • PostRemove
  • PreRemove

The listening function is passed the entity that triggering the event and an instance of Doctrine\Common\EventArgs. In my example i do not use the EventArgs object. It can be used to give you another way to access the entity triggering the event or an instance of Doctrine/ORM/EntityManager.

The following Doctrine YAML configuration specifies the Entity Event Listener class (Braddle\EntityListener) for the Price entities.

Braddle\Entity\Price:
  type: entity
  table: price
  inheritanceType: SINGLE_TABLE
  discriminatorColumn:
    name: type
    type: string
  discriminatorMap:
    including_tax: PriceIncludingTax
    excluding_tax: PriceExcludingTax
  id:
    id:
      type: integer
      generator:
        strategy: AUTO
  fields:
    price:
      type: float
  entityListeners:
    Braddle\EntityListener:

Here is the implementation of my Entity Event Listener (Braddle\EntityListener) this listener injects a TaxCalculator into price entities. I have only used the postLoad and postPersist functions so that i can ensure that new Prices have a TaxCalculator injected when they are saved and all existing Prices when they are retrieved from the database.

namespace Braddle;

use Braddle\Entity\Price;

class EntityListener
{

    /**
     * Fired before an object is about to be saved by doctrine
     *
     * @param PricingConfigAware $object The model to inject the price config into
     *
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     *
     * @return void
     */
    public function prePersist(Price $object)
    {
        $object->setTaxCalculator($this->getTaxCalculator());
    }

    /**
     * Fired before an object is about to be saved by doctrine
     *
     * @param PricingConfigAware $object The model to inject the price config into
     *
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     *
     * @return void
     */
    public function postLoad(Price $object)
    {
        $object->setTaxCalculator($this->getTaxCalculator());
    }

    /**
     * @return \Braddle\TaxCalculator
     */
    private function getTaxCalculator()
    {
        return new TaxCalculator(20);
    }
}

You can find the full example application using the Doctrine Entity Event in this GitHub Repository.