Jak mogę zmienić ciąg przekazany przez zdarzenie?

10

W mojej funkcji obserwatora otrzymuję zmienną przekazywaną przez takie zdarzenie:

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

Jeśli sthjest obiektem, mogę go zmienić, wywołując na nim metody. Ale jak mogę zmienić, sthjeśli jest to prosty ciąg? Próbowałem następujących bez powodzenia:

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

Właśnie dowiedziałem się, że niektóre zdarzenia przechodzą również obiekt transportowy, w którym można zmienić ciąg (dzięki Alex ), ale zdarzenie page_block_html_topmenu_gethtml_afternie. Więc co mogę zrobić?

Wydarzenie, o którym mowa, jest wysyłane w ten sposób i chcę zmienić $ html:

$html = $this->_getHtml($this->_menu, $childrenWrapClass);
Mage::dispatchEvent('page_block_html_topmenu_gethtml_after', array(
    'menu' => $this->_menu,
    'html' => $html
));
Szymon
źródło

Odpowiedzi:

12

Nie możesz

Powodem, dla którego działa podejście do obiektów transportowych, obiekty PHP, które są aliasami / referencjami . Kiedy modyfikujesz obiekt, modyfikujesz One True Object.

Prymitywne typy PHP (int, ciągi znaków, logiczne itp.) Nie są obiektami i podlegają regułom przekazywania wartości PHP dla argumentów. Jeśli programista modułu Magento przekaże ci nieprzetworzony ciąg znaków w obserwatorze zdarzeń

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

tak mówią

Możesz spojrzeć na tę wartość, ale nie chcę, żebyś ją modyfikował.

Odejdziemy od tego, czy jest to celowa decyzja projektowa, czy deweloper nie zastanawiający się nad tym, jako ćwiczenie dla czytelnika.

Jeśli chodzi o twoje pytanie bez odpowiedzi, jeśli chcesz zmodyfikować górne menu, wybrałbym kilka metod. Podłączenie do page_block_html_topmenu_gethtml_beforezdarzenia i modyfikacja menuobiektu

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

powinien działać, ponieważ _menujest przedmiotem

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

Po drugie, możesz przepisać klasę generującą menu

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

Po trzecie, możesz użyć aktualizacji układu, aby usunąć istniejący blok górnego menu i wstawić nowy blok z utworzoną przez siebie klasą niestandardową. Twoja klasa niestandardowa rozszerzy istniejącą klasę górnego menu, a następnie przedefiniuje getHtml. Jest to bardziej skomplikowane, ale pozwala uniknąć problemów związanych z przepisywaniem.

Alan Storm
źródło
5

Powiedziałbym, że to błąd projektowy w tym wydarzeniu.

Obiekty są przekazywane przez odniesienie, dzięki czemu można nimi manipulować. Ciągi zawsze są kopiowane. Tak więc w tym przypadku nie można manipulować sznurkiem w obserwatorze, nawet page_block_html_topmenu_gethtml_afterzdarzenie wydaje mi się, że jego celem jest umożliwienie ci manipulacji $html.

Alex
źródło
3

Możliwe jest zmodyfikowanie wyjścia bloku za pomocą transportowanego łańcucha, obserwując core_block_abstract_to_html_afterzdarzenie (link) . W takim przypadku renderowana treść jest transportowana z instancji bloku do instancji obserwatora, a - co najważniejsze - transportowana treść jest zwracana przez klasę bloku. Zauważ, że istnieje ważna kwestia buforowania, którą wyjaśniłem poniżej tego przykładu.

Przykład

Ponieważ to zdarzenie jest uruchamiane dla każdego renderowania bloku, należy skonfigurować obserwatora jako singletona i przetestować, czy typ bloku jest instancją 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);
     }
}

Twoja logika manipulacji może być zaimplementowana w metodzie obserwacji lub umieszczona w innej metodzie w obserwatorze.

Problemy

Ponieważ wiąże się to z manipulowaniem danymi wyjściowymi, a obserwator jest wywoływany dla wszystkich renderów bloków, należy tego używać tylko wtedy, gdy głównym problemem jest unikanie przepisywania bloków. Ponadto wygenerowaną zawartością w tym obserwatorze manipuluje się po block_htmlzapisaniu w pamięci podręcznej (poprzez wywołanie instancji bloku do _saveCache()), więc albo trzeba będzie ponownie buforować block_htmlwpis w obserwatorze (nieco lepki, ponieważ albo użyjesz Reflection lub powielanie logiki z metod _saveCache()i _getSidPlaceholder(), aby zapisać pozycję pamięci podręcznej. I wreszcie, jeśli trzeba manipulować czymkolwiek związanym z danymi węzła drzewa, należy wygenerować kopię danych węzła drzewa. Teoretycznie można to zrobić przez chwytanie Mage_Catalog_Model_Observersingletona i chwytanie z niego drzewa ... naprawdę bardzo lepkie.

zalety
źródło
1
Nienawidzę implementacji TopMenu przez Magento samą esencją mojej duszy. Rutynowo uderzam w nią głową podczas każdej implementacji, która wymaga dostosowania nawigacji. Sprawili, że bardzo trudno jest dostosować ten wynik HTML w dyskretny sposób; Magento walczy na każdym kroku.
wlvrn
Cóż, tak, menu jest nieodpowiednio nieelastyczne, ale dostajesz sporo funkcjonalności, która działa.
benmarks