PHP基于角色过滤多维对象数组菜单失效问题求助
导航栏角色过滤功能失效的排查与修复
以下是导致过滤失效的核心问题及对应修复方案:
1. 方法访问权限与参数传递错误
Menu::filterByRole被定义为private,但你在index.php中直接调用了该方法,这会触发PHP致命错误,需先将其改为public。- 方法参数
string ...$role是可变参数,调用时直接传数组['ADMIN', 'USER']会导致参数被解析为二维数组,正确调用方式是传递单个角色字符串:filterByRole('ADMIN', 'USER')。
2. 角色匹配逻辑错误
不管是Menu中过滤MenuItem,还是DropDownItem中过滤子项,你使用的in_array($item->getRole(), $role)逻辑完全错误——getRole()返回的是角色数组,而$role是字符串数组,in_array无法判断两个数组是否存在交集。正确做法是用array_intersect检查是否有共同角色。
3. DropDownItem完整过滤逻辑缺失
- 未判断
DropDownItem自身的角色权限,直接处理子项; - 子项过滤完成后,如果没有可见子项,应当把该
DropDownItem从菜单中移除。
修正后的完整代码
Menu类修正
class Menu { public const ROLE_HOST = "HOST"; public const ROLE_USER = "USER"; public const ROLE_ADMIN = "ADMIN"; /** *@param array<MenuItem|DropDownItem> */ public function __construct( private array $menuItems, private array $role = [self::ROLE_HOST] ){} public function build() { $result = "<ul class=\"nav-menu\">%s</ul>"; $items = array_map(fn ($item) => $item->build(), $this->menuItems); return sprintf($result, implode("", $items)); } public function setRole(...$role): self { $this->role = $role; return $this; } // 修改为public,同时修正参数与判断逻辑 public function filterByRole(string ...$role): self { $filtered = []; foreach ($this->menuItems as $item) { if ($item instanceof MenuItem) { // 检查当前菜单项的角色与目标角色是否有交集 if (!empty(array_intersect($item->getRole(), $role))) { $filtered[] = $item; } } if ($item instanceof DropDownItem) { // 先过滤子项 $item->filterItemsByRole(...$role); // 检查DropDownItem自身权限,且过滤后有子项才保留 if (!empty(array_intersect($item->getRole(), $role)) && !empty($item->getItems())) { $filtered[] = $item; } } } $this->menuItems = $filtered; return $this; } }
DropDownItem类修正
class DropDownItem { public function __construct( private string $title, /** * @param array<MenuItem> $menuItems */ private array $menuItems, private ?array $role = [Menu::ROLE_HOST] ){} public function getItems(): array { return $this->menuItems; } public function getRole(): array { return $this->role; } public function setRole(string ...$role): void { $this->role = $role; } public function build(): string { $result = <<<HTM <li class="menu-has-children"> <a href="">%s</a> <ul> %s </ul> HTM; $items = array_map(fn($item) => $item->build(), $this->menuItems); return sprintf( $result, $this->title, implode("", $items) ); } public function filterItemsByRole(string ...$role): self { $filtered = array_filter( $this->menuItems, fn($item) => !empty(array_intersect($item->getRole(), $role)) ); $this->menuItems = $filtered; return $this; } }
index.php调用修正
$menu = MenuFactory::build(); // 直接传递单个角色字符串,而非数组 echo $menu->filterByRole('ADMIN', 'USER')->build();
修复后效果
- 保留
Home、About、Contact(均支持ADMIN/USER角色); - 保留
Dropdown item,并仅显示Sub-item 1(ADMIN/USER可见)和Sub-item 2(USER可见); - 隐藏仅HOST可见的
Item菜单项。
内容的提问来源于stack exchange,提问作者Petr Fořt Fru-Fru
相关产品推荐
相关产品推荐

