8

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.

1
  • I have no need for this question, but I like it. +1.
    – pspahn
    Commented Jan 22, 2015 at 21:40

3 Answers 3

10

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.

7
  • 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) Commented Jul 10, 2013 at 14:46
  • I fixed it. Sorry for that
    – Marius
    Commented Jul 10, 2013 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
    Commented Jul 11, 2013 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
    Commented Jul 11, 2013 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
    Commented Jul 11, 2013 at 13:37
7

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()
        }
    }
}
3
  • Using the flag or registering it as dispatched seems the least hassle, interesting to see the method to physically disable it though. Commented Jul 10, 2013 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
    Commented Jul 10, 2013 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. Commented Jul 10, 2013 at 15:58
1

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

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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