Someone pointed out to me (quite correctly) that while the solution I offered before would work, it wasn't testable, so I just wanted to make a quick note on how I would solve that.
The easiest solution I see, would be using the Symfony Event system.
For the sake of simplicity, I'm going to keep the listener and init code within the object. I feel it keeps maintainability better, and it allows for easy mock extension and overriding with explicit test code.
WARNING: I have not played with events yet. This is how I see it playing out, but I'm sure this steps on a few best practices. Feel free to let me know if you have a better implementation idea and I will update this page or give you a direct reference to your blog, whichever you prefer.
Here's how I see the Model looking with this change:
<?php class MyObject extends Doctrine_Record { public function setUp() { parent::setUp(); static::initEventListener(); } protected static $listenerInitialized = false; public static function initEventListener() { if (ProjectConfiguration::hasActive() && self::$listenerInitialized == false) { static::doInitEventListener(); self::$listenerInitialized = true; } } public static function doInitEventListener() { $dispatcher = ProjectConfiguration::getActive()->getEventDispatcher(); $dispatcher->connect('MyObject.object_updated', array('MyObject', 'clearCacheEntries')); } // We could force-type this to sfEvent, but we don't /actually/ look at the event object... //so there is no need to require it. public static function clearCacheEntries($event = null) { cacheAssistant::clearCachePattern('**/**/pages/index'); } private $pendingChangeNotification= false; public function preSave($event) { if ($this->isModified()) $this->pendingChangeNotification= true; } public function postSave($event) { if ($this->pendingChangeNotification && ProjectConfiguration::hasActive()) { $dispatcher = ProjectConfiguration::getActive()->getEventDispatcher(); $dispatcher->notify(new sfEvent($this, 'MyObject.object_updated')); $this->pendingChangeNotification= false; } } } |
This way, you could even attach a mock event listener by overriding doInitEventListener to attach a different processor to the event, or overriding clearCacheEntries. It does add quite a bit of complexity to the setup, but if you're concerned about testability, this would let you accomplish the same goals.