Controller Action mit Events überschreiben

Veröffentlicht am 27.07.2011 von datenbrille in der Schublade Anderes | Ein Kommentar »

In meinem Projekt verwenden wir momentan einen Rewrite des Checkout Controllers um unsere Modifikation vorzunehmen. Irgendwie gefiel mir das nicht und ich habe nach einer anderen Methode gesucht. Nach einigen Hinweisen von den Webguys habe ich eine Event-Observer Idee entwickelt.

Das ganze ist möglich weil Magento in Mage_Core_Controller_Varien_Action in der Methode dispatch ein Flag abfragt ob es überhaupt einen Dispatch geben soll.

//Mage_Core_Controller_Varien_Action->dispatch()
...
if ($this->getRequest()->isDispatched()) {
    /**
     * preDispatch() didn't change the action, so we can continue
     */
    if (!$this->getFlag('', self::FLAG_NO_DISPATCH)) {
        $_profilerKey = self::PROFILER_KEY.'::'.$this->getFullActionName();
        Varien_Profiler::start($_profilerKey);
        $this->$actionMethodName();
        Varien_Profiler::stop($_profilerKey);
        Varien_Profiler::start(self::PROFILER_KEY.'::postdispatch');
        $this->postDispatch();
        Varien_Profiler::stop(self::PROFILER_KEY.'::postdispatch');
    }
}
...

Setzt man nun diesen Flag in Controller oder sonst wo, dann wird Magento nicht die Action des Controller ausführen. Damit war ich meinem Ziel schon etwas näher.
Aber wie kann ich mich in eine beliebige Controller Action einhängen? Auch hier bietet Magento einen Hook. Dieser befindet sich ebenfalls in Mage_Core_Controller_Varien_Action, aber dieses mal in der Methode preDispatch. Dort werden verschiede Events geworfen und das zu einem sehr frühen Zeitpunkt in Request-Response Zyklus von Magento. Erst dadurch ist es möglich eine Action vollständig zu überschreiben.

Mage::dispatchEvent('controller_action_predispatch', array('controller_action'=>$this));
Mage::dispatchEvent(
    'controller_action_predispatch_'.$this->getRequest()->getRouteName(),
    array('controller_action'=>$this)
);
Varien_Autoload::registerScope($this->getRequest()->getRouteName());
Mage::dispatchEvent(
    'controller_action_predispatch_'.$this->getFullActionName(),
    array('controller_action'=>$this)
);

Wie ihr seht werden hier viele verschiedene Events gefeuert. Ich kann einfach auf alle Controller preDispatches hören oder auch nur auf bestimmte. Zur Demonstration habe ich einfach mal alle Actions überschrieben.

//XML in aus der Config XML eines Moduls
<controller_action_predispatch>
  <observers>
    <updateflags>
      <type>singleton</type>
      <class>magelog_teaser/observer</class>
      <method>stopAction</method>
    </updateflags>
  </observers>
</controller_action_predispatch>

Die Methode im Oberserver macht nichts anderes als ein Hello in die Response zu schreiben. Will man nun eine Methode im Checkout überschreiben, ersetzt man das Hello durch eine JSON Rückgabe und wählt den Event weniger allgemein.

public function stopAction(Varien_Event_Observer $observer) {
        //controller aus dem event laden
        /** @var $controller Mage_Core_Controller_Varien_Action */
        $controller = $observer->getData('controller_action');
        //hier passiert die Magie, und wird der Dispatch unterbrochen
        $controller->setFlag(
             $controller->getRequest()->getActionName()
             ,Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH
             ,true
         );
        //jetzt kann man beliebigen Output erzeugen
        $controller->getResponse()->setBody("Hello");
}

Viel Spaß damit und ich werde berichten wie weit wir dieses Konzept in unserem neuen Checkout getrieben haben. Ach ja, das ganze hat bei uns auch schon einen Namen: “Das schöne Magento die()”.


Ein Kommentar zu “Controller Action mit Events überschreiben”

  1. 1 Mit PreDispatch Controller-Actions ändern | Magento eCommerce, Shop - webguys.de sprach am 09.08.2011 um 17:32:

    [...] Eine weitere (ähnliche) Möglichkeit stellt Karl Spieß in seinem Blog-Beitrag [...]