Como posso alterar uma sequência passada por um evento?

10

Na minha função de observador, recebo uma variável passada pelo evento assim:

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

Se sthfor um objeto, posso alterá-lo chamando métodos nele. Mas como posso alterar sthse é uma string simples? Eu tentei o seguinte sem sucesso:

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

Acabei de saber que alguns eventos também passam um objeto de transporte no qual a string pode ser alterada (graças a Alex ), mas o evento page_block_html_topmenu_gethtml_afternão. Então o que eu posso fazer?

O evento em questão é despachado assim e eu quero alterar o $ html:

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

Respostas:

12

Você não pode.

A razão pela qual a abordagem de objetos de transporte funciona é que os objetos do PHP são aliases / referências . Quando você modifica um objeto, está modificando o Objeto Único Verdadeiro.

Os tipos primitivos do PHP (ints, strings, booleans, etc.) não são objetos e se enquadram nas regras de passagem por valor do PHP para argumentos. Se um desenvolvedor de módulo Magento transmitir uma string não processada em um observador de eventos

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

essa é a maneira deles de dizer

Você pode olhar para esse valor, mas não quero que você o modifique.

Vamos deixar se essa é uma decisão deliberada de design ou se um desenvolvedor não está pensando nas coisas como um exercício para o leitor.

Quanto à sua pergunta não feita, se você deseja modificar o menu superior, há algumas abordagens que eu adotaria. Conectando-se ao page_block_html_topmenu_gethtml_beforeevento e modificando o menuobjeto

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

deve funcionar, pois _menué um objeto

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

Em segundo lugar, você pode reescrever a classe geradora de menus

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

Terceiro, você pode usar as atualizações de layout para remover o bloco de menu superior existente e inserir um novo bloco com uma classe personalizada criada por você. Sua classe personalizada estenderia a classe de menu superior existente e redefiniria getHtml. Isso é mais complicado, mas evita os problemas associados à reescrita.

Alan Storm
fonte
5

Eu diria que é um bug de design nesse evento.

Os objetos são passados ​​por referência, para que possam ser manipulados. As strings sempre são copiadas. Portanto, neste caso, a string não pode ser manipulada dentro do observador, mesmo o page_block_html_topmenu_gethtml_afterevento me parece que seu objetivo é dar a você a chance de manipular o $html.

Alex
fonte
3

Ele é possível modificar saída do bloco via corda transportado através da observação do core_block_abstract_to_html_afterevento (link) . Nesse caso, o conteúdo renderizado é transportado da instância de bloco para a instância de observador e, o mais importante, o conteúdo transportado é o que é retornado pela classe de bloco. Observe que há uma consideração importante sobre armazenamento em cache que expliquei abaixo do exemplo.

Exemplo

Como esse evento é acionado para cada renderização de bloco, você deve configurar o observador como um singleton e testar se o tipo de bloco é uma instância 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);
     }
}

Sua lógica de manipulação pode ser implementada no método de observação ou colocada em outro método no observador.

Problemas

Como envolve manipulação de saída e o observador é chamado para todas as renderizações de bloco, isso só deve ser usado quando a principal preocupação é evitar a reescrita de bloco. Além disso, o conteúdo gerado nesse observador é manipulado para block_htmlgravação pós- cache (por meio da chamada da instância de bloco para _saveCache()), portanto, você precisará armazenar novamente em cache a block_htmlentrada no observador (um pouco persistente, pois você está usando o Reflection ou duplicar a lógica dos métodos _saveCache()e _getSidPlaceholder()para gravar a entrada do cache.E, finalmente, se você precisar manipular qualquer coisa relacionada aos dados do nó da árvore, terá que gerar uma cópia dos dados do nó da árvore.Este teoricamente poderia ser feito por agarrando o Mage_Catalog_Model_Observersingleton e agarrando a árvore dele ... muito pegajoso mesmo.

benmarks
fonte
1
Odeio a implementação do TopMenu pelo Magento com a essência da minha alma. Rotineiramente, bato com a cabeça durante qualquer implementação que exija personalização da navegação. Eles tornaram super difícil ajustar a saída HTML de uma maneira discreta; Magento luta com você a cada passo do caminho.
Wlvrn
Bem, sim, o menu é inapropriadamente inflexível, mas você obtém algumas funcionalidades que funcionam.
benmarks 19/09/09