
Приступаем к установке и прочим действиям
Установите пакет разрешений 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 в 2026-2027 годах? Или это полная лажа?".

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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.