Wie überschreibt man eine Magento-Observer-Methode?

juhulian hat gestern in den Kommentaren gefragt:

Laut Bug-Trucker (http://www.magentocommerce.com/bug-tracking/issue/?issue=14208) hilft es die \app\code\core\Mage\Sales\Model\Observer.php entsprechend anzupassen, aber irgendwie funktioniert das bei mir nicht. Muss man auf was bestimmtes achten, wenn man Dateien unter app/code/core/ verändert? Außer das man somit die Update-Fähigkeit kaputt macht?

Wie juhulian richtig schreibt, soll man Core-Dateien unter anderem deswegen nie überschreiben, weil man sich spätestens beim nächsten Update damit weh tut. Entweder man hat Arbeit, weil man die Dateien beim Update abgleichen muss, vergisst darauf, dass man die Datei geändert hat und zieht sich beim nächsten Update den Fehler wieder ein oder beides.

Die wichtige Frage ist also: wie kann man das Problem sonst lösen?

Magento-Observer-Methoden überschreiben

Möchte man eine Observer-Methode in Magento ändern, kann man glücklicherweise nicht nur die Original-Datei unangetastet lassen. Man kann sich sogar Klassen-Rewrites ersparen.

Warum das? In Magento bestimmt man über die Konfigurations-Datei config.xml, welche Methode welches Observers ausgeführt wird. Wir müssen daher nur dafür sorgen, dass Magento nicht die originale Methode, sondern unsere eigene Methode ausführt.

Die nötigen Schritte sind daher:

  1. Eigene Extension erstellen
  2. Die Extension abhängig von jener Extension machen, welche den Observer enthält, der überschrieben werden soll
  3. Konfiguration anpassen, so dass die eigene Methode statt der originalen aufgerufen wird
  4. Die eigene Methode schreiben

1. Eigene Extension erstellen

Das Thema habe ich bereits in einem früheren Posting („Magento: eigene Extension erstellen“) abgehandelt. Als Beispiel für juhulians Frage habe ich eine Extension „Emzee_Sales“ im community-Code-Pool erstellt.

2. Extension von der ursprünglichen Extension abhängig machen

Das ist schnell in der XML-Aktivierungsdatei definiert:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Emzee_Sales>
            <active>true</active>
            <codePool>community</codePool>
            <depends>
                <Mage_Sales />
            </depends>
        </Emzee_Sales>
    </modules>
</config>

3. Die eigene Methode statt der originalen aufrufen

Mit diesem Code kann man das bewerkstelligen:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Emzee_Sales>
            <version>0.0.1</version>
        </Emzee_Sales>
    </modules>

    <global>
        <models>
            <emzee_sales>
                <class>Emzee_Sales_Model</class>
            </emzee_sales>
        </models>
    </global>
    <frontend>
        <events>
            <sales_quote_address_collect_totals_before>
                <observers>
                    <sales_customer_validate_vat_number>
                        <class>emzee_sales/observer</class>
                        <method>changeQuoteCustomerGroupId</method>
                    </sales_customer_validate_vat_number>
                </observers>
            </sales_quote_address_collect_totals_before>
        </events>
    </frontend>
</config>

Was dabei wichtig ist:

  1. Definiert man einen Event-Observer, muss man unterhalb der „observers“-Node immer einen eindeutigen Bezeichner für den Observer vergeben. In diesem Fall ist das für den Magento-Core-Observer „sales_customer_validate_vat_number“. Da alle XML-Dateien zusammengeführt werden und meine XML-Datei aufgrund der Abhängigkeit aus Schritt 2 erst nachher gelesen wird, kann ich die ursprünglichen Werte für „class“ und „method“ überschreiben, indem ich denselben Bezeichner für den Observer angebe.
  2. Ich kann nun eine eigene Klasse und eigene Methode angeben.
  3. Mein Observer kann vollkommen unabhängig vom ursprünglichen Observer sein. Er muss ihn nicht erweitern und wir müssen auch keinen Klassen-Rewrite einführen.

4. Eigene Methode schreiben

Jetzt kann ich mich der eigentlichen Aufgabe widmen und die neue Observer-Methode schreiben:

<?php

class Emzee_Sales_Model_Observer
{
    /**
     * Handle customer VAT number if needed on collect_totals_before event of quote address
     *
     * @param Varien_Event_Observer $observer
     */
    public function changeQuoteCustomerGroupId(Varien_Event_Observer $observer)
    {
         // ...
    }
}

Fazit

Nicht die Core-Dateien überschreiben, es geht auch anders. 😉