join('permissions', 'role_has_permissions.permission_id', '=', 'permissions.id') ->where('role_has_permissions.role_id', $roleId) ->where('permissions.name', 'like', 'menu:%'); if ($tenantId) { $query->where('permissions.tenant_id', $tenantId); } $rolePermissions = $query->pluck('permissions.name')->toArray(); $permissions = []; foreach ($rolePermissions as $permName) { if (preg_match('/^menu:(\d+)\.(\w+)$/', $permName, $matches)) { $menuId = (int) $matches[1]; $type = $matches[2]; if (! isset($permissions[$menuId])) { $permissions[$menuId] = []; } $permissions[$menuId][$type] = true; } } return $permissions; } /** * 특정 메뉴의 특정 권한 토글 * * @param int $roleId 역할 ID * @param int $menuId 메뉴 ID * @param string $permissionType 권한 유형 * @param int|null $tenantId 테넌트 ID * @return bool 토글 후 상태 (true: 허용, false: 거부) */ public function togglePermission(int $roleId, int $menuId, string $permissionType, ?int $tenantId = null): bool { $permissionName = "menu:{$menuId}.{$permissionType}"; // 권한 생성 또는 조회 $permission = Permission::firstOrCreate( ['name' => $permissionName, 'guard_name' => 'web', 'tenant_id' => $tenantId], ['created_by' => auth()->id()] ); // 현재 권한 상태 확인 $exists = DB::table('role_has_permissions') ->where('role_id', $roleId) ->where('permission_id', $permission->id) ->exists(); if ($exists) { // 권한 제거 DB::table('role_has_permissions') ->where('role_id', $roleId) ->where('permission_id', $permission->id) ->delete(); $newValue = false; } else { // 권한 부여 DB::table('role_has_permissions')->insert([ 'role_id' => $roleId, 'permission_id' => $permission->id, ]); $newValue = true; } // 하위 메뉴에 권한 전파 $this->propagateToChildren($roleId, $menuId, $permissionType, $newValue, $tenantId); return $newValue; } /** * 하위 메뉴에 권한 전파 * * @param int $roleId 역할 ID * @param int $parentMenuId 부모 메뉴 ID * @param string $permissionType 권한 유형 * @param bool $value 권한 값 (true: 허용, false: 거부) * @param int|null $tenantId 테넌트 ID */ protected function propagateToChildren(int $roleId, int $parentMenuId, string $permissionType, bool $value, ?int $tenantId = null): void { $children = Menu::where('parent_id', $parentMenuId)->get(); foreach ($children as $child) { $permissionName = "menu:{$child->id}.{$permissionType}"; $permission = Permission::firstOrCreate( ['name' => $permissionName, 'guard_name' => 'web', 'tenant_id' => $tenantId], ['created_by' => auth()->id()] ); if ($value) { // 권한 부여 $exists = DB::table('role_has_permissions') ->where('role_id', $roleId) ->where('permission_id', $permission->id) ->exists(); if (! $exists) { DB::table('role_has_permissions')->insert([ 'role_id' => $roleId, 'permission_id' => $permission->id, ]); } } else { // 권한 제거 DB::table('role_has_permissions') ->where('role_id', $roleId) ->where('permission_id', $permission->id) ->delete(); } // 재귀적으로 하위 메뉴 처리 $this->propagateToChildren($roleId, $child->id, $permissionType, $value, $tenantId); } } /** * 모든 권한 허용 * * @param int $roleId 역할 ID * @param int|null $tenantId 테넌트 ID */ public function allowAllPermissions(int $roleId, ?int $tenantId = null): void { $query = Menu::where('is_active', 1); if ($tenantId) { $query->where('tenant_id', $tenantId); } $menus = $query->get(); foreach ($menus as $menu) { foreach ($this->permissionTypes as $type) { $permissionName = "menu:{$menu->id}.{$type}"; $permission = Permission::firstOrCreate( ['name' => $permissionName, 'guard_name' => 'web', 'tenant_id' => $tenantId], ['created_by' => auth()->id()] ); // 권한 부여 $exists = DB::table('role_has_permissions') ->where('role_id', $roleId) ->where('permission_id', $permission->id) ->exists(); if (! $exists) { DB::table('role_has_permissions')->insert([ 'role_id' => $roleId, 'permission_id' => $permission->id, ]); } } } } /** * 모든 권한 거부 * * @param int $roleId 역할 ID * @param int|null $tenantId 테넌트 ID */ public function denyAllPermissions(int $roleId, ?int $tenantId = null): void { $query = Menu::where('is_active', 1); if ($tenantId) { $query->where('tenant_id', $tenantId); } $menus = $query->get(); foreach ($menus as $menu) { foreach ($this->permissionTypes as $type) { $permissionName = "menu:{$menu->id}.{$type}"; $permission = Permission::where('name', $permissionName)->first(); if ($permission) { DB::table('role_has_permissions') ->where('role_id', $roleId) ->where('permission_id', $permission->id) ->delete(); } } } } /** * 메뉴 트리 조회 (권한 매트릭스 표시용) * * @param int|null $tenantId 테넌트 ID * @return \Illuminate\Support\Collection 메뉴 트리 */ public function getMenuTree(?int $tenantId = null): \Illuminate\Support\Collection { $query = Menu::with('parent') ->where('is_active', 1); if ($tenantId) { $query->where('tenant_id', $tenantId); } $allMenus = $query->orderBy('sort_order', 'asc') ->orderBy('id', 'asc') ->get(); // depth 계산하여 플랫한 구조로 변환 return $this->flattenMenuTree($allMenus); } /** * 트리 구조를 플랫한 배열로 변환 (depth 정보 포함) * * @param \Illuminate\Support\Collection $menus 메뉴 컬렉션 * @param int|null $parentId 부모 메뉴 ID * @param int $depth 현재 깊이 * @return \Illuminate\Support\Collection */ private function flattenMenuTree(\Illuminate\Support\Collection $menus, ?int $parentId = null, int $depth = 0): \Illuminate\Support\Collection { $result = collect(); $filteredMenus = $menus->where('parent_id', $parentId)->sortBy('sort_order'); foreach ($filteredMenus as $menu) { $menu->depth = $depth; // 자식 메뉴 존재 여부 확인 $menu->has_children = $menus->where('parent_id', $menu->id)->count() > 0; $result->push($menu); // 자식 메뉴 재귀적으로 추가 $children = $this->flattenMenuTree($menus, $menu->id, $depth + 1); $result = $result->merge($children); } return $result; } /** * 특정 역할의 활성 메뉴 권한 확인 * * @param int $roleId 역할 ID * @param int $menuId 메뉴 ID * @param string $permissionType 권한 유형 * @return bool 권한 존재 여부 */ public function hasPermission(int $roleId, int $menuId, string $permissionType): bool { $permissionName = "menu:{$menuId}.{$permissionType}"; return DB::table('role_has_permissions') ->join('permissions', 'role_has_permissions.permission_id', '=', 'permissions.id') ->where('role_has_permissions.role_id', $roleId) ->where('permissions.name', $permissionName) ->exists(); } }