items[] = $menuItem; } /** * Get all menu items. */ public function getItems(?string $area = null): Collection { if (! $area) { throw new \Exception('Area must be provided to get menu items.'); } $configMenu = collect(config("menu.$area")); switch ($area) { case self::ADMIN: $this->configMenu = $configMenu ->filter(fn ($item) => bouncer()->hasPermission($item['key'])) ->toArray(); break; case self::CUSTOMER: $canShowWishlist = ! (bool) core()->getConfigData('customer.settings.wishlist.wishlist_option'); $canShowGdpr = ! (bool) core()->getConfigData('general.gdpr.settings.enabled'); $this->configMenu = $configMenu ->reject(fn ($item) => ($item['key'] == 'account.wishlist' && $canShowWishlist) || ($item['key'] == 'account.gdpr_data_request' && $canShowGdpr)) ->toArray(); break; default: $this->configMenu = $configMenu->toArray(); break; } if (! $this->items) { $this->prepareMenuItems(); } return collect($this->removeUnauthorizedMenuItem()) ->sortBy('sort'); } /** * Prepare menu items. */ private function prepareMenuItems(): void { $menuWithDotNotation = []; foreach ($this->configMenu as $item) { // 验证菜单项必须有必要的字段 if (!isset($item['key']) || !isset($item['name']) || !isset($item['route'])) { continue; } // 安全获取路由参数 $routeParameters = $item['route_parameters'] ?? []; // 检查是否有路由参数 if (!empty($routeParameters)) { try { $url = route($item['route'], $routeParameters); if (strpos(request()->url(), $url) !== false) { $this->currentKey = $item['key']; } } catch (\Exception $e) { // 如果路由生成失败,回退到原来的逻辑 try { if (strpos(request()->url(), route($item['route'])) !== false) { $this->currentKey = $item['key']; } } catch (\Exception $e2) { // 忽略错误 } } } else { try { if (strpos(request()->url(), route($item['route'])) !== false) { $this->currentKey = $item['key']; } } catch (\Exception $e) { // 忽略错误 } } $menuWithDotNotation[$item['key']] = $item; } $menu = Arr::undot(Arr::dot($menuWithDotNotation)); foreach ($menu as $menuItemKey => $menuItem) { // 验证顶级菜单项 if (!is_array($menuItem) || !isset($menuItem['key'])) { continue; } $subMenuItems = $this->processSubMenuItems($menuItem); // 安全解析路由参数 $routeParameters = $this->parseRouteParameters($menuItem['route_parameters'] ?? []); $this->addItem(new MenuItem( key: $menuItemKey, name: trans($menuItem['name']), route: $menuItem['route'], sort: $menuItem['sort'] ?? 0, icon: $menuItem['icon'] ?? '', children: $subMenuItems, routeParameters: $routeParameters, )); } } /** * Process sub menu items. */ private function processSubMenuItems($menuItem): Collection { return collect($menuItem) ->sortBy('sort') ->filter(fn ($value, $key) => is_array($value) && isset($value['key'])) ->map(function ($subMenuItem) { // 确保子菜单项有必要的字段 if (!isset($subMenuItem['key']) || !isset($subMenuItem['name']) || !isset($subMenuItem['route'])) { return null; } $subSubMenuItems = $this->processSubMenuItems($subMenuItem); // 安全解析子菜单的路由参数 $routeParameters = $this->parseRouteParameters($subMenuItem['route_parameters'] ?? []); return new MenuItem( key: $subMenuItem['key'], name: trans($subMenuItem['name']), route: $subMenuItem['route'], sort: $subMenuItem['sort'] ?? 0, icon: $subMenuItem['icon'] ?? '', children: $subSubMenuItems, routeParameters: $routeParameters, ); }) ->filter(); // 过滤掉 null 值 } /** * Parse route parameters from various formats. * * @param mixed $parameters * @return array */ private function parseRouteParameters($parameters): array { if (empty($parameters)) { return []; } // 如果已经是数组,直接返回 if (is_array($parameters)) { // 过滤空值 return array_values(array_filter($parameters)); } // 如果是 JSON 字符串,解码 if (is_string($parameters)) { $decoded = json_decode($parameters, true); if (is_array($decoded)) { return array_values(array_filter($decoded)); } } return []; } /** * Get current active menu. */ public function getCurrentActiveMenu(?string $area = null): ?MenuItem { $currentKey = implode('.', array_slice(explode('.', $this->currentKey), 0, 2)); return $this->findMatchingItem($this->getItems($area), $currentKey); } /** * Finding the matching item. */ private function findMatchingItem($items, $currentKey): ?MenuItem { foreach ($items as $item) { if ($item->key == $currentKey) { return $item; } if ($item->haveChildren()) { $matchingChild = $this->findMatchingItem($item->getChildren(), $currentKey); if ($matchingChild) { return $matchingChild; } } } return null; } /** * Remove unauthorized menu item. */ private function removeUnauthorizedMenuItem(): array { return collect($this->items)->map(function ($item) { $this->removeChildrenUnauthorizedMenuItem($item); return $item; })->toArray(); } /** * Remove unauthorized menuItem's children. This will handle all levels. */ private function removeChildrenUnauthorizedMenuItem(MenuItem &$menuItem): void { if ($menuItem->haveChildren()) { $firstChildrenItem = $menuItem->getChildren()->first(); $menuItem->route = $firstChildrenItem->getRoute(); $this->removeChildrenUnauthorizedMenuItem($firstChildrenItem); } } }