2

I have a Megamenu extension installed replacing the default navigation menu. This however when added breaks breadcrumbs on product pages.

I have debugged by also removing both navigation and this also breaks the navigation so I believe the issue lies with default navigation being required somehow for breadcrumbs to work now. I could of sworn however this was working on 2.2.4 earlier this week as had below issue:

Issue with Magento 2.2.4 - quotation mark in product name break product pages

This i resolved however am sure did not break breadcrumbs as the time. Reverting does not fix breadcrumbs either.

How do breadcrumbs work now? They seem to load asynchronously and not sure they did before.

From what i can tell the below function in breadcrumbs.js renders the breadcrumbs:

    _render: function () {
        var html,
            crumbs = breadcrumbList,
            template = mageTemplate(tpl);

        this._decorate(crumbs);

        html = template({
            'breadcrumbs': crumbs
        });

        if (html.length) {
            $(this.element).html(html);
        }
    },

However im not sure how it grabs the data for the html variable and why it breaks when i remove the default navigation.

  • are u fixed this issue? – Mano M Jul 23 '18 at 12:59
  • I just adapted an earlier plugin i made to also fix this. This is listed in my answer. – harri Jul 23 '18 at 14:45
  • 1
    this is correct one github.com/harrigo/EverCrumbs – Mano M Jul 24 '18 at 4:29
  • Still i can't get the breadcrumbs in view page? Any idea about that? – Mano M Jul 24 '18 at 8:33
  • Yeah that is the one, that is quite odd. When did this start happening? – harri Jul 24 '18 at 8:41
7

With help of some posts on stackexchange, I found out this solution, This approach does not require you to override with di.xml

catalog_product_view.xml

<referenceContainer name="content.top">
    <block class="Vendor\Module\Block\Breadcrumbs" name="breadcrumbs" as="breadcrumbs" />
</referenceContainer>

Vendor/Module/Block/Breadcumbs.php

<?php

namespace Vendor\Module\Block;

use Magento\Catalog\Helper\Data;
use Magento\Framework\View\Element\Template\Context;
use Magento\Store\Model\Store;
use Magento\Framework\Registry;

class Breadcrumbs extends \Magento\Theme\Block\Html\Breadcrumbs
{
    /**
     * Catalog data
     *
     * @var Data
     */
    protected $_catalogData = null;

    /**
     * @param Context $context
     * @param Data $catalogData
     * @param array $data
     */
    public function __construct(Context $context, Data $catalogData, Registry $registry, array $data = [])
    {
        $this->_catalogData = $catalogData;
        $this->registry = $registry;
        parent::__construct($context, $data);
    }

    /**
     * Retrieve HTML title value separator (with space)
     *
     * @param null|string|bool|int|Store $store
     * @return string
     */
    public function getTitleSeparator($store = null)
    {
        $separator = (string)$this->_scopeConfig->getValue('catalog/seo/title_separator', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $store);
        return ' ' . $separator . ' ';
    }

    public function getCrumbs() {
        return $this->_crumbs;
    }

    /**
     * Preparing layout
     *
     * @return \Magento\Catalog\Block\Breadcrumbs
     */
    protected function _prepareLayout() {

        $title = [];
        if ($breadcrumbsBlock = $this->getLayout()->getBlock('breadcrumbs')) {
            $breadcrumbsBlock->addCrumb(
                    'home', [
                'label' => __('Home'),
                'title' => __('Go to Home Page'),
                'link' => $this->_storeManager->getStore()->getBaseUrl()
                    ]
            );
            $path = $this->_catalogData->getBreadcrumbPath();
            $product = $this->registry->registry('current_product');

            if ($product && count($path) == 1) {
                $categoryCollection = clone $product->getCategoryCollection();
                $categoryCollection->clear();
                $categoryCollection->addAttributeToSort('level', $categoryCollection::SORT_ORDER_DESC)->addAttributeToFilter('path', array('like' => "1/" . $this->_storeManager->getStore()->getRootCategoryId() . "/%"));
                $categoryCollection->setPageSize(1);
                $breadcrumbCategories = $categoryCollection->getFirstItem()->getParentCategories();

                foreach ($breadcrumbCategories as $category) {
                    $catbreadcrumb = array("label" => $category->getName(), "link" => $category->getUrl());
                    $breadcrumbsBlock->addCrumb("category" . $category->getId(), $catbreadcrumb);
                    $title[] = $category->getName();
                }
                //add current product to breadcrumb
                $prodbreadcrumb = array("label" => $product->getName(), "link" => "");
                $breadcrumbsBlock->addCrumb("product" . $product->getId(), $prodbreadcrumb);
                $title[] = $product->getName();
            } else {
                foreach ($path as $name => $breadcrumb) {
                    $breadcrumbsBlock->addCrumb($name, $breadcrumb);
                    $title[] = $breadcrumb['label'];
                }
            }
            $this->pageConfig->getTitle()->set(join($this->getTitleSeparator(), array_reverse($title)));
            return parent::_prepareLayout();
        }
        $path = $this->_catalogData->getBreadcrumbPath();
        foreach ($path as $name => $breadcrumb) {
            $title[] = $breadcrumb['label'];
        }
        $this->pageConfig->getTitle()->set(join($this->getTitleSeparator(), array_reverse($title)));
        return parent::_prepareLayout();
    }
}

breadcrumbs.phtml

<?php if ($crumbs && is_array($crumbs)) : ?>
    <div class="breadcrumbs">
        <ul class="items">
    <?php foreach ($crumbs as $crumbName => $crumbInfo) : ?>
                <li class="item <?php /* @escapeNotVerified */ echo $crumbName ?>">
                <?php if ($crumbInfo['link']) : ?>
                        <a href="<?php /* @escapeNotVerified */ echo $crumbInfo['link'] ?>" title="<?php echo $block->escapeHtml($crumbInfo['title']) ?>">
                        <?php echo $block->escapeHtml($crumbInfo['label']) ?>
                        </a>
                        <?php elseif ($crumbInfo['last']) : ?>
                        <strong><?php echo $block->escapeHtml($crumbInfo['label']) ?></strong>
                    <?php else: ?>
                        <?php echo $block->escapeHtml($crumbInfo['label']) ?>
                    <?php endif; ?>
                </li>
                <?php endforeach; ?>
        </ul>
    </div>
<?php endif; ?>
  • your solution is working perfectly:) – akgola Oct 9 '18 at 13:13
  • Only thing missing here is ordering by level and also position – Joel Davey Nov 20 '18 at 22:05
  • Hi @nuwaus could u please look into my issue magento.stackexchange.com/questions/257932/… – NIPPU Jan 17 at 8:09
  • Hi nuwaus i have tried u r answer but getting "Invalid template file: 'html/breadcrumbs.phtml' can u please suggest where i need to add breadcrumbs.phtml? – NIPPU Jan 17 at 8:21
  • @Nagaraju Kasa, In magento breadcrumbs.phtml is at this location : vendor/magento/module-theme/view/frontend/templates/html If you want to override it, then you have to put it in your theme folder and path will be as below : Magento_Theme/templates/html/breadcrumbs.phtml Hope this will help you. :) – Ronak Parmar Jan 30 at 10:22
1

Thank you it work for me ! juste some improvement to get the key in phtml like default breadcrumb

public function getCrumbs()
{
    $evercrumbs = array();

    $evercrumbs[] = array(
        'label' => 'Home',
        'title' => 'Go to Home Page',
        'link' => $this->_storeManager->getStore()->getBaseUrl(),
        'key' => 'home'
    );

    $path = $this->_catalogData->getBreadcrumbPath();
    $product = $this->registry->registry('current_product');
    $categoryCollection = clone $product->getCategoryCollection();
    $categoryCollection->clear();
    $categoryCollection->addAttributeToSort('level', $categoryCollection::SORT_ORDER_DESC)->addAttributeToFilter('path', array('like' => "1/" . $this->_storeManager->getStore()->getRootCategoryId() . "/%"));
    $categoryCollection->setPageSize(1);
    $breadcrumbCategories = $categoryCollection->getFirstItem()->getParentCategories();
    foreach ($breadcrumbCategories as $category) {
        $evercrumbs[] = array(
            'label' => $category->getName(),
            'title' => $category->getName(),
            'link' => $category->getUrl(),
            'key' => $category->getUrlKey()
        );
    }


    $evercrumbs[] = array(
            'label' => $product->getName(),
            'title' => $product->getName(),
            'link' => '',
            'key' => $product->getUrlKey()
        );

    return $evercrumbs;
}

and in phtml file replace <li class="item <?= /* @escapeNotVerified */ $crumbName ?>">

by <li class="item <?= /* @escapeNotVerified */ $crumbInfo['key'] ?>">

I also have error with the file module.xml in your github so i change it to

<?xml version="1.0"?><config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Harrigo_EverCrumbs" active="true" setup_version="1.0.1"/>

  • I will look after and update the module thanks. – harri Jun 14 '18 at 11:51
  • Thats a wiered module.xml however what was going wrong? – harri Jun 14 '18 at 12:51
1

Know that since the update the product page has its own breadcrumbs :

vendor/magento/module-catalog/view/frontend/templates/product/breadcrumbs.phtml

I have made a module to fix this:

https://github.com/CompactCodeEU/FixProductBreadcrumbs

Composer Install Instructions

composer config repositories.compact-code-fix-product-breadcrumbs git git@github.com:CompactCodeEU/FixProductBreadcrumbs.git

composer require "compactcode/fixproductbreadcrumbs:dev-master"

php bin/magento setup:upgrade
0

It looks like the braedcrumbs are now heavily reliant on the default navigation and so all custom menus may need to adapt to fix this. I think this was an attempt to fix caching of incorrect breadcrumbs.

Instead of fixing my megamenu and because the default breadcrumbs are still poor for me as when searching they never show for a product i have ripped the default breadcrumbs and replaced with my own.

public function getCrumbs()
{
    $evercrumbs = array();

    $evercrumbs[] = array(
        'label' => 'Home',
        'title' => 'Go to Home Page',
        'link' => $this->_storeManager->getStore()->getBaseUrl()
    );

    $path = $this->_catalogData->getBreadcrumbPath();
    $product = $this->registry->registry('current_product');

    $categoryCollection = clone $product->getCategoryCollection();
    $categoryCollection->clear();
    $categoryCollection->addAttributeToSort('level', $categoryCollection::SORT_ORDER_DESC)->addAttributeToFilter('path', array('like' => "1/" . $this->_storeManager->getStore()->getRootCategoryId() . "/%"));
    $categoryCollection->setPageSize(1);
    $breadcrumbCategories = $categoryCollection->getFirstItem()->getParentCategories();
    foreach ($breadcrumbCategories as $category) {
        $evercrumbs[] = array(
            'label' => $category->getName(),
            'title' => $category->getName(),
            'link' => $category->getUrl()
        );
    }

    $evercrumbs[] = array(
            'label' => $product->getName(),
            'title' => $product->getName(),
            'link' => ''
        );

    return $evercrumbs;
}

And then replaced default breadcrumbs with my own:

<?xml version="1.0"?>
<page>
    <body>
        <referenceBlock name="breadcrumbs" remove="true" />
        <referenceContainer name="page.top">
            <block class="Harrigo\EverCrumbs\Block\Evercrumbs" name="evercrumbs" as="evercrumbs" template="Harrigo_EverCrumbs::evercrumbs.phtml" />
        </referenceContainer>
    </body>
</page>

And the template for the block:

<?php $crumbs = $block->getCrumbs(); ?>
<?php if ($crumbs && is_array($crumbs)) : ?>
<div class="breadcrumbs">
    <ul class="items">
        <?php foreach ($crumbs as $crumbName => $crumbInfo) : ?>
            <li class="item <?= /* @escapeNotVerified */ $crumbName ?>">
            <?php if ($crumbInfo['link']) : ?>
                <a href="<?= /* @escapeNotVerified */ $crumbInfo['link'] ?>" title="<?= $block->escapeHtml($crumbInfo['title']) ?>"><?= $block->escapeHtml($crumbInfo['label']) ?></a>
            <?php else: ?>
                <?= $block->escapeHtml($crumbInfo['label']) ?>
            <?php endif; ?>
            </li>
        <?php endforeach; ?>
    </ul>
</div>
<?php endif; ?>

I have updated my EverCrumbs module if you wish to simply install and use this or look over the full code used:

https://github.com/harrigo/EverCrumbs

  • I have installed your extension, but breadcrumb is not correct shows when product added in multiple categories. – Akash Jul 17 '18 at 5:46
  • Yeah that is my main issue. It would be wrong on normal magento although also depending on which page it was originally cached on so is still an improvement. – harri Jul 17 '18 at 7:25
  • Hi @harri can u please help me on this issue ? magento.stackexchange.com/questions/257932/… – NIPPU Jan 17 at 8:32
  • @NagarajuKasa This looks like a similar problem mentioned in this question here. As stated i think in my question the breadcrumbs may potentially being cached and if visited from search or directly no category is present weather or not you later visit from a category these old crumbs will get stuck in FPC. Have you tried flushing cache and visiting from category to see if they work then. Do the reverse and the breadcrumbs may not work. If so try my module and all should work, – harri Jan 17 at 10:42
  • Actually i think my question is different buy my answer should solve your issue none the less. I think i did have a question once where i solve your issue and this question updated my module to work with 2.2.5 or 2.2.6 as breadcrumbs relied on having default nav – harri Jan 17 at 10:44
0
<referenceContainer name="page.top">
       <block class="Magento\Theme\Block\Html\Topmenu" name="catalog.topnav" template="html/topmenu.phtml" ttl="3600"/>
                <container name="top.container" as="topContainer" label="After Page Header Top" htmlTag="div" htmlClass="top-container"/>
                <block class="Magento\Theme\Block\Html\Breadcrumbs" name="breadcrumbs" as="breadcrumbs"/>
</referenceContainer>
0

I've found solution, approved by Magento team. Because if you use approaches with block rewrites or plugins and you have some extensions that use Breadcrumbs block or Magento\Catalog\Controller\Product\View.php, you'll have problems. So, you have to customize just app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js. So, here are full code:

    /**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define([
    'jquery',
    'Magento_Theme/js/model/breadcrumb-list'
], function ($, breadcrumbList) {
    'use strict';

    return function (widget) {
        $.widget('mage.breadcrumbs', widget, {
            options: {
                categoryUrlSuffix: '',
                useCategoryPathInUrl: false,
                product: '',
                menuContainer: '[data-action="navigation"] > ul'
            },

            /** @inheritdoc */
            _render: function () {
                this._appendCatalogCrumbs();
                this._super();
            },

            /**
             * Append category and product crumbs.
             *
             * @private
             */
            _appendCatalogCrumbs: function () {
                var categoryCrumbs = this._resolveCategoryCrumbs();

                categoryCrumbs.forEach(function (crumbInfo) {
                    breadcrumbList.push(crumbInfo);
                });

                if (this.options.product) {
                    breadcrumbList.push(this._getProductCrumb());
                }
            },

            /**
             * Resolve categories crumbs.
             *
             * @return Array
             * @private
             */
            _resolveCategoryCrumbs: function () {
                var menuItem = this._resolveCategoryMenuItem(),
                    categoryCrumbs = [];

                if (menuItem !== null && menuItem.length) {
                    categoryCrumbs.unshift(this._getCategoryCrumb(menuItem));

                    while ((menuItem = this._getParentMenuItem(menuItem)) !== null) {
                        categoryCrumbs.unshift(this._getCategoryCrumb(menuItem));
                    }
                }

                return categoryCrumbs;
            },

            /**
             * Returns crumb data.
             *
             * @param {Object} menuItem
             * @return {Object}
             * @private
             */
            _getCategoryCrumb: function (menuItem) {

                return {
                    'name': 'category',
                    'label': menuItem.text(),
                    'link': menuItem.attr('href'),
                    'title': ''
                };
            },

            /**
             * Returns product crumb.
             *
             * @return {Object}
             * @private
             */
            _getProductCrumb: function () {
                return {
                    'name': 'product',
                    'label': this.options.product,
                    'link': '',
                    'title': ''
                };
            },

            /**
             * Find parent menu item for current.
             *
             * @param {Object} menuItem
             * @return {Object|null}
             * @private
             */
            _getParentMenuItem: function (menuItem) {
                var classes,
                    classNav,
                    parentClass,
                    parentMenuItem = null;

                if (!menuItem) {
                    return null;
                }

                classes = menuItem.parent().attr('class');
                classNav = classes.match(/(nav\-)[0-9]+(\-[0-9]+)+/gi);

                if (classNav) {
                    classNav = classNav[0];
                    parentClass = classNav.substr(0, classNav.lastIndexOf('-'));

                    if (parentClass.lastIndexOf('-') !== -1) {
                        parentMenuItem = $(this.options.menuContainer).find('.' + parentClass + ' > a');
                        parentMenuItem = parentMenuItem.length ? parentMenuItem : null;
                    }
                }

                return parentMenuItem;
            },

            /**
             * Returns category menu item.
             *
             * Tries to resolve category from url or from referrer as fallback and
             * find menu item from navigation menu by category url.
             *
             * @return {Object|null}
             * @private
             */
            _resolveCategoryMenuItem: function () {
                var categoryUrl = this._resolveCategoryUrl(),
                    menu = $(this.options.menuContainer),
                    categoryMenuItem = null;

                if (categoryUrl && menu.length) {
                    categoryMenuItem = menu.find('a[href="' + categoryUrl + '"]');
                }

                return categoryMenuItem;
            },

            /**
             * Returns category url.
             *
             * @return {String}
             * @private
             */
            _resolveCategoryUrl: function () {
                var categoryUrl;

                if (this.options.useCategoryPathInUrl) {
                    // In case category path is used in product url - resolve category url from current url.
                    categoryUrl = window.location.href.split('?')[0];
                    categoryUrl = categoryUrl.substring(0, categoryUrl.lastIndexOf('/')) +
                        this.options.categoryUrlSuffix;
                } else {
                    // In other case - try to resolve it from referrer (without parameters).
                    categoryUrl = document.referrer;

                    if (categoryUrl.indexOf('?') > 0) {
                        categoryUrl = categoryUrl.substr(0, categoryUrl.indexOf('?'));
                    }
                }

                return categoryUrl;
            }
        });

        return $.mage.breadcrumbs;
    };
});

And here is a link to merge request: ENGCOM-1707

0

In my instance, I created a plugin to update the Magento\Catalog\ViewModel\Product\Breadcrumbs::getJsonConfigurationHtmlEscaped() function return value since I only needed to update the default widget options in this js file: /vendor/magento/module-catalog/view/frontend/web/js/product/breadcrumbs.js

The main issue was the default menu selectors (categoryItemSelector and menuContainer) did not match the current menu structure. Another route would be to fix the new menu structure and update the classes to match the default values. You could always do a js file override or mixin as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.