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 getMenu($id): array { $menu = Menu::query(); return $menu->select($this->selectColumn)->find($id)->toArray(); } /** * @throws ApiException */ public function createMenu(array $data) { // 校验父菜单存在 self::validateParentMenu($data['parentId'], null); // 校验菜单(自己) self::validateMenu($data['parentId'], $data['name'], null); // 校验权限标识 $permissions = isset($data['permission']) ? self::convertPermissions($data['permission']) : []; $this->validatePermissions($permissions); // 插入数据库 $menu = self::toModel($data, Menu::class); self::initMenuProperty($menu); DB::beginTransaction(); try { $menu = $menu->create($menu->getAttributes()); self::createPermissions($permissions); self::syncMenuHasPermissions($menu, $permissions); DB::commit(); return $menu->id; } catch (Exception) { DB::rollBack(); self::error('添加失败'); } } /** * @throws ApiException */ public function updateMenu($data): void { // 校验更新的菜单是否存在 !self::isExistMenu($data['id']) && self::error('MENU_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY); // 校验父菜单存在 self::validateParentMenu($data['parentId'], $data['id']); // 校验菜单(自己) self::validateMenu($data['parentId'], $data['name'], $data['id']); // 校验权限标识 $permissions = isset($data['permission']) ? self::convertPermissions($data['permission']) : []; $this->validatePermissions($permissions, $data['id']); // 更新到数据库 $menu = self::toModel($data, Menu::class); self::initMenuProperty($menu); DB::beginTransaction(); try { $menu->update($menu->getAttributes()); self::detachMenuHasPermissions($menu); self::createPermissions($permissions); self::syncMenuHasPermissions($menu, $permissions); DB::commit(); } catch (Exception) { DB::rollBack(); self::error('修改失败'); } } /** * @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); try { self::detachMenuHasPermissions($menu); // 标记删除 $res = $menu->delete(); DB::commit(); return $res; } catch (Exception) { DB::rollBack(); self::error('删除失败'); } } 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); } protected static function convertPermissions(string $permission): array { if (empty($permission)) return []; return collect(explode('|', $permission))->map(function ($item) { return trim($item); })->all(); } /** * @throws ApiException */ protected function validatePermissions(array $permissions, int $menu_id = null): void { if (empty($permissions)) return; $isExistsPermissions = Permission::query()->whereIn('name', $permissions)->where('guard_name', Guard::getDefaultName(static::class))->get(); count($isExistsPermissions) && empty($menu_id) && self::error(ErrorMessage::PERMISSION_NAME_DUPLICATE); if (empty($menu_id)) return; $menu = Menu::query()->find($menu_id); $isHas = $menu->hasAllPermissions($isExistsPermissions); !$isHas && self::error(ErrorMessage::PERMISSION_NAME_DUPLICATE); } protected static function syncMenuHasPermissions(Menu $menu, array $permissions): void { if (empty($permissions)) return; $permissions_ids = Permission::query()->whereIn('name', $permissions)->pluck('id'); empty($permissions_ids) && self::error(ErrorMessage::PERMISSION_NAME_NOT_EXISTS);; $menu->permissions()->sync($permissions_ids); } protected static function detachMenuHasPermissions(Menu $menu) { $permission_ids = $menu->permissions()->pluck('id'); Permission::query()->whereIn('id', $permission_ids)->delete(); } protected static function createPermissions(array $permissions): void { if (empty($permissions)) return; foreach ($permissions as $permissionName) { Permission::create(['name' => $permissionName]); } // $menu->givePermissionTo($permissions); // $menu->flushPermissionCache(); } /** * 初始化菜单的通用属性。 *
* 例如说,只有目录或者菜单类型的菜单,才设置 icon * */ private static function initMenuProperty(Menu &$menu): void { // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空 if ($menu['type'] === MenuType::BUTTON) { $menu['component'] = null; $menu['component_name'] = null; $menu['icon'] = null; $menu['path'] = null; } (empty($menu['permission'])) && ($menu['permission'] = 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; } }