Taking cache invalidation further…

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.

Leave a Reply

*

twitter