Magento 2 – Show Top Menu from 2nd Sub Category

categorycategory-treemagento-2.1menu

a client has now a shop with the following structure of categories:

 Base
   Shop
   --Menu 1
     ---Sub menu 1
     ---Sub menu 2
   --Menu 2
   --etc

Normal magento would only show "shop" in the menu.
But i would like to start skip this subcategory and shop directly Menu 1, menu 2 etc in the menu.

I could remove the shop step, but thats not good for google, and would relate in many redirects in the .htaccess because the client has many categories (100+)

Best Answer

Let's create a new module. We'll call it StackExchange_Topmenu.
you will need the following files.
app/code/StackExchange/Topmenu/registration.php - the registration file

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'StackExchange_Topmenu',
    __DIR__
);

app/code/StackExchange/Topmenu/etc/module.xml - the module declaration file

<?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="StackExchange_Topmenu" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Catalog"/>
        </sequence>
    </module>
</config>

app/code/StackExchange/Topmenu/etc/frontend/di.xml - di file to declare a plugin

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Theme\Block\Html\Topmenu">
        <plugin name="stackexchange-topmenu" type="StackExchange\Topmenu\Plugin\Topmenu" />
    </type>
</config>

app/code/StackExchange/Topmenu/Plugin/Topmenu.php - the actual plugin

<?php
namespace StackExchange\Topmenu\Plugin;

class Topmenu
{
    protected $storeManager;
    public function __construct(
        \Magento\Store\Model\StoreManagerInterface $storeManager
    )
    {
        $this->storeManager = $storeManager;
    }

    public function beforeGetHtml(
        \Magento\Theme\Block\Html\Topmenu $subject,
        $outermostClass = '',
        $childrenWrapClass = '',
        $limit = 0
    ) {
        $storeIds = [4, 5];//replace with the id of your store.
        $currentStoreId = $this->storeManager->getStore()->getId();
        if (in_array($currentStoreId, $storeIds)) {
            $menu = $subject->getMenu();
            $newMenuItems = [];
            $firstLevel = $menu->getChildren();
            foreach ($firstLevel as $menuItem) {
                /** @var \Magento\Framework\Data\Tree\Node $menuItem */
                //check if menu is a category
                if (substr($menuItem->getId(), 0, strlen('category-node-')) == 'category-node-') {
                    //get all child nodes (second level) and save them in an array
                    $subItems = $menuItem->getChildren();
                    foreach ($subItems as $subItem) {
                        $newMenuItems[] = $subItem;
                    }

                } else { //if menu item is not a category, leave it in place
                    $newMenuItems[] = $menuItem;
                }
            }
            //remove all menu items
            foreach ($firstLevel as $childNode) {
                $menu->removeChild($childNode);
            }
            //put new menu items back;
            foreach ($newMenuItems as $newMenuItem) {
                $menu->addChild($newMenuItem);
            }
        }
    }
}

now run in the console bin/magento setup:upgrade to install your module.
Notice: This will remove any top level categories from the menu. If you only have one as you describe it will remove that one. If you have 2 or more, it will remove all top level categories and add the second level categories in the menu.
If you have other menu links that are not categories they will remain unchanged.

Edit

I added an if statement inside the plugin to make it apply for a single store. Check the id of the store and replace the 1 in $storeId = 1; with what you need.
If you want to do this in a prettier way, you can add a yes/no config setting that will determine if the store should use only second level categories, and read that value in the plugin.
If not, you can leave it like this and just change the store id.