Does anyone know if it's possible to unregister an event observer programatically? I have an observer on newsletter_subscribe_save_after that updates a custom attribute in the customer model but when the customer record is saved it triggers off the customer_save_after event defined in Mage_All.xml which then re-saves the newsletter subscription status resulting in an infinite loop that then triggers the PHP recursion error due to nesting 100 times.

Ideally I'd like to unregister the customer_save_after event only when my observer fires.

  • I have no need for this question, but I like it. +1. – pspahn Jan 22 '15 at 21:40
up vote 10 down vote accepted

You can try to mark your event as dispatched and on the second attempt of running it just do nothing if it's already dispatched. For example let's say your method is called updateCustomer().

public function updateCustomer($observer){
    if (Mage::registry('my_event_was_dispatched') == 1){//do nothing if the event was dispatched
        return $this;
    }
    //your code here
    Mage::register('my_event_was_dispatched', 1);//mark the event as dispatched.
}

The registry key name is not important, just make sure you use the same one in both cases.

  • Worked like a dream. One small thing, the return should be $this not this (stackexchange won't let me change it as it's less than 6 characters) – Richard Cleverley Jul 10 '13 at 14:46
  • I fixed it. Sorry for that – Marius Jul 10 '13 at 20:53
  • I would use a variable local to your observer instance (assuming its a singleton) or a static var local to the method itself vs using the registry. – davidalger Jul 11 '13 at 3:06
  • @davidalger Can you explain a bit why? I used registry because It seams most handy. Are there any advantages on using a static var? – Marius Jul 11 '13 at 6:46
  • @Marius Using the registry is essentially using a global variable where there is no need to. Keeping it contained by the observer instance is cleaner. So mostly semantical, your solution works great. :) – davidalger Jul 11 '13 at 13:37

You can unregister an event observer on the fly programmatically as follows:

$area = Mage::app()->getStore()->isAdmin() ? 'adminhtml' : 'frontend';
foreach (array($area, 'global') as $branch) {
    $path = $branch . '/events/customer_save_after/observers/your_module/type';
    Mage::app()->getConfig()->setNode($path, 'disabled');
}

You can not unregister an event itself. To completely deactivate an event, you would have to loop over all children of $branch . '/events/customer_save_after/observers' and deactivate each one.

If it's just a single specific observer method you don't want to run again, you can set it's type to be singleton and then just use a boolean property as a flag to check if it already was called once as follows:

class Your_Module_Model_Observer
{
    private $_processFlag = false;

    public function customerSaveAfter($data)
    {
        if (! $this->_processFlag) {
            $this->_processFlag = true;
            // do your thing with $customer->save()
        }
    }
}
  • Using the flag or registering it as dispatched seems the least hassle, interesting to see the method to physically disable it though. – Richard Cleverley Jul 10 '13 at 14:49
  • 1
    Using the registry is almost like using a global variable, I think managing state on the observer instance is nicer. – Vinai Jul 10 '13 at 14:56
  • You are probably right actually. Keeping it all self contained in the observer in that way is probably the better way to do it. – Richard Cleverley Jul 10 '13 at 15:58

here's a proof-of-concept helper method that toggles an existing observer. if you want to have the event disabled as default, add <type>disabled</type> to your observer's config.xml definition.

public function toggleObserver($area, $event, $name, $enable)
{
    $app = Mage::app();

    // reflection on the property Mage_Core_Model_App::_events
    $class = new ReflectionClass(get_class($app));
    $property = $class->getProperty('_events');
    $property->setAccessible(true);

    // get the events
    $events = $property->getValue($app);

    // make sure the event config is loaded
    if (!isset($events[$area][$event]))
    {
        // load observers from config
        /** @see Mage_Core_Model_App::dispatchEvent() */

        $config = $app->getConfig()->getEventConfig($area, $event);
        if (!$config)
        {
            // event is not configured
            return;
        }

        // create observers array
        $observers = array();
        foreach ($config->observers->children() as $name => $values)
        {
            $observers[$name] = array(
                'type'  => (string) $values->type,
                'model' => $values->class ? (string) $values->class : $values->getClassName(),
                'method'=> (string) $values->method,
                'args'  => (array) $values->args,
            );
        }
        $events[$area][$event]['observers'] = $observers;
    }

    if ($events[$area][$event] && isset($events[$area][$event]['observers'][$name]))
    {
        // enable/disable the observer by changing its type
        $events[$area][$event]['observers'][$name]['type'] = $enable ? '' : 'disabled';

        // update the object
        $property->setValue($app, $events);
    }
}

the function makes use of reflection to access the otherwise protected $_events property of Mage_Mage_Core_Model_App. the same trick could be used to inject previously undefined observers.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.