catalogCategory = $catalogCategory; $this->collectionFactory = $categoryCollectionFactory; $this->storeManager = $storeManager; $this->layerResolver = $layerResolver; } /** * Build category tree for menu block. * * @param \Magento\Theme\Block\Html\Topmenu $subject * @param string $outermostClass * @param string $childrenWrapClass * @param int $limit * @return void * @SuppressWarnings("PMD.UnusedFormalParameter") */ public function beforeGetHtml( \Magento\Theme\Block\Html\Topmenu $subject, $outermostClass = '', $childrenWrapClass = '', $limit = 0 ) { $rootId = $this->storeManager->getStore()->getRootCategoryId(); $storeId = $this->storeManager->getStore()->getId(); /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ $collection = $this->getCategoryTree($storeId, $rootId); $currentCategory = $this->getCurrentCategory(); $mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion foreach ($collection as $category) { $categoryParentId = $category->getParentId(); if (!isset($mapping[$categoryParentId])) { $parentIds = $category->getParentIds(); foreach ($parentIds as $parentId) { if (isset($mapping[$parentId])) { $categoryParentId = $parentId; } } } /** @var Node $parentCategoryNode */ $parentCategoryNode = $mapping[$categoryParentId]; $categoryNode = new Node( $this->getCategoryAsArray( $category, $currentCategory, $category->getParentId() == $categoryParentId ), 'id', $parentCategoryNode->getTree(), $parentCategoryNode ); $parentCategoryNode->addChild($categoryNode); $mapping[$category->getId()] = $categoryNode; //add node in stack } } /** * Add list of associated identities to the top menu block for caching purposes. * * @param \Magento\Theme\Block\Html\Topmenu $subject * @return void */ public function beforeGetIdentities(\Magento\Theme\Block\Html\Topmenu $subject) { $subject->addIdentity(Category::CACHE_TAG); $rootId = $this->storeManager->getStore()->getRootCategoryId(); $storeId = $this->storeManager->getStore()->getId(); /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ $collection = $this->getCategoryTree($storeId, $rootId); $mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion foreach ($collection as $category) { if (!isset($mapping[$category->getParentId()])) { continue; } $subject->addIdentity(Category::CACHE_TAG . '_' . $category->getId()); } } /** * Get current Category from catalog layer * * @return \Magento\Catalog\Model\Category */ private function getCurrentCategory() { $catalogLayer = $this->layerResolver->get(); if (!$catalogLayer) { return null; } return $catalogLayer->getCurrentCategory(); } /** * Convert category to array * * @param \Magento\Catalog\Model\Category $category * @param \Magento\Catalog\Model\Category $currentCategory * @param bool $isParentActive * @return array */ private function getCategoryAsArray($category, $currentCategory, $isParentActive) { return [ 'name' => $category->getName(), 'id' => 'category-node-' . $category->getId(), 'url' => $this->catalogCategory->getCategoryUrl($category), 'has_active' => in_array((string)$category->getId(), explode('/', $currentCategory->getPath()), true), 'is_active' => $category->getId() == $currentCategory->getId(), 'is_category' => true, 'is_parent_active' => $isParentActive ]; } /** * Get Category Tree * * @param int $storeId * @param int $rootId * @return \Magento\Catalog\Model\ResourceModel\Category\Collection * @throws \Magento\Framework\Exception\LocalizedException */ protected function getCategoryTree($storeId, $rootId) { /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ $collection = $this->collectionFactory->create(); $collection->setStoreId($storeId); $collection->addAttributeToSelect('name'); $collection->addFieldToFilter('path', ['like' => '1/' . $rootId . '/%']); //load only from store root $collection->addAttributeToFilter('include_in_menu', 1); $collection->addIsActiveFilter(); $collection->addNavigationMaxDepthFilter(); $collection->addUrlRewriteToResult(); $collection->addOrder('level', Collection::SORT_ORDER_ASC); $collection->addOrder('position', Collection::SORT_ORDER_ASC); $collection->addOrder('parent_id', Collection::SORT_ORDER_ASC); $collection->addOrder('entity_id', Collection::SORT_ORDER_ASC); return $collection; } }