Wie kann ich eine von einem Ereignis übergebene Zeichenfolge ändern?

10

In meiner Beobachterfunktion erhalte ich eine Variable, die vom Ereignis wie folgt übergeben wird:

public function observerFunc(Varien_Event_Observer $observer)
{
    $sth = $observer->getEvent()->getSth();
}

Wenn sthes sich um ein Objekt handelt, kann ich es durch Aufrufen von Methoden ändern. Aber wie kann ich ändern, sthwenn es sich um eine einfache Zeichenfolge handelt? Ich habe folgendes ohne Erfolg versucht:

public function observerFunc(Varien_Event_Observer $observer)
{
    $sth = $observer->getEvent()->getSth();
    $observer->getEvent()->setSth('test');
    $observer->setSth('test');
}

Ich habe gerade erfahren, dass einige Ereignisse auch ein Transportobjekt übergeben, in dem die Zeichenfolge geändert werden kann (danke Alex ), das Ereignis page_block_html_topmenu_gethtml_afterjedoch nicht. Was kann ich also tun?

Das betreffende Ereignis wird wie folgt ausgelöst und ich möchte $ html ändern:

$html = $this->_getHtml($this->_menu, $childrenWrapClass);
Mage::dispatchEvent('page_block_html_topmenu_gethtml_after', array(
    'menu' => $this->_menu,
    'html' => $html
));
Simon
quelle

Antworten:

12

Das kannst du nicht.

Der Grund, warum der Transportobjektansatz funktioniert, ist, dass die Objekte von PHP Aliase / Referenzen sind . Wenn Sie ein Objekt ändern, ändern Sie das One True Object.

Die primitiven Typen von PHP (Ints, Strings, Boolesche Werte usw.) sind keine Objekte und fallen unter die PHP- Regeln für die Übergabe von Werten für Argumente. Wenn ein Magento-Modulentwickler Ihnen eine Rohzeichenfolge in einem Ereignisbeobachter übergibt

    Mage::dispatchEvent('page_block_html_topmenu_gethtml_after', array(
        'menu' => $this->_menu,
        'html' => $html
    ));

das ist ihre Art zu sagen

Sie können sich diesen Wert ansehen, aber ich möchte nicht, dass Sie ihn ändern.

Wir werden entscheiden, ob dies eine bewusste Entwurfsentscheidung ist oder ob ein Entwickler die Dinge nicht als Übung für den Leser durchdenkt.

In Bezug auf Ihre nicht gestellte Frage, wenn Sie das Hauptmenü ändern möchten, gibt es einige Ansätze, die ich verfolgen würde. Einhängen in das page_block_html_topmenu_gethtml_beforeEreignis und Ändern des menuObjekts

    Mage::dispatchEvent('page_block_html_topmenu_gethtml_before', array(
        'menu' => $this->_menu
    ));

sollte funktionieren, da _menuist ein Objekt

/**
 * Top menu data tree
 *
 * @var Varien_Data_Tree_Node
 */
protected $_menu;

Zweitens können Sie die Menügenerierungsklasse neu schreiben

public function getHtml($outermostClass = '', $childrenWrapClass = '')
{
    $html = parent::getHtml($outermostClass, $childrenWrapClass);
    //monkey with $html here to add your menu items or custom markup
    return $html;
}

Drittens können Sie Layoutaktualisierungen verwenden, um den vorhandenen oberen Menüblock zu entfernen und einen neuen Block mit einer von Ihnen erstellten benutzerdefinierten Klasse einzufügen. Ihre benutzerdefinierte Klasse würde die vorhandene Top-Menüklasse erweitern und dann neu definieren getHtml. Dies ist komplizierter, vermeidet jedoch die Probleme, die mit dem Umschreiben verbunden sind.

Alan Storm
quelle
5

Ich würde sagen, dass dies in diesem Fall ein Designfehler ist.

Objekte werden als Referenz weitergegeben, sodass sie manipuliert werden können. Zeichenfolgen werden immer kopiert. In diesem Fall kann der String also nicht innerhalb des Beobachters manipuliert werden, selbst das page_block_html_topmenu_gethtml_afterEreignis sieht für mich so aus, als ob es Ihnen die Möglichkeit geben soll, den String zu manipulieren $html.

Alex
quelle
3

Es ist möglich, die Blockausgabe über eine transportierte Zeichenfolge durch Beobachtung des core_block_abstract_to_html_afterEreignisses (Link) zu ändern . In diesem Fall wird der gerenderte Inhalt von der Blockinstanz zur Beobachterinstanz transportiert, und vor allem wird der transportierte Inhalt von der Blockklasse zurückgegeben. Beachten Sie, dass es eine wichtige Überlegung zum Zwischenspeichern gibt, die ich im Folgenden erläutert habe.

Beispiel

Da dieses Ereignis für jedes Block-Rendering ausgelöst wird , sollten Sie den Beobachter als Singleton konfigurieren und testen, ob der Blocktyp eine Instanz von ist Mage_Page_Block_Html_Topmenu.

public function manipulateTopmenuOutput(Varien_Event_Observer $obs)
{
     if ($obs->getBlock() instanceof Mage_Page_Block_Html_Topmenu){
         $initialOutput = $obs->getTransport()->getHtml();
         //e.g. $modified output = $this->yourManipulationMethod($initialOutput);
         $obs->getTransport()->setHtml($modifiedOutput);
     }
}

Ihre Manipulationslogik kann in der Beobachtungsmethode implementiert oder in einer anderen Methode im Beobachter platziert werden.

Probleme

Da es sich um eine Ausgabemanipulation handelt und der Beobachter für alle Blockrender aufgerufen wird, sollte dies nur verwendet werden, wenn das Hauptanliegen darin besteht, ein Blockumschreiben zu vermeiden. Außerdem wird der in diesem Beobachter generierte Inhalt nach dem block_htmlCache-Schreiben (über den Aufruf der Blockinstanz an _saveCache()) manipuliert , sodass Sie entweder den block_htmlEintrag im Beobachter neu zwischenspeichern müssten (etwas klebrig, da Sie entweder Reflection verwenden würden oder Duplizieren der Logik sowohl von der _saveCache()als auch von der _getSidPlaceholder()Methode und zum Schreiben des Cache-Eintrags. Wenn Sie schließlich irgendetwas in Bezug auf die Baumknotendaten manipulieren müssen, müssen Sie eine Kopie der Baumknotendaten generieren. Dies könnte theoretisch durch erfolgen den Mage_Catalog_Model_ObserverSingleton packen und den Baum davon greifen ... in der Tat sehr klebrig.

Benmarks
quelle
1
Ich hasse Magentos Implementierung des TopMenu mit der Essenz meiner Seele. Bei jeder Implementierung, die eine Anpassung der Navigation erfordert, stoße ich routinemäßig mit dem Kopf dagegen. Sie haben es sehr schwierig gemacht, diese HTML-Ausgabe unauffällig zu optimieren. Magento bekämpft Sie auf jedem Schritt des Weges.
wlvrn
Ja, das Menü ist unangemessen unflexibel, aber Sie erhalten eine Menge Funktionen, die funktionieren.
Benmarks