RoleController.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. namespace App\Http\Controllers\Admin;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Permission;
  5. use App\Models\Role;
  6. use App\Models\User;
  7. use App\Services\Access\AccessService;
  8. use Illuminate\Http\RedirectResponse;
  9. use Illuminate\Http\Request;
  10. use Illuminate\Support\Facades\DB;
  11. use Illuminate\Support\Str;
  12. use Illuminate\View\View;
  13. class RoleController extends Controller
  14. {
  15. public function index(): View
  16. {
  17. return view('admin.roles.index', [
  18. 'active' => 'roles',
  19. 'title' => 'Роли и права',
  20. 'roles' => Role::query()
  21. ->withCount(['users' => fn ($query) => $query->withTrashed()])
  22. ->orderBy('sort')
  23. ->orderBy('name')
  24. ->get(),
  25. ]);
  26. }
  27. public function create(): View
  28. {
  29. return view('admin.roles.edit', $this->formData(new Role(), []));
  30. }
  31. public function store(Request $request, AccessService $accessService): RedirectResponse
  32. {
  33. $validated = $this->validateRole($request);
  34. $role = DB::transaction(function () use ($validated, $request, $accessService) {
  35. $role = Role::query()->create([
  36. 'slug' => $validated['slug'],
  37. 'name' => $validated['name'],
  38. 'description' => $validated['description'] ?? null,
  39. 'is_system' => false,
  40. 'is_active' => (bool) ($validated['is_active'] ?? true),
  41. 'sort' => (int) ($validated['sort'] ?? 100),
  42. ]);
  43. $this->syncRolePermissions($role, $request->input('permission_effects', []));
  44. $accessService->bumpCacheVersion();
  45. return $role;
  46. });
  47. return redirect()
  48. ->route('admin.roles.edit', $role)
  49. ->with('success', 'Роль создана.');
  50. }
  51. public function storeFromUser(User $user, Request $request, AccessService $accessService): RedirectResponse
  52. {
  53. $validated = $request->validate([
  54. 'name' => ['required', 'string', 'min:2', 'max:255'],
  55. 'slug' => ['nullable', 'string', 'alpha_dash', 'max:80', 'unique:roles,slug'],
  56. 'description' => ['nullable', 'string', 'max:1000'],
  57. ]);
  58. $slug = $validated['slug'] ?: Str::slug($validated['name']);
  59. if (!$slug) {
  60. $slug = 'role_' . $user->id;
  61. }
  62. $baseSlug = $slug;
  63. $counter = 2;
  64. while (Role::query()->where('slug', $slug)->exists()) {
  65. $slug = $baseSlug . '_' . $counter++;
  66. }
  67. $role = DB::transaction(function () use ($validated, $slug, $user, $accessService) {
  68. $role = Role::query()->create([
  69. 'slug' => $slug,
  70. 'name' => $validated['name'],
  71. 'description' => $validated['description'] ?? 'Создана из совокупности прав пользователя ' . $user->name,
  72. 'is_system' => false,
  73. 'is_active' => true,
  74. 'sort' => 100,
  75. ]);
  76. $sync = [];
  77. foreach ($accessService->getEffectivePermissions($user) as $permissionSlug => $effect) {
  78. $sync[$permissionSlug] = $effect;
  79. }
  80. $this->syncRolePermissions($role, $sync);
  81. $accessService->bumpCacheVersion();
  82. return $role;
  83. });
  84. return redirect()
  85. ->route('admin.roles.edit', $role)
  86. ->with('success', 'Роль создана из прав пользователя.');
  87. }
  88. public function edit(Role $role): View
  89. {
  90. $effects = $role->permissions()
  91. ->pluck('role_permissions.effect', 'permissions.id')
  92. ->all();
  93. return view('admin.roles.edit', $this->formData($role, $effects));
  94. }
  95. public function update(Role $role, Request $request, AccessService $accessService): RedirectResponse
  96. {
  97. $validated = $this->validateRole($request, $role);
  98. DB::transaction(function () use ($role, $validated, $request, $accessService) {
  99. $role->update([
  100. 'name' => $validated['name'],
  101. 'description' => $validated['description'] ?? null,
  102. 'is_active' => $role->slug === Role::ADMIN ? true : (bool) ($validated['is_active'] ?? false),
  103. 'sort' => (int) ($validated['sort'] ?? $role->sort),
  104. ]);
  105. if ($role->slug === Role::ADMIN) {
  106. $this->syncRolePermissions(
  107. $role,
  108. Permission::query()->pluck('slug')->mapWithKeys(fn (string $slug): array => [$slug => 'allow'])->all()
  109. );
  110. } else {
  111. $this->syncRolePermissions($role, $request->input('permission_effects', []));
  112. }
  113. $accessService->bumpCacheVersion();
  114. });
  115. return redirect()
  116. ->route('admin.roles.edit', $role)
  117. ->with('success', 'Роль сохранена.');
  118. }
  119. public function destroy(Role $role, AccessService $accessService): RedirectResponse
  120. {
  121. if ($role->slug === Role::ADMIN) {
  122. return redirect()
  123. ->route('admin.roles.index')
  124. ->with('danger', 'Роль администратора нельзя удалить.');
  125. }
  126. $linkedUsers = User::withTrashed()
  127. ->where('role_id', $role->id)
  128. ->orWhere('role', $role->slug)
  129. ->count();
  130. if ($linkedUsers > 0) {
  131. return redirect()
  132. ->route('admin.roles.edit', $role)
  133. ->with('danger', 'Роль нельзя удалить, пока на неё ссылается хотя бы один пользователь.');
  134. }
  135. $role->delete();
  136. $accessService->bumpCacheVersion();
  137. return redirect()
  138. ->route('admin.roles.index')
  139. ->with('success', 'Роль удалена.');
  140. }
  141. private function formData(Role $role, array $effects): array
  142. {
  143. return [
  144. 'active' => 'roles',
  145. 'title' => $role->exists ? 'Редактирование роли' : 'Создание роли',
  146. 'role' => $role,
  147. 'permissionGroups' => Permission::getGroupedForUi(),
  148. 'permissionEffects' => $effects,
  149. ];
  150. }
  151. private function validateRole(Request $request, ?Role $role = null): array
  152. {
  153. $roleId = $role?->id ?: 'NULL';
  154. return $request->validate([
  155. 'slug' => [
  156. $role?->exists ? 'nullable' : 'required',
  157. 'string',
  158. 'alpha_dash',
  159. 'max:80',
  160. 'unique:roles,slug,' . $roleId,
  161. ],
  162. 'name' => ['required', 'string', 'min:2', 'max:255'],
  163. 'description' => ['nullable', 'string', 'max:1000'],
  164. 'is_active' => ['nullable', 'boolean'],
  165. 'sort' => ['nullable', 'integer', 'min:0', 'max:100000'],
  166. 'permission_effects' => ['nullable', 'array'],
  167. 'permission_effects.*' => ['nullable', 'in:none,allow,deny'],
  168. ]);
  169. }
  170. private function syncRolePermissions(Role $role, array $effects): void
  171. {
  172. $permissions = Permission::query()
  173. ->whereIn('id', array_filter(array_keys($effects), 'is_numeric'))
  174. ->orWhereIn('slug', array_filter(array_keys($effects), fn ($key): bool => !is_numeric($key)))
  175. ->get();
  176. $sync = [];
  177. foreach ($permissions as $permission) {
  178. $effect = $effects[$permission->id] ?? $effects[$permission->slug] ?? null;
  179. if (in_array($effect, ['allow', 'deny'], true)) {
  180. $sync[$permission->id] = ['effect' => $effect];
  181. }
  182. }
  183. $role->permissions()->sync($sync);
  184. }
  185. }