TransWikia.com

How to activate product navigation in Magento 2?

Magento Asked by Morison on January 22, 2021

Magento 2 product navigation (next and previous buttons) in the product details page are not working for me. I cannot find any settings to enable this feature. I explored the template to

magento_home/vendor/magento/module-catalog/view/frontend/templates/product/view/type/default.phtml

and found the following script:

<?php
/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

?>
<?php /* @var $block MagentoCatalogBlockProductViewAbstractView */?>
<?php $_product = $block->getProduct() ?>

<?php if ($block->displayProductStockStatus()): ?>
    <?php if ($_product->isAvailable()): ?>
        <div class="stock available" title="<?php /* @escapeNotVerified */ echo __('Availability') ?>">
            <span><?php /* @escapeNotVerified */ echo __('In stock') ?></span>
        </div>
    <?php else: ?>
        <div class="stock unavailable" title="<?php /* @escapeNotVerified */ echo __('Availability') ?>">
            <span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span>
        </div>
    <?php endif; ?>
<?php endif; ?>

However, it is taking me nowhere. How to enable Next, and Previous buttons to the product details page to explore next and previous products?

I have found few tutorials on it, as this and this but they are all for Magento 1.x. I am confused whether this feature is available for Magento 2. Please confirm if you have implemented this feature successfully.

2 Answers

Just improved circlesix's block code to handle first and last items in the category and name ordered navigation (instead of product ids)

namespace XXXXXXModulesBlock;
    use   MagentoFrameworkViewElementTemplate;

class ProductNextPrevious extends Template {

public function __construct(
    TemplateContext $context,
    MagentoCatalogBlockProductViewabstractView $product,
    MagentoCatalogApiProductRepositoryInterface $productModel,
    array $data = []
    ) {
        parent::__construct($context, $data);
        $this->_product = $product;
        $this->_productModel = $productModel;
}

protected function _prepareLayout() {
    return parent::_prepareLayout();
}

protected function _getCurrentProduct() {
    return $this->_product->getProduct();
}

protected function _getCurrentCategory() {      
    $currentCategories = $this->_getCurrentProduct()->getCategoryCollection();
    foreach ($currentCategories as $currentCategory) {
        return $currentCategory;
    }
}

protected function _getProductById($id) {
    $_product = $this->_productModel->getById($id);

    return $_product;
}

public function getPreviousProductId() {
    $_categoryArray = $this->_getCurrentCategory();
    $_productCollection = $this->_getCurrentCategory()->getProductCollection()        
    ->addAttributeToSort('name')
    ->getData();

    if ($_categoryArray) {
        $_currentProductId = $this->_getCurrentProduct()->getId();

        $_currentProductCollectionId  = 0;
        foreach ($_productCollection  as  $item) {
            if ( $item['entity_id'] ==  $_currentProductId )
                break;
            $_currentProductCollectionId ++;
        }

        if ($_currentProductCollectionId == 0  )
            return false;

        $_previousProductId = $_productCollection[$_currentProductCollectionId - 1]['entity_id'];

        if ($_previousProductId) {

            return $_previousProductId;
        }

        return false;
    }

    return false;
}

public function getNextProductId() {
    $_categoryArray = $this->_getCurrentCategory();
    $_productCollection = $this->_getCurrentCategory()
    ->getProductCollection()
    ->addAttributeToSort('name')
    ->getData();


    if ($_categoryArray) {
        $_currentProductId = $this->_getCurrentProduct()->getId();

        $_currentProductCollectionId  = 0;
        foreach ($_productCollection  as  $item) {
            if ( $item['entity_id'] ==  $_currentProductId )
                break;
            $_currentProductCollectionId ++;
        }

        // last element
        if ( $_currentProductCollectionId ==  ( count ( $_productCollection) - 1 ) ) 
            return false;

        $_nextProductId = $_productCollection[$_currentProductCollectionId + 1]['entity_id'];

        $_product = $this->_productModel;

        if ($_nextProductId) {
            return $_nextProductId;
        }

        return false;
    }

    return false;
}

public function getPreviousProductUrl() {

    if ( ! $productId = $this->getPreviousProductId() )
        return false;

    if (! $product = $this->_getProductById($productId) )
        return false;

    $_previousProductUrl = $product ->getProductUrl();

    return $_previousProductUrl;
}

public function getNextProductUrl() {

    if ( ! $productId = $this->getNextProductId() )
        return false;

    $_nextProductUrl = $this->_getProductById($productId)->getProductUrl();

    return $_nextProductUrl;
}

public function getPreviousProductName() {

    if ( ! $productId = $this->getPreviousProductId() )
        return false;

    $_previousProductName = $this->_getProductById($productId )->getName();

    return $_previousProductName;
}

public function getNextProductName() {

    if ( ! $productId = $this->getNextProductId() )
        return false;

    $_nextProductName = $this->_getProductById($productId)->getName();

    return $_nextProductName;
}

public function getPreviousProductImage() {

    if ( ! $productId = $this->getPreviousProductId() )
        return false;

    $_previousProductImage = $this->_getProductById($productId)->getImage();

    return $_previousProductImage;
}

public function getNextProductImage() {

    if ( ! $productId = $this->getNextProductId() )
        return false;

    $_nextProductImage = $this->_getProductById($productId )->getImage();

    return $_nextProductImage;
}

}

Answered by Mike Bahar on January 22, 2021

Here is a way you could do this. The trick seems to be using the API instead of using the Object Manager. With the object manager you can call a 'create' method to pull in the product object and load the product with the product id, but so far most advise warns against this method. Using the API you can call the getById() method on the object and get back a unique product object each time you call it. Theoretically with should preform better then using the object manager, and if the object manager goes away at some point, you aren't re-writing code (Alan Storm goes into the issues with the object manager here: Magento 2 Object Manager).

Doing it any other way you will run into Magento's automatic singleton design and will never be able to get the link url for each product, as the first product loaded by ID with set that value and it will persist in the page.

Here is the block (in a module you would put this in {{vender-namespace}}/{{module-name}}/Block/Product.php)

<?php

namespace {{vender-namespace}}/{{module-name}}Block;
    use   MagentoFrameworkViewElementTemplate;

class Product extends Template {

    public function __construct(
        TemplateContext $context,
        MagentoCatalogBlockProductViewabstractView $product,
        MagentoCatalogApiProductRepositoryInterface $productModel,
        array $data = []
        ) {
            parent::__construct($context, $data);
            $this->_product = $product;
            $this->_productModel = $productModel;
    }

    protected function _prepareLayout() {
        return parent::_prepareLayout();
    }

    protected function _getCurrentProduct() {
        return $this->_product->getProduct();
    }

    protected function _getCurrentCategory() {      
        $currentCategories = $this->_getCurrentProduct()->getCategoryCollection();
        foreach ($currentCategories as $currentCategory) {
            return $currentCategory;
        }
    }

    protected function _getProductById($id) {
        $_product = $this->_productModel->getById($id);

        return $_product;
    }

    public function getPreviousProductId() {
        $_categoryArray = $this->_getCurrentCategory();
        $_productCollectionIds = $this->_getCurrentCategory()->getProductCollection()->getAllIds();

        if ($_categoryArray) {
            $_currentProductId = $this->_getCurrentProduct()->getId();
            $_currentProductCollectionId = array_search($_currentProductId, $_productCollectionIds);
            $_previousProductId = $_productCollectionIds[$_currentProductCollectionId - 1];

            if ($_previousProductId) {

                return $_previousProductId;
            }

            return false;
        }

        return false;
    }

    public function getNextProductId() {
        $_categoryArray = $this->_getCurrentCategory();
        $_productCollectionIds = $this->_getCurrentCategory()->getProductCollection()->getAllIds();

        if ($_categoryArray) {
            $_currentProductId = $this->_getCurrentProduct()->getId();
            $_currentProductCollectionId = array_search($_currentProductId, $_productCollectionIds);
            $_nextProductId = $_productCollectionIds[$_currentProductCollectionId + 1];

            $_product = $this->_productModel;

            if ($_nextProductId) {
                return $_nextProductId;
            }

            return false;
        }

        return false;
    }

    public function getPreviousProductUrl() {
        $_previousProductUrl = $this->_getProductById($this->getPreviousProductId())->getProductUrl();

        return $_previousProductUrl;
    }

    public function getNextProductUrl() {
        $_nextProductUrl = $this->_getProductById($this->getNextProductId())->getProductUrl();

        return $_nextProductUrl;
    }

    public function getPreviousProductName() {
        $_previousProductName = $this->_getProductById($this->getPreviousProductId())->getName();

        return $_previousProductName;
    }

    public function getNextProductName() {
        $_nextProductName = $this->_getProductById($this->getNextProductId())->getName();

        return $_nextProductName;
    }

    public function getPreviousProductImage() {
        $_previousProductImage = $this->_getProductById($this->getPreviousProductId())->getImage();

        return $_previousProductImage;
    }

    public function getNextProductImage() {
        $_nextProductImage = $this->_getProductById($this->getNextProductId())->getImage();

        return $_nextProductImage;
    }
}

For displaying this on the front end you need to make a .xml file that will display the template on the product page top (in a module you would put this in {{vender-namespace}}/{{module-name}}/view/frontend/layout/catalog_product_view.xml)

<?xml version="1.0"?>
<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="{{vender-namespace}}/{{module-name}}BlockProduct" name="custom.product.pagination" template="{{vender-namespace}}/{{module-name}}/::product/product-pagination.phtml" before="-" />
        </referenceContainer>
    </body>
</page>

And then in your phtml file (in a module you would put this in {{vender-namespace}}/{{module-name}}/view/frontend/template/product/product-pagination.phtml)

<?php

$previousProductUrl =   $block->getPreviousProductUrl();
$nextProductUrl =       $block->getNextProductUrl();
$previousProductName =  $block->getPreviousProductName();
$nextProductName =      $block->getNextProductName();
$previousProductImage = $block->getPreviousProductImage();
$nextProductImage =     $block->getNextProductImage();
$previousImage =        $this->getBaseUrl() . 'pub/media/catalog/product' . $previousProductImage;
$nextImage =            $this->getBaseUrl() . 'pub/media/catalog/product' . $nextProductImage;

?>

<div class="product-pagination">
    <div class="previous-product" style="float: left;">
        <a href="<?php echo $previousProductUrl; ?>">
            <img src="<?php echo $previousImage; ?>" alt="<?php echo $previousProductName; ?>" height="75" width="75" />
            <p><?php echo $previousProductName; ?></p>
            <button type="submit" title="Previous Product" class="action primary to-prev-product">
                <span>&larr; prev</span>
            </button>
        </a>
    </div>

    <div class="next-product" style="float: right;">
        <a href="<?php echo $nextProductUrl; ?>">
            <img src="<?php echo $nextImage; ?>" alt="<?php echo $nextProductName; ?>" height="75" width="75" />
            <p><?php echo $nextProductName; ?></p>
            <button type="submit" title="Next Product" class="action primary to-next-product">
                <span>next &rarr;</span>
            </button>
        </a>
    </div>
</div>

Of course there is a lot more you can do with this as far as theme integration and styling. But for a proof of concept, this will get you there.

Answered by circlesix on January 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP