Topmenu.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Catalog\Plugin\Block;
  7. use Magento\Catalog\Model\Category;
  8. use Magento\Framework\Data\Collection;
  9. use Magento\Framework\Data\Tree\Node;
  10. /**
  11. * Plugin for top menu block
  12. */
  13. class Topmenu
  14. {
  15. /**
  16. * Catalog category
  17. *
  18. * @var \Magento\Catalog\Helper\Category
  19. */
  20. protected $catalogCategory;
  21. /**
  22. * @var \Magento\Catalog\Model\ResourceModel\Category\StateDependentCollectionFactory
  23. */
  24. private $collectionFactory;
  25. /**
  26. * @var \Magento\Store\Model\StoreManagerInterface
  27. */
  28. private $storeManager;
  29. /**
  30. * @var \Magento\Catalog\Model\Layer\Resolver
  31. */
  32. private $layerResolver;
  33. /**
  34. * Initialize dependencies.
  35. *
  36. * @param \Magento\Catalog\Helper\Category $catalogCategory
  37. * @param \Magento\Catalog\Model\ResourceModel\Category\StateDependentCollectionFactory $categoryCollectionFactory
  38. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  39. * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver
  40. */
  41. public function __construct(
  42. \Magento\Catalog\Helper\Category $catalogCategory,
  43. \Magento\Catalog\Model\ResourceModel\Category\StateDependentCollectionFactory $categoryCollectionFactory,
  44. \Magento\Store\Model\StoreManagerInterface $storeManager,
  45. \Magento\Catalog\Model\Layer\Resolver $layerResolver
  46. ) {
  47. $this->catalogCategory = $catalogCategory;
  48. $this->collectionFactory = $categoryCollectionFactory;
  49. $this->storeManager = $storeManager;
  50. $this->layerResolver = $layerResolver;
  51. }
  52. /**
  53. * Build category tree for menu block.
  54. *
  55. * @param \Magento\Theme\Block\Html\Topmenu $subject
  56. * @param string $outermostClass
  57. * @param string $childrenWrapClass
  58. * @param int $limit
  59. * @return void
  60. * @SuppressWarnings("PMD.UnusedFormalParameter")
  61. */
  62. public function beforeGetHtml(
  63. \Magento\Theme\Block\Html\Topmenu $subject,
  64. $outermostClass = '',
  65. $childrenWrapClass = '',
  66. $limit = 0
  67. ) {
  68. $rootId = $this->storeManager->getStore()->getRootCategoryId();
  69. $storeId = $this->storeManager->getStore()->getId();
  70. /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
  71. $collection = $this->getCategoryTree($storeId, $rootId);
  72. $currentCategory = $this->getCurrentCategory();
  73. $mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion
  74. foreach ($collection as $category) {
  75. $categoryParentId = $category->getParentId();
  76. if (!isset($mapping[$categoryParentId])) {
  77. $parentIds = $category->getParentIds();
  78. foreach ($parentIds as $parentId) {
  79. if (isset($mapping[$parentId])) {
  80. $categoryParentId = $parentId;
  81. }
  82. }
  83. }
  84. /** @var Node $parentCategoryNode */
  85. $parentCategoryNode = $mapping[$categoryParentId];
  86. $categoryNode = new Node(
  87. $this->getCategoryAsArray(
  88. $category,
  89. $currentCategory,
  90. $category->getParentId() == $categoryParentId
  91. ),
  92. 'id',
  93. $parentCategoryNode->getTree(),
  94. $parentCategoryNode
  95. );
  96. $parentCategoryNode->addChild($categoryNode);
  97. $mapping[$category->getId()] = $categoryNode; //add node in stack
  98. }
  99. }
  100. /**
  101. * Add list of associated identities to the top menu block for caching purposes.
  102. *
  103. * @param \Magento\Theme\Block\Html\Topmenu $subject
  104. * @return void
  105. */
  106. public function beforeGetIdentities(\Magento\Theme\Block\Html\Topmenu $subject)
  107. {
  108. $subject->addIdentity(Category::CACHE_TAG);
  109. $rootId = $this->storeManager->getStore()->getRootCategoryId();
  110. $storeId = $this->storeManager->getStore()->getId();
  111. /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
  112. $collection = $this->getCategoryTree($storeId, $rootId);
  113. $mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion
  114. foreach ($collection as $category) {
  115. if (!isset($mapping[$category->getParentId()])) {
  116. continue;
  117. }
  118. $subject->addIdentity(Category::CACHE_TAG . '_' . $category->getId());
  119. }
  120. }
  121. /**
  122. * Get current Category from catalog layer
  123. *
  124. * @return \Magento\Catalog\Model\Category
  125. */
  126. private function getCurrentCategory()
  127. {
  128. $catalogLayer = $this->layerResolver->get();
  129. if (!$catalogLayer) {
  130. return null;
  131. }
  132. return $catalogLayer->getCurrentCategory();
  133. }
  134. /**
  135. * Convert category to array
  136. *
  137. * @param \Magento\Catalog\Model\Category $category
  138. * @param \Magento\Catalog\Model\Category $currentCategory
  139. * @param bool $isParentActive
  140. * @return array
  141. */
  142. private function getCategoryAsArray($category, $currentCategory, $isParentActive)
  143. {
  144. return [
  145. 'name' => $category->getName(),
  146. 'id' => 'category-node-' . $category->getId(),
  147. 'url' => $this->catalogCategory->getCategoryUrl($category),
  148. 'has_active' => in_array((string)$category->getId(), explode('/', $currentCategory->getPath()), true),
  149. 'is_active' => $category->getId() == $currentCategory->getId(),
  150. 'is_category' => true,
  151. 'is_parent_active' => $isParentActive
  152. ];
  153. }
  154. /**
  155. * Get Category Tree
  156. *
  157. * @param int $storeId
  158. * @param int $rootId
  159. * @return \Magento\Catalog\Model\ResourceModel\Category\Collection
  160. * @throws \Magento\Framework\Exception\LocalizedException
  161. */
  162. protected function getCategoryTree($storeId, $rootId)
  163. {
  164. /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
  165. $collection = $this->collectionFactory->create();
  166. $collection->setStoreId($storeId);
  167. $collection->addAttributeToSelect('name');
  168. $collection->addFieldToFilter('path', ['like' => '1/' . $rootId . '/%']); //load only from store root
  169. $collection->addAttributeToFilter('include_in_menu', 1);
  170. $collection->addIsActiveFilter();
  171. $collection->addNavigationMaxDepthFilter();
  172. $collection->addUrlRewriteToResult();
  173. $collection->addOrder('level', Collection::SORT_ORDER_ASC);
  174. $collection->addOrder('position', Collection::SORT_ORDER_ASC);
  175. $collection->addOrder('parent_id', Collection::SORT_ORDER_ASC);
  176. $collection->addOrder('entity_id', Collection::SORT_ORDER_ASC);
  177. return $collection;
  178. }
  179. }