Listing out Previous/Next products in Magento (Inchoo_Prevnext extension)

Featured Image

There are several Previous/Next extensions out there for Magento by now. Honestly I do not know how any of them works as I never used any :). I decided to code my own since I wanted to tackle the challenge of coding the previous/next that works alongside the layered navigation filter and sort order in the product listing grid.

Here is an example code for the extension (partial but fully functional) I just posted few minutes ago on the Magento Connect (still waiting for approval, I’ll update the post with the link once it’s approved).

First we start of with the config.xml file of the Inchoo_Prevnext extension:

<config>
...
    <frontend>         
        <events>
            <controller_action_postdispatch>
                <observers>
                    <inchoo_prevnext_set_filtered_category_product_collection>
                        <class>inchoo_prevnext/observer</class>
                        <method>setInchooFilteredCategoryProductCollection</method>
                    </inchoo_prevnext_set_filtered_category_product_collection>
                </observers>                
            </controller_action_postdispatch>
        </events>      
    </frontend>
...
</config>

Now we implement the actual observer method. Here I am grabbing the loaded product collection, the one last looked at the category product listing page. As you can see, I’m focusing down to the “category” controller, “view” action, extracting the product id’s from collection and setting them under the inchoo_filtered_category_product_collection session key. This way when I go on the product view page I can read the last viewed products grid and then decide what is previous and what next product. Next time I’m on category product listing grid this code will simply reset the id’s to those shown in the grid.

class Inchoo_Prevnext_Model_Observer
{
    public function setInchooFilteredCategoryProductCollection()
    {
        /**
         * There might be some illogical buggy behavior when coming directly 
         * from "Related products" / "Recently viewed" products block. 
         * Nothing that should break the page however.
         */
	if (Mage::app()->getRequest()->getControllerName() == 'category' && Mage::app()->getRequest()->getActionName() == 'view') {
 
		$products = Mage::app()->getLayout()
				->getBlockSingleton('Mage_Catalog_Block_Product_List')
				->getLoadedProductCollection()
				->getColumnValues('entity_id');
 
		Mage::getSingleton('core/session')
				->setInchooFilteredCategoryProductCollection($products);
 
		unset($products);
	}
 
	return $this;        
    }
} 

And finally the helper class to host the getPreviousProduct and getNextProduct methods.

class Inchoo_Prevnext_Helper_Data extends Mage_Core_Helper_Abstract
{
    /**
     * @return Mage_Catalog_Model_Product or FALSE
     */
    public function getPreviousProduct()
    {
            $prodId = Mage::registry('current_product')->getId();
 
            $positions = Mage::getSingleton('core/session')->getInchooFilteredCategoryProductCollection();
 
            if (!$positions) {
                $positions = array_reverse(array_keys(Mage::registry('current_category')->getProductsPosition()));
            }
 
            $cpk = @array_search($prodId, $positions);
 
            $slice = array_reverse(array_slice($positions, 0, $cpk));
 
            foreach ($slice as $productId) {
                    $product = Mage::getModel('catalog/product')
                                                    ->load($productId);
 
                    if ($product && $product->getId() && $product->isVisibleInCatalog() && $product->isVisibleInSiteVisibility()) {
                            return $product;
                    }
            }
 
            return false;
    }
 
    /**
     * @return Mage_Catalog_Model_Product or FALSE
     */
    public function getNextProduct()
    {
            $prodId = Mage::registry('current_product')->getId();
 
            $positions = Mage::getSingleton('core/session')->getInchooFilteredCategoryProductCollection();
 
            if (!$positions) {
                $positions = array_reverse(array_keys(Mage::registry('current_category')->getProductsPosition()));
            }            
 
            $cpk = @array_search($prodId, $positions);
 
            $slice = array_slice($positions, $cpk + 1, count($positions));
 
            foreach ($slice as $productId) {
                    $product = Mage::getModel('catalog/product')
                                                    ->load($productId);
 
                    if ($product && $product->getId() && $product->isVisibleInCatalog() && $product->isVisibleInSiteVisibility()) {
                            return $product;
                    }
            }
 
            return false;
    }
}

There is a known “issue” with this approach, that is it does not compensate for the rest of the possible pages in the category product grid. This is due to the fact that at the moment of fetching the id’s of category listed products we still do now know the ids of the products for possible other pages. This however would require more load on the server as it requires one or two extra database queries. Additionally, there might be some illogical prev/next from time to time (like if you are landing on a product directly without ever being on some category view page within the current session, etc) 🙂 -Thus there is a room for improvement, but it’s important to know that nothing will get broken due to this.

Hope this code was helpful, and some of you find the room to improve it.


11 comments

  1. I am getting an error when using: Mage::registry(‘current_category’)->getProductsPosition();
    Is this a problem with flat catalog? Is there any way around it? I mean like every code example on the whole web only works with flat catalog disabled, but also everyone tells you to use flat tables because it is so good for performance. This is highly annoying. What is your expert opinion on that?
    Cheers

  2. Hi,

    Thank you for the module! But does it work with flat categories? Cause I get an error on getProductsPosition function.

    Thanks!

  3. Thanks for this module! Do you happen to know the correct way of adding a link to current category as well? Without it, it looks not complete.

    Thank you!

  4. HI there,
    i have a similar problem like Tyv.
    When i browse trough the navigation and/or filter with layered navigation everything works great on product-detail page.
    But if i copy the url and paste it in another browser (the direkt link to the prod-detail) … the page is loaded until col-main and then its over…
    Any tipps?

  5. Thanks for this, works as expected, but I wonder how we can also traverse the category tree structure so that when the last product is reached, it will link to the first product in an adjacent category. Could we possibly set the current category to be the ‘root’ category when getting the list of products?

  6. If the category is filtered, the plugin displays only a narrow number of products, such as “10 on the side”, the collection will contain only 10 products.
    To prevent this, and view all products in the category, add this code: Mage::register(‘current_layer’, Mage::getModel(‘catalog/layer’)); to \Inchoo\Prevnext\Model\Observer.php in line 11 above “$products = Mage::app()->getLayout() …”

  7. Hi Branko,
    Thanks for the great Module!
    I’ve installed it in Magento CE 1.4.2.0 with 2 little problems, and I hope you can helped me out here:

    First and the biggest problem is when a customer comes without a cookie, or an old cookie, and and Search something, the product-detail-site didn’t work.
    I got Search Results, but when I try to click on a founded item i get a empty page.
    But when I go first trough the Navigation, click on a item, take a look at the item, then the Search works great.

    In IE9 it dosen’t work at all, I get every time the empty page.

    With empty page I mean: All Design works until it comes to the content. There is no content.

    Any Idea?

    Thanks in advance.

    P.S.: This is the first Module that really works great with full Layered-Navigation & Filter support, thats really great!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.