Приступаем к установке и прочим действиям
Установите пакет разрешений Spatie с помощью этих команд :
//Install the package composer require spatie/laravel-permission //Register the provider in the config/app.php 'providers' => [ // ... Spatie\Permission\PermissionServiceProvider::class, ]; //This will generate the necessary migrations for the package and the config file php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" php artisan optimize:clear # or php artisan config:clear php artisan migrate // will migrate the neccessary tables required for this package
Модель пользователя должна иметь признак HasRole:
use Illuminate\Foundation\Auth\User as Authenticatable; use Spatie\Permission\Traits\HasRoles; class User extends Authenticatable { use HasRoles; // ... }
Теперь мы создадим несколько разрешений в таблице разрешений.
Imp: Убедитесь, что сначала добавили " MODULE Name " с точечным обозначением и OPERATION Name, как показано ниже:
Имя модуля: категории
Имя операции: create
Конечный результат: categories.create
Это поможет отобразить настройки "добавления прав доступа к ролям" по категориям следующим образом:
Теперь создадим RoleController с помощью команды " php artisan make:controller RoleController ". Нам не нужно создавать модель для ролей или разрешений, так как они уже есть в нашем пакете и мы можем использовать их с помощью команды "use Spatie\Permission\Models\Role ". Давайте создадим несколько методов для нашего контроллера ролей.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Spatie\Permission\Models\Role; <--- ROLE MODEL use Spatie\Permission\Models\Permission; <---- PERMISSION MODEL class RoleController extends Controller { public function allRoles() { } public function create() { } public function store(Request $request) { } public function edit($id) { } public function update(Request $request, $id) { } public function delete($id) { } }
Давайте посмотрим на страницу создания ролей и как отобразить ее следующим образом:
Метод создания :
public function create() { \DB::statement("SET SQL_MODE=''");; $role_permission = Permission::select('name','id')->groupBy('name')->get(); $custom_permission = array(); foreach($role_permission as $per){ $key = substr($per->name, 0, strpos($per->name, ".")); if (str_starts_with($per->name, $key)){ $custom_permission[$key][] = $per; } } return view('admin.roles.create')->with('permissions',$custom_permission); }
Посмотрите, как мы создадим этот массив:
Пример :-
2. $custom_permission = array();: Это инициализирует пустой массив $custom_permission, который будет использоваться для хранения пользовательских разрешений позже в коде.
foreach($role_permission as $per){ $key = substr($per->name, 0, strpos($per->name, ".")); if (str_starts_with($per->name, $key)){ $custom_permission[$key][] = $per; } }
3. $key = substr($per->name, 0, strpos($per->name, "."));: Эта строка извлекает подстроку из начала поля name текущего элемента $per до первого появления символа точки ("."). Это делается с помощью функций substr() и strpos() в PHP, а полученная подстрока сохраняется как $key .
4. if (str_starts_with($per->name, $key)){ ... }: Затем проверяется, начинается ли строка столбца 'name' с извлеченного $key, используя функцию str_starts_with(). например, вот так :
if categories.create starts with categories which is true (categories == categories.create) ==> true
Some defination of str_starts_with. str_starts_with($string, $substring) Parameters: $string: This parameter refers to the string whose starting string needs to be checked. $substring: This parameter refers to the string that needs to be checked. Return Type: If the string begins with the substring then str_starts_with() will return TRUE otherwise it will return FALSE.
5. Если да, то происходит добавление записи в массив $custom_permission, используя $key в качестве ключа массива, который представляет собой категории, а $per в качестве объекта.
Таким образом, это будет выглядеть следующим образом:
Это используется как механизм группировки для объединения разрешений с похожими префиксами в массив $custom_permission.
Мы можем получить доступ к массиву $custom_permission и использовать его для отображения пользовательских разрешений, сгруппированных по их префиксам.
Теперь отобразим это в представлении
Create.blade.php:
<div class="ml-4 mt-16 w-9/12"> <form action="{{route('roles.store')}}" method="POST"> @csrf <h1 class="text-3xl mt-4 mb-8"> Create Role </h1> <div class="mb-6"> <label for="text" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Role Name</label> <input type="text" value="{{old('name')}}" name="name" id="email" class="bg-gray-50 w-80 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 " placeholder="User, Editor, Author ... " > @foreach ($errors->get('name') as $error) <p class="text-red-600">{{$error}}</p> @endforeach </div> <table class="permissionTable border rounded-md bg-white overflow-hidden shadow-lg my-4 p-4"> <th class="px-4 py-4"> {{__('Section')}} </th> <th class="px-4 py-4"> <label> <input class="grand_selectall" type="checkbox"> {{__('Select All') }} </label> </th> <th class="px-4 py-4"> {{__("Available permissions")}} </th> <tbody> @foreach($permissions as $key => $group) <tr class="py-8"> <td class="p-6"> <b>{{ ucfirst($key) }}</b> </td> <td class="p-6" width="30%"> <label> <input class="selectall" type="checkbox"> {{__('Select All') }} </label> </td> <td class="p-6"> @forelse($group as $permission) <label style="width: 30%" class=""> <input name="permissions[]" class="permissioncheckbox" class="rounded-md border" type="checkbox" value="{{ $permission->id }}"> {{$permission->name}} </label> @empty {{ __("No permission in this group !") }} @endforelse </td> </tr> @endforeach </tbody> </table> <button type="submit" class="text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 shadow-lg shadow-blue-500/50 dark:shadow-lg dark:shadow-blue-800/80 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 "> Create Role </button> </form> </div>
Теперь для того, чтобы функция select all работала, вставьте этот код в тег script в файле create.blade.php:
$(".permissionTable").on('click', '.selectall', function () { if ($(this).is(':checked')) { $(this).closest('tr').find('[type=checkbox]').prop('checked', true); } else { $(this).closest('tr').find('[type=checkbox]').prop('checked', false); } calcu_allchkbox(); }); $(".permissionTable").on('click', '.grand_selectall', function () { if ($(this).is(':checked')) { $('.selectall').prop('checked', true); $('.permissioncheckbox').prop('checked', true); } else { $('.selectall').prop('checked', false); $('.permissioncheckbox').prop('checked', false); } }); $(function () { calcu_allchkbox(); selectall(); }); function selectall(){ $('.selectall').each(function (i) { var allchecked = new Array(); $(this).closest('tr').find('.permissioncheckbox').each(function (index) { if ($(this).is(":checked")) { allchecked.push(1); } else { allchecked.push(0); } }); if ($.inArray(0, allchecked) != -1) { $(this).prop('checked', false); } else { $(this).prop('checked', true); } }); } function calcu_allchkbox(){ var allchecked = new Array(); $('.selectall').each(function (i) { $(this).closest('tr').find('.permissioncheckbox').each(function (index) { if ($(this).is(":checked")) { allchecked.push(1); } else { allchecked.push(0); } }); }); if ($.inArray(0, allchecked) != -1) { $('.grand_selectall').prop('checked', false); } else { $('.grand_selectall').prop('checked', true); } } $('.permissionTable').on('click', '.permissioncheckbox', function () { var allchecked = new Array; $(this).closest('tr').find('.permissioncheckbox').each(function (index) { if ($(this).is(":checked")) { allchecked.push(1); } else { allchecked.push(0); } }); if ($.inArray(0, allchecked) != -1) { $(this).closest('tr').find('.selectall').prop('checked', false); } else { $(this).closest('tr').find('.selectall').prop('checked', true); } calcu_allchkbox(); });
Store method :
Теперь мы создадим метод роли и прикрепим разрешения к ролям с помощью метода givePermissionTo() .
public function store(Request $request) { $request->validate([ 'name' => 'required', ]); $role = Role::create([ 'name' => $request->name, ]); if ($request->permissions){ foreach ($request->permissions as $key => $value) { $role->givePermissionTo($value); } } return redirect()->route('roles.all'); }
Метод редактирования:
public function edit($id) { $role = Role::with('permissions')->find($id); \DB::statement("SET SQL_MODE=''"); $role_permission = Permission::select('name','id')->groupBy('name')->get(); $custom_permission = array(); foreach($role_permission as $per){ $key = substr($per->name, 0, strpos($per->name, ".")); if (str_starts_with($per->name, $key)){ $custom_permission[$key][] = $per; } } return view('admin.roles.edit',compact('role'))->with('permissions',$custom_permission); }
Edit.blade.php:
<div class="ml-4 mt-16 w-9/12"> <form action="{{route('roles.store')}}" method="POST"> @csrf <h1 class="text-3xl mt-4 mb-8"> Update Role </h1> <div class="mb-6"> <label for="text" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Role Name</label> <input type="text" value="{{old('name',$role->name ?? '')}}" name="name" id="email" class="bg-gray-50 w-80 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 " placeholder="Wedding, Kitty kat, parties, lol deaths haha" > @foreach ($errors->get('name') as $error) <p class="text-red-600">{{$error}}</p> @endforeach </div> <table class="permissionTable border rounded-md bg-white overflow-hidden shadow-lg my-4 p-4"> <th class="px-4 py-4"> {{__('Section')}} </th> <th class="px-4 py-4"> <label> <input class="grand_selectall" type="checkbox"> {{__('Select All') }} </label> </th> <th class="px-4 py-4"> {{__("Available permissions")}} </th> <tbody> @foreach($permissions as $key => $group) <tr class="py-8"> <td class="p-6"> <b>{{ ucfirst($key) }}</b> </td> <td class="p-6" width="30%"> <label> <input class="selectall" type="checkbox"> {{__('Select All') }} </label> </td> <td class="p-6"> @forelse($group as $permission) <label style="width: 30%" class=""> <input {{ $role->permissions->contains('id',$permission->id) ? "checked" : "" }} name="permissions[]" class="permissioncheckbox" class="rounded-md border" type="checkbox" value="{{ $permission->id }}"> {{$permission->name}} </label> @empty {{ __("No permission in this group !") }} @endforelse </td> </tr> @endforeach </tbody> </table> <button type="submit" class="text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 shadow-lg shadow-blue-500/50 dark:shadow-lg dark:shadow-blue-800/80 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 "> Update Role </button> </form> </div>
Метод обновления :
Несколько разрешений могут быть синхронизированы с ролью с помощью метода syncPermissions:
public function update(Request $request, $id) { $role = Role::where('id',$id)->first(); $request->validate([ 'name' => 'required' ]); $role->update([ "name" => $request->name ]); $role->syncPermissions($request->permissions); return redirect()->route('admin.roles.all')->with('success','Roles Updated Successfully'); }
Метод удаления :
public function delete($id) { $role = Role::where('id',$id)->first(); if (isset($role)){ $role->permissions()->detach(); $role->delete(); return redirect()->route('roles.all')->with('success','Roles Deleted Successfully'); } }
Index.blade.php:
<div class="flex flex-col mt-6"> <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> <div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8"> <div class="overflow-hidden border border-gray-200 dark:border-gray-700 md:rounded-lg"> <table id="eventstable" class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <thead class="bg-gray-50 dark:bg-gray-800"> <tr> <th scope="col" class="py-3.5 px-4 text-sm font-normal text-left rtl:text-right text-gray-500 dark:text-gray-400"> <div class="flex items-center gap-x-3"> <input type="checkbox" class="text-blue-500 border-gray-300 rounded dark:bg-gray-900 dark:ring-offset-gray-900 dark:border-gray-700"> <span>Id</span> </div> </th> <th scope="col" class="px-12 py-3.5 text-sm font-normal text-left rtl:text-right text-gray-500 dark:text-gray-400"> <button class="flex items-center gap-x-2"> <span>Role Name</span> </button> </th> <th scope="col" class="px-4 py-3.5 text-sm font-normal text-left rtl:text-right text-gray-500 dark:text-gray-400"> <button class="flex items-center gap-x-2"> <span>Permissions</span> </button> </th> <th scope="col" class="relative py-3.5 px-4"> <span class="sr-only">Edit</span> </th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200 dark:divide-gray-700 dark:bg-gray-900"> @foreach ($roles as $role) <tr> <td class="px-4 py-4 text-sm font-medium text-gray-700 whitespace-nowrap"> <div class="inline-flex items-center gap-x-3"> <div class="flex items-center gap-x-2"> <div> <h2 class="font-medium text-gray-800 dark:text-white ">{{$loop->iteration}}</h2> </div> </div> </div> </td> <td class="px-4 py-4 text-sm text-gray-500 dark:text-gray-300 whitespace-nowrap">{{$role->name}}</td> <td class="px-4 py-4 text-sm text-gray-500 dark:text-gray-300 whitespace-nowrap"> <div class="flex items-center flex-wrap gap-2"> @foreach ($role->permissions as $permission) <div class="rounded-full bg-indigo-400 px-2 py-0.5 text-indigo-200 font-semibold">{{$permission->name}}</div> @endforeach </div> </td> <td class="px-4 py-4 text-sm whitespace-nowrap"> <div class="flex items-center gap-x-6"> <a href="{{route('roles.edit',$role->id)}}" class="block text-gray-500 transition-colors duration-200 dark:hover:text-yellow-500 dark:text-gray-300 hover:text-yellow-500 focus:outline-none"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" /> </svg> </a> <form method="POST" action="{{route('roles.delete',$role->id)}}"> @csrf @method('DELETE') <button type="submit" class="text-gray-500 transition-colors duration-200 dark:hover:text-red-500 dark:text-gray-300 hover:text-red-500 focus:outline-none"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /> </svg> </button> </form> </div> </td> </tr> @endforeach </tbody> </table> </div> </div> </div> </div>
Теперь, чтобы заставить это работать, мы можем прикрепить роли к пользователям с помощью метода assignRole:
$user->assignRole('Seller');
Итак, допустим, если вы создадите роль и прикрепите к ней права на создание категорий и просмотр категорий.
Теперь в пакете разрешений Spatie всем разрешениям будут назначены ворота, так что вы можете сделать это, чтобы запретить пользователям доступ к определенным областям.
Скажем, если вы хотите, чтобы только роли с правами на создание категорий могли просматривать только страницу/разделы создания категорий, вы можете сделать это следующим образом:
В Blade
@can('categories.create') <div class="ml-auto px-3 py-1 text-blue-600 bg-blue-100 rounded-md"> <a href="{{route('categories.create')}}">Category Create</a> </div> @endcan //=============== OR FOR CHECKING MULTIPLE ABILITIES/PERMISSIONS ========== // @canany(['categories.create', 'categories.delete']) <div class="actions"> @can('categories.edit') <button>Edit</button> @endcan @can('categories.delete') <button>Delete</button> @endcan </div> @endcanany
В контроллере:
public function createCategory() { $this->authorize('categories.create'); ================= OR ================= if (Gate::allows('categories.create')) { // User is authorized, perform the action } else { abort(403); // Or redirect, or return an error response, depending on your needs } return view('admin.categories.create'); }
При использовании Middleware:
Route::group(['middleware' => ['can:categories.create']], function () { // });
При хранении/обновлении данных с помощью класса Form Request:
Requests/StoreCategories.php
public function authorize(): bool { switch ($this->method()) { case 'POST': return $this->user()->can('categories.create'); break; case 'PUT': return $this->user()->can('categories.edit'); break; } return true; }
Определение супер администратора:
Если вы хотите, чтобы роль "Super Admin" отвечала true на все разрешения, без необходимости назначать все эти разрешения роли, вы можете использовать метод Gate::before() в Laravel. Например:
use Illuminate\Support\Facades\Gate; class AuthServiceProvider extends ServiceProvider { public function boot() { $this->registerPolicies(); // Implicitly grant "Super Admin" role all permissions // This works in the app by using gate-related functions like auth()->user->can() and @can() Gate::before(function ($user, $ability) { return $user->hasRole('Super Admin') ? true : null; }); } }
ПРИМЕЧАНИЕ: Правила Gate::before должны возвращать null, а не false, иначе это будет мешать нормальной работе политики.
Лучшие практики из пакета разрешений Spatie :
Роли лучше всего назначать только Пользователям, чтобы "группировать" людей по "наборам разрешений".
Разрешения лучше всего назначать ролям. Чем более гранулированными/детальными будут названия ваших разрешений (например, отдельные разрешения типа "просмотр документа" и "редактирование документа"), тем легче контролировать доступ в вашем приложении.
Пользователям редко следует давать "прямые" разрешения. Лучше всего, если пользователи наследуют разрешения через роли, к которым они приписаны.
При таком проектировании все разделы вашего приложения могут проверять наличие определенных разрешений, необходимых для доступа к определенным функциям или выполнения определенных действий. И таким образом вы всегда можете использовать родные директивы Laravel @can и can() везде в вашем приложении, что позволяет слою Gate в Laravel делать всю тяжелую работу.
Надеюсь, вы получили большое удовольствие от чтения этой статьи.
20.08.2023 18:21
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".
20.08.2023 17:46
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
19.08.2023 18:39
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.
19.08.2023 17:22
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!
18.08.2023 20:33
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий их языку и культуре.
14.08.2023 14:49
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.