validatePermissions($permissions); // 插入数据库 $menu = self::toModel($data, Menu::class); self::initMenuProperty($menu); DB::beginTransaction(); try { $menu_id = $menu->create($menu->getAttributes())->id; $this->createPermissions($permissions, $menu_id); DB::commit(); } catch (Exception){ DB::rollBack(); self::error('添加失败'); } return $menu_id; } /** * @throws ApiException */ public function updateMenu($params): void { // 校验更新的菜单是否存在 !self::isExistMenu($params['id']) && self::error('MENU_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY); // 校验父菜单存在 self::validateParentMenu($params['parentId'], $params['id']); // 校验菜单(自己) self::validateMenu($params['parentId'], $params['name'], $params['id']); // 更新到数据库 $menu = self::toModel($params, Menu::class); self::initMenuProperty($menu); $menu->update($menu->getAttributes()); } /** * @throws ApiException */ public function deleteMenu($id) { // 校验是否还有子菜单 self::validateChildrenMenu($id); // 校验删除的菜单是否存在 !self::isExistMenu($id) && self::error('MENU_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY); $menu = self::toModel(['id' => $id], Menu::class); // 标记删除 return $menu->delete(); } public function getMenu($id): array { $menu = Menu::query(); return $menu->select($this->selectColumn)->find($id)->toArray(); } public function getMenuList($params = []): array { $menu = Menu::query(); isset($params['name']) && filled($params['name']) && $menu->whereLike('name', "%{$params['name']}%"); isset($params['status']) && filled($params['status']) && $menu->where('status', $params['status']); return $menu->orderBy('sort')->select($this->selectColumn)->get()->toArray(); } public function getSimpleMenuList(): array { $menus = $this->getMenuList(['status' => 0]); // list.sort(Comparator . comparing(MenuDO::getSort)); return self::filterDisableMenus($menus); } protected static function isExistMenu(int|array $condition): bool { $menu = Menu::query(); is_array($condition) && $menu->where($condition); is_numeric($condition) && $menu->where('id', $condition); return $menu->exists(); } /** * @throws ApiException */ protected static function validateChildrenMenu($parentId): void { Menu::query()->where('parent_id', $parentId)->count() && self::error('MENU_EXISTS_CHILDREN', Response::HTTP_UNPROCESSABLE_ENTITY); } /** * @throws ApiException */ protected static function validateParentMenu($parentId, $childId): void { if (!$parentId) return; // 不能设置自己为父菜单 $parentId === $childId && self::error('MENU_PARENT_ERROR', Response::HTTP_UNPROCESSABLE_ENTITY); $menu = Menu::query()->select('type')->find($parentId); // 父菜单不存在 !$menu && self::error('MENU_PARENT_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY); // 父菜单必须是目录或者菜单类型 $menu->type != MenuType::DIR && $menu->type != MenuType::MENU && self::error('MENU_PARENT_NOT_DIR_OR_MENU', Response::HTTP_UNPROCESSABLE_ENTITY); } /** * @throws ApiException */ protected static function validateMenu($parentId, $name, $id): void { $where = ['parent_id' => $parentId, 'name' => $name]; $menu = Menu::query()->where($where)->first(); if (is_null($menu)) return; // 如果 id 为空,说明不用比较是否为相同 id 的菜单 !$id && self::error('MENU_NAME_DUPLICATE', Response::HTTP_UNPROCESSABLE_ENTITY); $menu->id !== $id && self::error('MENU_NAME_DUPLICATE', Response::HTTP_UNPROCESSABLE_ENTITY); } /** * @throws ApiException */ protected function validatePermissions(array $permissions, int $menu_id = null): void { if (empty($permissions)) return; $isExists = Permission::query()->whereIn('name', $permissions)->where('guard_name', Guard::getDefaultName(static::class))->exists(); $isExists && empty($menu_id) && self::error('权限标识重复'); } protected function createPermissions(array $permissions, int $menu_id): void { if (empty($permissions)) return; $permissionsData = []; $guardName = Guard::getDefaultName(static::class); $permissionQuery = Permission::query(); foreach ($permissions as $permission) { $permissionsData[] = ['name' => $permission, 'guard_name' => $guardName]; } $permissionQuery->insert($permissionsData); $permissionIds = $permissionQuery->whereIn('name', $permissions)->pluck('id'); $this->createMenuHasPermissions($menu_id, $permissionIds->toArray()); } protected function createMenuHasPermissions(int $menu_id, array $permission_ids): void { $tableNames = config('permission.table_names'); $menuHasPermissionsData = []; $model_type = Menu::class; foreach ($permission_ids as $permission_id) { $menuHasPermissionsData[] = ['permission_id' => $permission_id, 'model_type' => $model_type, 'model_id' => $menu_id]; } DB::table($tableNames['model_has_permissions'])->insert($menuHasPermissionsData); // 存在权限并清空权限缓存 // Cache::forget(config('permission.cache.key')); } /** * 初始化菜单的通用属性。 *
* 例如说,只有目录或者菜单类型的菜单,才设置 icon * */ private static function initMenuProperty(&$menu): void { // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空 if ($menu['type'] === MenuType::BUTTON) { $menu['component'] = null; $menu['component_name'] = null; $menu['icon'] = null; $menu['path'] = null; } } private static function filterDisableMenus($menus): array { if (empty($menus)) return []; // 遍历 menu 菜单,查找不是禁用的菜单,添加到 enabledMenus 结果 $enabledMenus = []; // $disabledMenuCache = []; // 存下递归搜索过被禁用的菜单,防止重复的搜索 foreach ($menus as $menu) { if ($menu['status'] === Status::DISABLE) continue; $enabledMenus[] = collect($menu)->only(['id', 'status', 'name', 'parentId', 'type']); //['id' => $menu['id'], 'status' => $menu['status'], 'name' => $menu['name'], 'parentId' => $menu['parentId'], 'type' => $menu['type']]; } return $enabledMenus; } }