[], # путь к разделу/пункту меню 'main' => [], # основной раздел/пункт меню 'parent' => [], # parent раздел/пункт меню 'current' => [], # активный раздел/пункт меню 'way' => '', # способ активации пункта меню ]; /** * Формируем меню для вывода * @param bool $bFindActive подсветить активный пункт меню * @param string|bool $sDefaultPath пункт меню по-умолчанию ("Главная") или FALSE * @return array */ public function buildMenu($bFindActive = true, $sDefaultPath = '/main/index') { $this->initMenu(); if ($bFindActive && !$this->activated) { # подготавливаем URL, без query (?x=1) $sURL = $this->app->request()->url(); $aURI = parse_url($this->app->request()->uri()); if (!empty($aURI['path'])) { $sURL .= $aURI['path']; } if (bff::isIndex() || $sURL == Url::to('/')) { # активируем пункт меню по-умолчанию ("Главная") if (!empty($sDefaultPath)) { $this->setActiveMenuByPath($sDefaultPath); } } else { # активируем пункт меню по URL $this->setActiveMenuByURL($sURL); } } return $this->menu; } /** * Активируем требуемый пункт меню используя путь * @param string $sPath путь например: /main/items * @param mixed $mActiveStateData данные, которые необходимо записать в активный пункт меню (['a']=>$mActiveData) * @return bool|array */ public function setActiveMenuByPath($sPath, $mActiveStateData = 1) { # проверяем путь if (empty($sPath)) { return false; } $sPath = trim($sPath, '/ '); if (empty($sPath)) { return false; } $sPath = explode('/', $sPath); if (empty($sPath)) { return false; } $this->initMenu(); $menu =& $this->menu; $menuParent = []; $i = sizeof($sPath); foreach ($sPath as $key) { if (!isset($menu[$key])) { return false; } if (--$i) { $menuParent =& $menu[$key]; $menu =& $menu[$key]['sub']; continue; } else { $menuParent['a'] = $mActiveStateData; $menu[$key]['a'] = $mActiveStateData; $this->setActivated(true, $menu[$key], 'path'); return [ 'mtitle' => $menu[$key]['mtitle'], 'mkeywords' => $menu[$key]['mkeywords'], 'mdescription' => $menu[$key]['mdescription'] ]; } } return false; } /** * Активируем пункт меню по текущему URL * @param string $sURL например: http://example.com/page.html * @param mixed $mActiveStateData данные, которые необходимо записать в активный пункт меню (['a']=$mActiveData) * @return bool */ public function setActiveMenuByURL($sURL, $mActiveStateData = 1) { if ($this->activated) { return false; } $this->initMenu(); $aParent = []; $this->findActiveByURL($this->menu, $aParent, $sURL, $mActiveStateData); return $this->activated; } /** * Помечаем заверешение активации пункта меню * @param bool $bActivated активирован * @param array $aActiveData @ref данные об активированном меню * @param string $sActivatedWay способ активации пукнта меню */ public function setActivated($bActivated = true, &$aActiveData = [], $sActivatedWay = '') { $this->activated = $bActivated; $this->activatedData['way'] = $sActivatedWay; if ($bActivated) { $this->activatedData['current'] =& $aActiveData; } else { $this->activatedData['current'] = []; } } /** * Возвращаем данные об активном пункте меню * @return array: * 'path' => путь к активному пункту меню * 'main' => основной раздел меню * 'parent' => parent-раздел * 'current' => текущий активный пункт меню */ public function getActivated() { return $this->activatedData; } /** * Возвращаем ID текущего активного пункта меню * @return int */ public function getActivatedID() { $aData = $this->getActivated(); return (!empty($aData['current']['id']) ? (int)$aData['current']['id'] : 0); } /** * Дополняем пункт меню дополнительными данными * @param string $sPath путь, например: /main/items * @param string $sDataKey ключ по которому добавляемые данные будут доступны * @param mixed $aData дополнительные данные * @return bool */ public function setMenuDataByPath($sPath, $sDataKey, $aData) { # проверяем ключ if ( empty($sDataKey) || in_array($sDataKey, [ 'id', 'pid', 'keyword', 'link', 'type', 'style', 'title', 'mtitle', 'mkeywords', 'mdescription', 'a', 'sub', ]) ) { return false; } $this->initMenu(); # проверяем путь if (empty($sPath)) { return false; } $sPath = trim($sPath, '/ '); if (empty($sPath)) { return false; } $sPath = explode('/', $sPath); if (empty($sPath)) { return false; } $menu =& $this->menu; $i = sizeof($sPath); foreach ($sPath as $key) { if (!isset($menu[$key])) { return false; } if (--$i) { $menu =& $menu[$key]['sub']; continue; } else { $menu[$key][$sDataKey] = $aData; return true; } } return false; } /** * Удаляем пункт меню по указанному пути * @param string $sPath путь, например: /main/items * @return bool */ protected function removeMenuByPath($sPath) { $this->initMenu(); # проверяем путь if (empty($sPath)) { return false; } $sPath = trim($sPath, '/ '); if (empty($sPath)) { return false; } $sPath = explode('/', $sPath); if (empty($sPath)) { return false; } $menu =& $this->menu; $i = sizeof($sPath); foreach ($sPath as $key) { if (!isset($menu[$key])) { return false; } if (--$i) { $menu =& $menu[$key]['sub']; continue; } else { unset($menu[$key]); return true; } } return false; } /** * Формируем меню (кешируем) * @param string|null $lang */ protected function initMenu($lang = null) { if ($this->menu !== false) { return; } $lang = $lang ?? $this->locale->current(); $this->menu = Cache::rememberForever('sitemap:menu:' . $lang, function () use ($lang) { return $this->prepareMenu($this->model->itemsMenu(), $lang); }); $this->replaceMacrosRecursive($this->menu, $lang); $this->checkOwnedByRecursive($this->menu); } /** * Активируем пункт меню по URL * @param array $aMenu @ref разделы/пункты меню * @param array $aParentMenu @ref parent раздел/пункт меню * @param string $sURL например: http://example.com/page.html * @param mixed $mActiveStateData данные, которые необходимо записать в активный пункт меню (['a']=$mActiveData) * @return bool */ protected function findActiveByURL(&$aMenu, &$aParentMenu = [], $sURL, $mActiveStateData = 1) { foreach ($aMenu as $k => &$v) { if ($v['type'] == static::typeMenu) { if (! empty($v['sub'])) { if ($this->findActiveByURL($v['sub'], $v, $sURL, $mActiveStateData)) { $aMenu[$k]['sub'] = $v['sub']; return true; } } } else { if (! empty($v['link']) && $v['link'] == $sURL) { $aMenu[$k]['a'] = $mActiveStateData; if (! empty($aParentMenu)) { $aParentMenu['a'] = $mActiveStateData; } $this->setActivated(true, $v, 'url'); return true; } } } unset($v); return false; } }