'roles', 'title' => 'Роли и права', 'roles' => Role::query() ->withCount(['users' => fn ($query) => $query->withTrashed()]) ->orderBy('sort') ->orderBy('name') ->get(), ]); } public function create(): View { return view('admin.roles.edit', $this->formData(new Role(), [])); } public function store(Request $request, AccessService $accessService): RedirectResponse { $validated = $this->validateRole($request); $role = DB::transaction(function () use ($validated, $request, $accessService) { $role = Role::query()->create([ 'slug' => $validated['slug'], 'name' => $validated['name'], 'description' => $validated['description'] ?? null, 'is_system' => false, 'is_active' => (bool) ($validated['is_active'] ?? true), 'sort' => (int) ($validated['sort'] ?? 100), ]); $this->syncRolePermissions($role, $request->input('permission_effects', [])); $accessService->bumpCacheVersion(); return $role; }); return redirect() ->route('admin.roles.edit', $role) ->with('success', 'Роль создана.'); } public function storeFromUser(User $user, Request $request, AccessService $accessService): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'min:2', 'max:255'], 'slug' => ['nullable', 'string', 'alpha_dash', 'max:80', 'unique:roles,slug'], 'description' => ['nullable', 'string', 'max:1000'], ]); $slug = $validated['slug'] ?: Str::slug($validated['name']); if (!$slug) { $slug = 'role_' . $user->id; } $baseSlug = $slug; $counter = 2; while (Role::query()->where('slug', $slug)->exists()) { $slug = $baseSlug . '_' . $counter++; } $role = DB::transaction(function () use ($validated, $slug, $user, $accessService) { $role = Role::query()->create([ 'slug' => $slug, 'name' => $validated['name'], 'description' => $validated['description'] ?? 'Создана из совокупности прав пользователя ' . $user->name, 'is_system' => false, 'is_active' => true, 'sort' => 100, ]); $sync = []; foreach ($accessService->getEffectivePermissions($user) as $permissionSlug => $effect) { $sync[$permissionSlug] = $effect; } $this->syncRolePermissions($role, $sync); $accessService->bumpCacheVersion(); return $role; }); return redirect() ->route('admin.roles.edit', $role) ->with('success', 'Роль создана из прав пользователя.'); } public function edit(Role $role): View { $effects = $role->permissions() ->pluck('role_permissions.effect', 'permissions.id') ->all(); return view('admin.roles.edit', $this->formData($role, $effects)); } public function update(Role $role, Request $request, AccessService $accessService): RedirectResponse { $validated = $this->validateRole($request, $role); DB::transaction(function () use ($role, $validated, $request, $accessService) { $role->update([ 'name' => $validated['name'], 'description' => $validated['description'] ?? null, 'is_active' => $role->slug === Role::ADMIN ? true : (bool) ($validated['is_active'] ?? false), 'sort' => (int) ($validated['sort'] ?? $role->sort), ]); if ($role->slug === Role::ADMIN) { $this->syncRolePermissions( $role, Permission::query()->pluck('slug')->mapWithKeys(fn (string $slug): array => [$slug => 'allow'])->all() ); } else { $this->syncRolePermissions($role, $request->input('permission_effects', [])); } $accessService->bumpCacheVersion(); }); return redirect() ->route('admin.roles.edit', $role) ->with('success', 'Роль сохранена.'); } public function destroy(Role $role, AccessService $accessService): RedirectResponse { if ($role->slug === Role::ADMIN) { return redirect() ->route('admin.roles.index') ->with('danger', 'Роль администратора нельзя удалить.'); } $linkedUsers = User::withTrashed() ->where('role_id', $role->id) ->orWhere('role', $role->slug) ->count(); if ($linkedUsers > 0) { return redirect() ->route('admin.roles.edit', $role) ->with('danger', 'Роль нельзя удалить, пока на неё ссылается хотя бы один пользователь.'); } $role->delete(); $accessService->bumpCacheVersion(); return redirect() ->route('admin.roles.index') ->with('success', 'Роль удалена.'); } private function formData(Role $role, array $effects): array { return [ 'active' => 'roles', 'title' => $role->exists ? 'Редактирование роли' : 'Создание роли', 'role' => $role, 'permissionGroups' => Permission::getGroupedForUi(), 'permissionEffects' => $effects, ]; } private function validateRole(Request $request, ?Role $role = null): array { $roleId = $role?->id ?: 'NULL'; return $request->validate([ 'slug' => [ $role?->exists ? 'nullable' : 'required', 'string', 'alpha_dash', 'max:80', 'unique:roles,slug,' . $roleId, ], 'name' => ['required', 'string', 'min:2', 'max:255'], 'description' => ['nullable', 'string', 'max:1000'], 'is_active' => ['nullable', 'boolean'], 'sort' => ['nullable', 'integer', 'min:0', 'max:100000'], 'permission_effects' => ['nullable', 'array'], 'permission_effects.*' => ['nullable', 'in:none,allow,deny'], ]); } private function syncRolePermissions(Role $role, array $effects): void { $permissions = Permission::query() ->whereIn('id', array_filter(array_keys($effects), 'is_numeric')) ->orWhereIn('slug', array_filter(array_keys($effects), fn ($key): bool => !is_numeric($key))) ->get(); $sync = []; foreach ($permissions as $permission) { $effect = $effects[$permission->id] ?? $effects[$permission->slug] ?? null; if (in_array($effect, ['allow', 'deny'], true)) { $sync[$permission->id] = ['effect' => $effect]; } } $role->permissions()->sync($sync); } }