123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Theme\Block\Html;
- use Magento\Framework\Data\Tree\Node;
- use Magento\Framework\Data\Tree\NodeFactory;
- use Magento\Framework\Data\TreeFactory;
- use Magento\Framework\DataObject\IdentityInterface;
- use Magento\Framework\View\Element\Template;
- /**
- * Html page top menu block
- *
- * @api
- * @since 100.0.2
- */
- class Topmenu extends Template implements IdentityInterface
- {
- /**
- * Cache identities
- *
- * @var array
- */
- protected $identities = [];
- /**
- * Top menu data tree
- *
- * @var \Magento\Framework\Data\Tree\Node
- */
- protected $_menu;
- /**
- * @var NodeFactory
- */
- private $nodeFactory;
- /**
- * @var TreeFactory
- */
- private $treeFactory;
- /**
- * @param Template\Context $context
- * @param NodeFactory $nodeFactory
- * @param TreeFactory $treeFactory
- * @param array $data
- */
- public function __construct(
- Template\Context $context,
- NodeFactory $nodeFactory,
- TreeFactory $treeFactory,
- array $data = []
- ) {
- parent::__construct($context, $data);
- $this->nodeFactory = $nodeFactory;
- $this->treeFactory = $treeFactory;
- }
- /**
- * Get block cache life time
- *
- * @return int
- * @since 100.1.0
- */
- protected function getCacheLifetime()
- {
- return parent::getCacheLifetime() ?: 3600;
- }
- /**
- * Get top menu html
- *
- * @param string $outermostClass
- * @param string $childrenWrapClass
- * @param int $limit
- * @return string
- */
- public function getHtml($outermostClass = '', $childrenWrapClass = '', $limit = 0)
- {
- $this->_eventManager->dispatch(
- 'page_block_html_topmenu_gethtml_before',
- ['menu' => $this->getMenu(), 'block' => $this, 'request' => $this->getRequest()]
- );
- $this->getMenu()->setOutermostClass($outermostClass);
- $this->getMenu()->setChildrenWrapClass($childrenWrapClass);
- $html = $this->_getHtml($this->getMenu(), $childrenWrapClass, $limit);
- $transportObject = new \Magento\Framework\DataObject(['html' => $html]);
- $this->_eventManager->dispatch(
- 'page_block_html_topmenu_gethtml_after',
- ['menu' => $this->getMenu(), 'transportObject' => $transportObject]
- );
- $html = $transportObject->getHtml();
- return $html;
- }
- /**
- * Count All Subnavigation Items
- *
- * @param \Magento\Backend\Model\Menu $items
- * @return int
- */
- protected function _countItems($items)
- {
- $total = $items->count();
- foreach ($items as $item) {
- /** @var $item \Magento\Backend\Model\Menu\Item */
- if ($item->hasChildren()) {
- $total += $this->_countItems($item->getChildren());
- }
- }
- return $total;
- }
- /**
- * Building Array with Column Brake Stops
- *
- * @param \Magento\Backend\Model\Menu $items
- * @param int $limit
- * @return array|void
- *
- * @todo: Add Depth Level limit, and better logic for columns
- */
- protected function _columnBrake($items, $limit)
- {
- $total = $this->_countItems($items);
- if ($total <= $limit) {
- return;
- }
- $result[] = ['total' => $total, 'max' => (int)ceil($total / ceil($total / $limit))];
- $count = 0;
- $firstCol = true;
- foreach ($items as $item) {
- $place = $this->_countItems($item->getChildren()) + 1;
- $count += $place;
- if ($place >= $limit) {
- $colbrake = !$firstCol;
- $count = 0;
- } elseif ($count >= $limit) {
- $colbrake = !$firstCol;
- $count = $place;
- } else {
- $colbrake = false;
- }
- $result[] = ['place' => $place, 'colbrake' => $colbrake];
- $firstCol = false;
- }
- return $result;
- }
- /**
- * Add sub menu HTML code for current menu item
- *
- * @param \Magento\Framework\Data\Tree\Node $child
- * @param string $childLevel
- * @param string $childrenWrapClass
- * @param int $limit
- * @return string HTML code
- */
- protected function _addSubMenu($child, $childLevel, $childrenWrapClass, $limit)
- {
- $html = '';
- if (!$child->hasChildren()) {
- return $html;
- }
- $colStops = [];
- if ($childLevel == 0 && $limit) {
- $colStops = $this->_columnBrake($child->getChildren(), $limit);
- }
- $html .= '<ul class="level' . $childLevel . ' ' . $childrenWrapClass . '">';
- $html .= $this->_getHtml($child, $childrenWrapClass, $limit, $colStops);
- $html .= '</ul>';
- return $html;
- }
- /**
- * Recursively generates top menu html from data that is specified in $menuTree
- *
- * @param \Magento\Framework\Data\Tree\Node $menuTree
- * @param string $childrenWrapClass
- * @param int $limit
- * @param array $colBrakes
- * @return string
- *
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- */
- protected function _getHtml(
- \Magento\Framework\Data\Tree\Node $menuTree,
- $childrenWrapClass,
- $limit,
- array $colBrakes = []
- ) {
- $html = '';
- $children = $menuTree->getChildren();
- $parentLevel = $menuTree->getLevel();
- $childLevel = $parentLevel === null ? 0 : $parentLevel + 1;
- $counter = 1;
- $itemPosition = 1;
- $childrenCount = $children->count();
- $parentPositionClass = $menuTree->getPositionClass();
- $itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-';
- /** @var \Magento\Framework\Data\Tree\Node $child */
- foreach ($children as $child) {
- if ($childLevel === 0 && $child->getData('is_parent_active') === false) {
- continue;
- }
- $child->setLevel($childLevel);
- $child->setIsFirst($counter == 1);
- $child->setIsLast($counter == $childrenCount);
- $child->setPositionClass($itemPositionClassPrefix . $counter);
- $outermostClassCode = '';
- $outermostClass = $menuTree->getOutermostClass();
- if ($childLevel == 0 && $outermostClass) {
- $outermostClassCode = ' class="' . $outermostClass . '" ';
- $currentClass = $child->getClass();
- if (empty($currentClass)) {
- $child->setClass($outermostClass);
- } else {
- $child->setClass($currentClass . ' ' . $outermostClass);
- }
- }
- if (is_array($colBrakes) && count($colBrakes) && $colBrakes[$counter]['colbrake']) {
- $html .= '</ul></li><li class="column"><ul>';
- }
- $html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';
- $html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml(
- $child->getName()
- ) . '</span></a>' . $this->_addSubMenu(
- $child,
- $childLevel,
- $childrenWrapClass,
- $limit
- ) . '</li>';
- $itemPosition++;
- $counter++;
- }
- if (is_array($colBrakes) && count($colBrakes) && $limit) {
- $html = '<li class="column"><ul>' . $html . '</ul></li>';
- }
- return $html;
- }
- /**
- * Generates string with all attributes that should be present in menu item element
- *
- * @param \Magento\Framework\Data\Tree\Node $item
- * @return string
- */
- protected function _getRenderedMenuItemAttributes(\Magento\Framework\Data\Tree\Node $item)
- {
- $html = '';
- $attributes = $this->_getMenuItemAttributes($item);
- foreach ($attributes as $attributeName => $attributeValue) {
- $html .= ' ' . $attributeName . '="' . str_replace('"', '\"', $attributeValue) . '"';
- }
- return $html;
- }
- /**
- * Returns array of menu item's attributes
- *
- * @param \Magento\Framework\Data\Tree\Node $item
- * @return array
- */
- protected function _getMenuItemAttributes(\Magento\Framework\Data\Tree\Node $item)
- {
- $menuItemClasses = $this->_getMenuItemClasses($item);
- return ['class' => implode(' ', $menuItemClasses)];
- }
- /**
- * Returns array of menu item's classes
- *
- * @param \Magento\Framework\Data\Tree\Node $item
- * @return array
- */
- protected function _getMenuItemClasses(\Magento\Framework\Data\Tree\Node $item)
- {
- $classes = [];
- $classes[] = 'level' . $item->getLevel();
- $classes[] = $item->getPositionClass();
- if ($item->getIsCategory()) {
- $classes[] = 'category-item';
- }
- if ($item->getIsFirst()) {
- $classes[] = 'first';
- }
- if ($item->getIsActive()) {
- $classes[] = 'active';
- } elseif ($item->getHasActive()) {
- $classes[] = 'has-active';
- }
- if ($item->getIsLast()) {
- $classes[] = 'last';
- }
- if ($item->getClass()) {
- $classes[] = $item->getClass();
- }
- if ($item->hasChildren()) {
- $classes[] = 'parent';
- }
- return $classes;
- }
- /**
- * Add identity
- *
- * @param string|array $identity
- * @return void
- */
- public function addIdentity($identity)
- {
- if (!in_array($identity, $this->identities)) {
- $this->identities[] = $identity;
- }
- }
- /**
- * Get identities
- *
- * @return array
- */
- public function getIdentities()
- {
- return $this->identities;
- }
- /**
- * Get tags array for saving cache
- *
- * @return array
- * @since 100.1.0
- */
- protected function getCacheTags()
- {
- return array_merge(parent::getCacheTags(), $this->getIdentities());
- }
- /**
- * Get menu object.
- *
- * Creates \Magento\Framework\Data\Tree\Node root node object.
- * The creation logic was moved from class constructor into separate method.
- *
- * @return Node
- * @since 100.1.0
- */
- public function getMenu()
- {
- if (!$this->_menu) {
- $this->_menu = $this->nodeFactory->create(
- [
- 'data' => [],
- 'idField' => 'root',
- 'tree' => $this->treeFactory->create()
- ]
- );
- }
- return $this->_menu;
- }
- }
|