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.