В данной статье продолжение темы авторизации, начатой в статье
"Авторизация в Laravel. Использование методов класса Gate."

Для большого проекта, правила стоит сохранять не в методе boot() класса AuthServiceProvider, а в отдельных классах политик, только подключая их в классе AuthServiceProvider в защищенном свойстве $policies.
Правила указанные в политиках привязываются к конкретной сущности - модели.

Для создания класса политики авторизации, удобно использовать консоль:
php artisan make:policy PostPolicy
создастся папка и файл app\Policies\PostPolicy.php

В классе политики создаем методы проверок с названиями по которым будем к ним обращаться:
public function add($user){
    foreach($user->roles as $role){
        if($role->name == 'admin') return true; 
    }
    return false;
}
public function update($user, $post){
    foreach($user->roles as $role){
        if($role->name == 'admin'){
            return true;
        }
    }
    if ($user->id == $post->user_id){
        return true;
    }
    return false;
}
код проверок идентичный тому, что прописывался в методе boot() класса AuthServiceProvider в предыдущей статье.


Так же можно делать перехват проверок авторизации с помощью методов before() и after(). При использовании политик, для их использования нужно создавать соответствующий метод в классе политики:
public function before($user){
    if ($user->id == 2) return true;
    return false;
}
в данном случае, метод before() будет выполнен перед любым из методов проверок данного класса политики авторизации.


Зарегистрируем данную политику.

Файл app\Providers\AuthServiceProvider.php, массив
'App\Model' => 'App\Policies\ModelPolicy',
указанный в свойстве $policies удаляем, т.к. он только для примера.
Вместо него прописываем свою политику:
protected $policies = [
    Post::class => PostPolicy::class,
];
ключ массива – сущность к которой привязываем (модель);
значение массива – класс политики.


Проверка политик.


1. В нужном действии контроллера.
Подключение почти аналогичное тому, которое использовалось при подключении правил указанных в методе boot(). Только при работе с политиками, аргументом, после названия правила, нужно передать сущность для которой правило создавалось (модель) – класс или объект.

Пример - проверим возможность добавления поста для текущего пользователя:
if(Gate::denies('add', Post::class)){
    return redirect()->back()->with('message','Доступ запрещен.');
}

если в коде уже есть доступ к объекту, то его и передаем:
if(Gate::denies('add', $post)){
    return redirect()->back()->with('message','Доступ запрещен.');
}
где $post – объект модели Post.


Пример – проверим права на редактирование поста:
$post = Post::find($request->id);
if(Gate::denies('update', $post)){
    return redirect()->back()->with('message', 'Доступ запрещен.');
}
в данном примере не пришлось менять ничего кроме названия правила, т.к. для получения id поста мы и так передавали объект модели Post.




2. В шаблонах Blade.
Аналогично будет работать и проверка в шаблонах с помощью директивы @can, где так же нужно только указать правильно имя:
@can('update', $post)
    <form action="{{route('admin_create_post_p')}}" method="POST" class="form-horizontal">
    …
    </form>
@else
    <div class="alert alert-danger">
        <h2>У Вас нет прав на изменение данных.</h2>
    </div>
@endcan

3. С помощью модели User и методов can() и cannot().
if($request->user()->cannot('update',$post)){
    return redirect()->back()->with('message', 'Доступ запрещен.');
}
нужно не забывать передать вторым аргументом сущность с которой связана политика – класс или объект нужной модели.


4. С помощью функции Policy.
Данная функция получает в качестве параметра класс или объект к которой привязана политика авторизации и возвращает объект данной политики, например PostPolicy. У которого далее вызывается нужный метод проверки правил. В данный метод нужно самому передать объект текущего пользователя.
$user = Auth::user();
if(!policy(Post::class)->add($user)){
    return redirect()->back()->with('message', 'Доступ запрещен.');
}
после аргумента $user можно передать прочие, нужные аргументы.
Для того, чтобы данная проверка соответствовала проверке с помощью вызова Gate::denies() – перед вызовом я поставил восклицательный знак (инверсия возвращаемого значения true/false).


5. Авторизация контроллера.
Родительский контроллер App\Http\Controllers\Controller, от которого наследуются все пользовательские контроллеры, подключает трейт AuthorizesRequests из файла vendor\laravel\framework\src\Illuminate\Foundation\Auth\Access\AuthorizesRequests.php

Данный трейт содержит метод authorize(), который может быть использован для быстрой авторизации данного действия или выброса AuthorizationException. То есть, при отсутствии прав, действие контроллера обрывается с выводом исключения - HTTP-ответ с кодом состояния 403 Not Authorized.
public function authorize($ability, $arguments = [])
{
    list($ability, $arguments) = $this->parseAbilityAndArguments($ability, $arguments);
    return app(Gate::class)->authorize($ability, $arguments);
}

Метод authorize() данного трейта возвращает результат вызова объекта класса Gate (файл vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php) для которого вызывается одноименный метод authorize() определяющий есть ли у пользователя права на совершение указанного действия.
Как видим, метод принимает только 2 аргумента. Вторым аргументом можно передать один из параметров или же массив параметров.

Примеры.
1. Добавление поста:
$this->authorize('add', Post::class);

2. Обновление поста:
$this->authorize('update', [Post::class, $post]);

Так же можно проверить права для пользователя, который не является текущим аутентифицированным пользователем:
$this->authorizeForUser($user, 'update', $post);


Имена методов политик авторизации.
Если название метода контроллера, в котором проверяются права, будет совпадать с названием метода политики авторизации, то название метода политики при проверке прав можно не указывать:
$this->authorize($post);