| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- <?php
- namespace Webkul\Core;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Collection;
- use Webkul\Core\Menu\MenuItem;
- class Menu
- {
- /**
- * Menu items.
- */
- private array $items = [];
- /**
- * Config menu.
- */
- private array $configMenu = [];
- /**
- * Contains current item key.
- */
- private string $currentKey = '';
- /**
- * Menu area for admin.
- */
- const ADMIN = 'admin';
- /**
- * Menu area for customer.
- */
- const CUSTOMER = 'customer';
- /**
- * Add a new menu item.
- */
- public function addItem(MenuItem $menuItem): void
- {
- $this->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);
- }
- }
- }
|