¿Cómo puedo alterar una cadena pasada por un evento?

10

En mi función de observador, obtengo una variable pasada por el evento así:

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

Si sthes un objeto, puedo alterarlo invocando métodos. Pero, ¿cómo puedo alterar sthsi es una cadena simple? Intenté lo siguiente sin éxito:

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

Acabo de enterarme de que algunos eventos también pasan un objeto de transporte en el que se puede alterar la cadena (gracias Alex ), pero el evento page_block_html_topmenu_gethtml_afterno. ¿Entonces Que puedo hacer?

El evento en cuestión se envía de esta manera y quiero alterar $ html:

$html = $this->_getHtml($this->_menu, $childrenWrapClass);
Mage::dispatchEvent('page_block_html_topmenu_gethtml_after', array(
    'menu' => $this->_menu,
    'html' => $html
));
Simón
fuente

Respuestas:

12

No puedes

La razón por la que funciona el enfoque del objeto de transporte es que los objetos de PHP son alias / referencias . Cuando modifica un objeto, está modificando el Un objeto verdadero.

Los tipos primitivos de PHP (ints, cadenas, booleanos, etc.) no son objetos, y caen bajo las reglas de paso por valor de PHP para argumentos. Si un desarrollador de módulos de Magento le pasa una cadena sin procesar en un observador de eventos

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

esa es su forma de decir

Puede ver este valor, pero no quiero que lo modifique.

Dejaremos si esta es una decisión de diseño deliberada o si un desarrollador no piensa las cosas como un ejercicio para el lector.

En cuanto a su pregunta no formulada, si desea modificar el menú superior, hay algunos enfoques que tomaría. Conectarse al page_block_html_topmenu_gethtml_beforeevento y modificar el menuobjeto

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

debería funcionar, ya que _menues un objeto

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

En segundo lugar, puede reescribir el menú que genera la clase

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

En tercer lugar, puede usar actualizaciones de diseño para eliminar el bloque de menú superior existente e insertar un nuevo bloque con una clase personalizada que haya creado. Su clase personalizada ampliaría la clase de menú superior existente y luego la redefiniría getHtml. Esto es más complicado, pero evita los problemas asociados con las reescrituras.

Alan Storm
fuente
5

Yo diría que es un error de diseño en ese caso.

Los objetos se pasan por referencia, por lo que pueden ser manipulados. Las cadenas siempre se copian. Entonces, en este caso, la cadena no se puede manipular dentro del observador, incluso el page_block_html_topmenu_gethtml_afterevento me parece que su propósito es darle la oportunidad de manipular el $html.

Alex
fuente
3

Se es posible modificar salida del bloque a través de la cadena transportado mediante la observación del core_block_abstract_to_html_afterevento (enlace) . En este caso, el contenido procesado se transporta desde la instancia de bloque a la instancia de observador y, lo más importante, el contenido transportado es lo que devuelve la clase de bloque. Tenga en cuenta que hay una consideración importante de almacenamiento en caché que he explicado debajo del ejemplo.

Ejemplo

Debido a que este evento se dispara para cada representación de bloque, debe configurar el observador como un singleton y probar que el tipo de bloque es una instancia de 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);
     }
}

Su lógica de manipulación podría implementarse en el método de observación o colocarse en otro método en el observador.

Cuestiones

Debido a que involucra la manipulación de salida y se llama al observador para todos los renders de bloque, esto solo debe usarse cuando la preocupación principal es evitar una reescritura de bloque. Además, el contenido generado en este observador se manipula después de la block_htmlescritura en caché (a través de la llamada a la instancia de bloque _saveCache()), por lo que necesitaría volver a almacenar en caché la block_htmlentrada en el observador (un poco pegajoso, ya que estaría utilizando Reflection o duplicar la lógica de los métodos _saveCache()y _getSidPlaceholder()para escribir la entrada de la memoria caché. Y, por último, si necesita manipular algo relacionado con los datos del nodo del árbol, tendría que generar una copia de los datos del nodo del árbol. En teoría, esto podría hacerse mediante agarrando el Mage_Catalog_Model_Observersingleton y agarrando el árbol ... muy pegajoso.

puntos de referencia
fuente
1
Odio la implementación de Magento del TopMenu con la esencia misma de mi alma. Rutinariamente me golpeo la cabeza durante cualquier implementación que requiera personalización de navegación. Han hecho que sea muy difícil modificar esa salida HTML de una manera discreta; Magento te combate en cada paso del camino.
wlvrn
Bueno, sí, el menú es inapropiadamente inflexible, pero obtienes bastante funcionalidad que funciona.
Benmarks