Ereignisbeobachter programmgesteuert abmelden

7

Weiß jemand, ob es möglich ist, die Registrierung eines Ereignisbeobachters programmgesteuert aufzuheben? Ich habe einen Beobachter newsletter_subscribe_save_after, der ein benutzerdefiniertes Attribut im Kundenmodell aktualisiert, aber wenn der Kundendatensatz gespeichert wird, wird das customer_save_afterdefinierte Ereignis Mage_All.xmlausgelöst, bei dem der Newsletter-Abonnementstatus erneut gespeichert wird, was zu einer Endlosschleife führt, die dann den fälligen PHP-Rekursionsfehler auslöst 100 mal verschachteln.

Idealerweise möchte ich die Registrierung des customer_save_afterEreignisses nur dann aufheben, wenn mein Beobachter feuert.

Richard Cleverley
quelle
Ich brauche diese Frage nicht, aber ich mag sie. +1.
pspahn

Antworten:

10

Sie können versuchen, Ihr Ereignis als zu markieren, dispatchedund beim zweiten Versuch, es auszuführen, nichts tun, wenn es bereits ausgelöst wurde. Angenommen, Ihre Methode wird aufgerufen 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.
}

Der Name des Registrierungsschlüssels ist nicht wichtig. Stellen Sie nur sicher, dass Sie in beiden Fällen denselben verwenden.

Marius
quelle
Arbeitete wie ein Traum. Eine kleine Sache, die Rendite sollte $ this not this sein (Stackexchange lässt mich das nicht ändern, da es weniger als 6 Zeichen sind)
Richard Cleverley
Ich habe es repariert. Entschuldigung
Marius
Ich würde eine lokale Variable für Ihre Beobachterinstanz verwenden (vorausgesetzt, es handelt sich um einen Singleton) oder eine statische lokale Variable für die Methode selbst, anstatt die Registrierung zu verwenden.
Davidalger
@davidalger Kannst du ein bisschen erklären warum? Ich habe die Registrierung verwendet, weil sie am praktischsten ist. Gibt es irgendwelche Vorteile bei der Verwendung einer statischen Variable?
Marius
@Marius Bei der Verwendung der Registrierung wird im Wesentlichen eine globale Variable verwendet, für die keine Notwendigkeit besteht. Es ist sauberer, es in der Beobachterinstanz zu halten. Meistens semantisch, funktioniert Ihre Lösung also hervorragend. :)
Davidalger
7

Sie können ein Ereignis deregistrieren Beobachter on the fly programmatisch wie folgt:

$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');
}

Sie können die Registrierung eines Ereignisses selbst nicht aufheben. Um ein Ereignis vollständig zu deaktivieren, müssten Sie alle $branch . '/events/customer_save_after/observers'untergeordneten Elemente eines Ereignisses durchlaufen und jedes deaktivieren.

Wenn es sich nur um eine einzelne bestimmte Beobachtermethode handelt, die Sie nicht erneut ausführen möchten, können Sie den Typ festlegen singletonund dann einfach eine boolesche Eigenschaft als Flag verwenden, um zu überprüfen, ob sie bereits einmal wie folgt aufgerufen wurde:

class Your_Module_Model_Observer
{
    private $_processFlag = false;

    public function customerSaveAfter($data)
    {
        if (! $this->_processFlag) {
            $this->_processFlag = true;
            // do your thing with $customer->save()
        }
    }
}
Vinai
quelle
Das Flag zu verwenden oder es als versendet zu registrieren, scheint der geringste Aufwand zu sein. Interessant ist jedoch die Methode, um es physisch zu deaktivieren.
Richard Cleverley
1
Die Verwendung der Registrierung ähnelt fast der Verwendung einer globalen Variablen. Ich denke, die Verwaltung des Status auf der Beobachterinstanz ist besser.
Vinai
Sie haben wahrscheinlich tatsächlich recht. Es ist wahrscheinlich der bessere Weg, alles auf diese Weise im Beobachter in sich geschlossen zu halten.
Richard Cleverley
1

Hier ist eine Proof-of-Concept-Hilfsmethode, mit der ein vorhandener Beobachter umgeschaltet werden kann. Wenn Sie das Ereignis standardmäßig deaktivieren möchten, fügen <type>disabled</type>Sie es der config.xml-Definition Ihres Beobachters hinzu.

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);
    }
}

Die Funktion nutzt die Reflexion, um auf das ansonsten geschützte $_eventsEigentum von zuzugreifen Mage_Mage_Core_Model_App. Der gleiche Trick könnte verwendet werden, um zuvor undefinierte Beobachter zu injizieren.

abwechseln
quelle