| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <?php
- namespace App\Http\Controllers\Admin;
- use App\Http\Controllers\Controller;
- use App\Models\Permission;
- use App\Models\Role;
- use App\Models\User;
- use App\Services\Access\AccessService;
- use Illuminate\Http\RedirectResponse;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Str;
- use Illuminate\View\View;
- class RoleController extends Controller
- {
- public function index(): View
- {
- return view('admin.roles.index', [
- 'active' => '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);
- }
- }
|