Magento 1.9 – Add Custom View Mode to Category Product List via Observer

blocksevent-observermagento-1.9product-list

So I'm trying to accomplish the following: I want to add a third view mode to the product listing (I called it "gridx5") using event observers.

I originally did it via Blocks rewrites and it was working fine but I want to do it via observers now as it's conflicting with 3rd party modules depending on which website I install the module.

So here it what I've got:

config.xml

<global>
   <events>
            <core_block_abstract_to_html_before>
                <observers>
                    <extendedcatalog_toolbar_modes>
                        <type>singleton</type>
                        <class>extendedcatalog/observer</class>
                        <method>addToolbarModes</method>
                    </extendedcatalog_toolbar_modes>
                </observers>
            </core_block_abstract_to_html_before>
        </events>
</global>

In my observer, I've got this:

public function addToolbarModes(Varien_Event_Observer $observer)
    {
        $block = $observer->getEvent()->getBlock();

        if ($block instanceof Mage_Catalog_Block_Product_List) {
            switch (Mage::getStoreConfig('catalog/frontend/list_mode')) {
                case 'grid':
                    $block->setModes(array('grid' => $block->__('Grid')));
                    break;

                case 'gridx5':
                    $block->setModes(array('gridx5' => $block->__('Grid x5')));
                    break;

                case 'list':
                    $block->setModes(array('list' => $block->__('List')));
                    break;

/*** Etc for the other cases ***/
            }
        }
    }

So everything above works, but according to my tests, here is what happens.
So basically in _beforeToHtml function of the Mage_Catalog_Block_Product_List block, you've got this part of code:

if ($modes = $this->getModes()) {
            $toolbar->setModes($modes);
        }

This is where this block is supposed to set the modes of the toolbar block. Then in the setModes of the Mage_Catalog_Block_Product_List_Toolbar block:

public function setModes($modes)
    {
        if(!isset($this->_availableMode)){
            $this->_availableMode = $modes;
        }
        return $this;
    }

HERE is the problem, by default the $_availableMode is an empty array.
But that means the condition above is ALWAYS true right ?

EDIT based on questions asked:

  • Why using the setModes function on Mage_Catalog_Block_Product_List_Toolbar does not work:

In the _construct() method, the _availableModes is supposed to be set with a value like this:

switch (Mage::getStoreConfig('catalog/frontend/list_mode')) {
            case 'grid':
                $this->_availableMode = array('grid' => $this->__('Grid'));
                break;

            case 'list':
                $this->_availableMode = array('list' => $this->__('List'));
                break;

            case 'grid-list':
                $this->_availableMode = array('grid' => $this->__('Grid'), 'list' =>  $this->__('List'));
                break;

            case 'list-grid':
                $this->_availableMode = array('list' => $this->__('List'), 'grid' => $this->__('Grid'));
                break;
        }

However, as my mode value is "gridx5" is not listed in the cases of the switch, the variable keep its original value defined at the top of the file, so an empty array.

Thus when I use the setModes method, the variable is considered as set so my value is automatically skipped with the code below:

public function setModes($modes)
    {
        if(!isset($this->_availableMode)){
            $this->_availableMode = $modes;
        }
        return $this;
    }

So three questions here (EDIT: added the answers):

  • am I doing something wrong ? No, definitely was the good idea.

  • shouldn't Magento use if(empty()) instead of if (!isset()) ? Yes and No, see below.

  • can I do it WITHOUT rewriting the toolbar block NOR rewriting the setModes function ? It is actually not possible to add a new mode without rewriting the toolbar block.

Reasons behind that is the following code in the toolbar block, it is too closed to let anyone add a new mode without rewriting everything. That's why Magento uses !isset because of empty, there is no reason to use the least because no one can add a mode using the setModes.

Below is the other parts of the code (does not include the construct function as already pasted above) that I found too strict to let someone add a mode via observer:

    public function getDefaultPerPageValue()
    {
        if ($this->getCurrentMode() == 'list') {
            if ($default = $this->getDefaultListPerPage()) {
                return $default;
            }
            return Mage::getStoreConfig('catalog/frontend/list_per_page');
        }
        elseif ($this->getCurrentMode() == 'grid') {
            if ($default = $this->getDefaultGridPerPage()) {
                return $default;
            }
            return Mage::getStoreConfig('catalog/frontend/grid_per_page');
        }
        return 0;
    }

public function getAvailableLimit()
    {
        $currentMode = $this->getCurrentMode();
        if (in_array($currentMode, array('list', 'grid'))) {
            return $this->_getAvailableLimit($currentMode);
        } else {
            return $this->_defaultAvailableLimit;
        }
    }

Best Answer

Ugh, the core_block_abstract_to_html_before event. It's intentionally non-specific to prevent improper view hacks via the event system, but your use case seems acceptable. Given that it's fired for every single block render, it's correct that you are (1) using a singleton observer and (2) some refining logic. However, based on this statement...

• I did try to use the setModes function of the toolbar block

...your block type logic...

if ($block instanceof Mage_Catalog_Block_Product_List) { /*...*/}

...is incorrect for the Mage_Catalog_Block_Product_List_Toolbar block, which directly extends Mage_Core_Block_Template. You can try your latest approach using Mage_Catalog_Block_Product_List_Toolbar as a test.

Pretty sure there are different/better ways to do this, but I will give you this for now.

Related Topic