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.