Загрузка...

Что такое Policy в Laravel

Разбираемся, что такое Policy, зачем они нужны, как с ними работать и как писать безопасный код без хаоса в контроллерах.

Когда вы начинаете строить приложение на Laravel, рано или поздно возникает вопрос:
«Как контролировать, кто и что может делать?»

Например:

  • Только автор статьи может её редактировать.
  • Только администратор может удалять пользователей.
  • Гость не должен видеть приватные данные.

Все эти проверки называются авторизацией (authorization), и Laravel предлагает для этого два основных механизма:
👉 Gates (ворота) и Policies (политики).

В этой статье мы подробно разберём Policies — что это, зачем они нужны, как их использовать и какие есть лучшие практики.


🧭 Что такое Policy в Laravel

Policy (политика) — это отдельный PHP-класс, в котором вы описываете правила доступа к конкретной модели.
Проще говоря: в Policy хранятся все правила, кто и что может делать с конкретным типом данных.

Например:

  • PostPolicy отвечает за доступ к постам (App\Models\Post),
  • UserPolicy — за пользователей (App\Models\User),
  • OrderPolicy — за заказы (App\Models\Order), и т.д.

Каждый метод политики описывает одно действие:

public function update(User $user, Post $post)
{
    return $user->id === $post->user_id;
}

Этот метод говорит: «пользователь может редактировать пост только если он его автор».


🧩 Зачем нужны политики

Многие начинающие разработчики сначала просто вставляют проверки доступа прямо в контроллеры:

if ($post->user_id !== auth()->id()) {
    abort(403);
}

Это работает, но со временем код превращается в кашу:

  • Логика авторизации размазана по всем контроллерам;
  • Повторяются одни и те же проверки;
  • Изменить правило — значит искать и править в десятках мест.

Политики решают эту проблему.

Они позволяют:

  • Вынести всю авторизацию в одно место;
  • Сократить код контроллеров;
  • Упростить тестирование;
  • Сделать проект понятнее и масштабируемее.

🏁 Когда стоит использовать Policy

Используйте Policy, когда:

  • Правила доступа зависят от модели (например, пост, заказ, комментарий);
  • Есть разные действия над одной моделью (просмотр, обновление, удаление и т.д.);
  • Вам нужно централизовать и повторно использовать проверки доступа.

А вот Gates лучше использовать, если проверка простая и не привязана к модели.
Например:

Gate::define('view-admin-dashboard', fn(User $user) => $user->is_admin);

🚀 Как начать использовать Policy в Laravel

Давайте создадим простую политику для модели Post.

1. Создаём политику

php artisan make:policy PostPolicy --model=Post

Laravel создаст файл:
app/Policies/PostPolicy.php

Содержимое будет примерно таким:

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    public function viewAny(User $user): bool
    {
        return true;
    }

    public function view(User $user, Post $post): bool
    {
        return true;
    }

    public function create(User $user): bool
    {
        return true;
    }

    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

2. Регистрируем политику

Laravel умеет автоматически находить политики, если вы соблюдаете стандартные имена:
PostPostPolicy.

Если хотите указать вручную, откройте app/Providers/AuthServiceProvider.php:

protected $policies = [
    \App\Models\Post::class => \App\Policies\PostPolicy::class,
];

3. Используем политику в контроллере

public function update(Request $request, Post $post)
{
    $this->authorize('update', $post);

    $post->update($request->all());

    return redirect()->route('posts.show', $post);
}

Если пользователь не имеет права редактировать пост — Laravel выбросит исключение AuthorizationException (HTTP 403).


4. Используем политику в Blade

Laravel предоставляет удобную директиву @can:

@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Редактировать</a>
@endcan

или альтернативу:

@cannot('delete', $post)
    <p>Вы не можете удалить этот пост</p>
@endcannot

5. Проверка через фасад Gate

Иногда полезно проверить доступ в сервисах или других местах, не связанных с контроллером:

if (Gate::allows('update', $post)) {
    // пользователь может обновлять пост
}

или наоборот:

if (Gate::denies('delete', $post)) {
    abort(403);
}

💡 Полезные методы и возможности

before() — универсальная проверка

Иногда нужно дать администратору доступ ко всему.
Можно использовать метод before внутри политики:

public function before(User $user, string $ability)
{
    if ($user->is_admin) {
        return true;
    }
}

Теперь админ может выполнять любое действие без дополнительных проверок.


🔄 Проверка нескольких действий

В Blade можно сразу проверить несколько правил:

@canany(['update', 'delete'], $post)
    <p>Вы можете редактировать или удалять этот пост.</p>
@endcanany

🧱 Валидация на уровне маршрутов

Если вы используете Route Model Binding, Laravel может автоматически проверять доступ к модели:

Route::resource('posts', PostController::class)->middleware('can:update,post');

🧠 Лучшие практики

  1. Одна политика — одна модель.
    Не мешайте в одном классе логику разных сущностей.
  2. Не пишите бизнес-логику в политике.
    Политика — только для проверки прав, не для изменения данных.
  3. Используйте before() для суперпользователей.
  4. Всегда тестируйте политики.
    Писать unit-тесты на политики легко, а баги в авторизации бывают дорогими.
  5. Не дублируйте логику.
    Если правило повторяется — вынесите его в отдельный метод или используйте Gate::before.
  6. Будьте явными.
    Лучше явно вызвать $this->authorize('update', $post), чем писать скрытые проверки.

🧩 Пример реальной ситуации

Допустим, у нас блог.
Требования:

  • Автор может редактировать и удалять только свои посты.
  • Админ может всё.

PostPolicy будет выглядеть так:

class PostPolicy
{
    public function before(User $user, string $ability)
    {
        if ($user->is_admin) {
            return true;
        }
    }

    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

Контроллер:

public function destroy(Post $post)
{
    $this->authorize('delete', $post);
    $post->delete();

    return redirect()->route('posts.index');
}

И всё!
Laravel сам позаботится о безопасности и вернёт 403, если доступ запрещён.


🧭 Итог

Политики в Laravel — мощный и удобный инструмент, который:

  • Делает код чище;
  • Централизует авторизацию;
  • Защищает от случайных ошибок;
  • И легко интегрируется в контроллеры и шаблоны.

Если вы только начинаете — создайте хотя бы одну политику для своей основной модели и попробуйте использовать её вместо ручных if.


📘 Краткое резюме

Что Описание
Что такое Policy Класс с правилами доступа для модели
Когда использовать Когда проверка связана с конкретной моделью
Как вызвать $this->authorize('update', $post)
В Blade @can('update', $post)
Где хранится app/Policies
Регистрируется В AuthServiceProvider
Совет Делайте логику прозрачной, пишите тесты

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *