Laravel ‐ chapter 16 - pierre-akhrass/Lavarel-Docs GitHub Wiki

Chapter 16: Gates and Policy (Authorization)


Authentication vs Authorization

  • Authentication: Verifies identity via credentials (e.g., login).
  • Authorization: Defines what the user can do in the system (e.g., edit, delete, view).

What is a Gate?

Gates are closures that determine if a user can perform an action.

use Illuminate\Support\Facades\Gate;

Gate::define('update-post', function ($user, $post) {
    return $user->id == $post->user_id;
});

Gate Elements:

  1. Uses Gate Facade.
  2. Defines a key like 'update-post'.
  3. Implements the authorization rule.

Preparing for Gates

  1. Create migration:
php artisan make:migration add_user_id_to_posts_table

This will add a foreign key column to an existing table: posts.

  1. Define foreign key:
$table->foreignId('user_id')->constrained()->onDelete('cascade');

If a user is deleted, all their posts will be deleted automatically (cascade delete).

  1. Run fresh migration:
php artisan migrate:fresh
  • Drops all tables in the database.

  • Recreates them from scratch using all migration files.

  • Useful during development, but be careful — it deletes all data.

  1. Update Post model:
protected $fillable = [..., 'user_id'];
  1. Update factory (PostFactory.php) and seeder (PostSeeder.php) to assign user_id.

PostFactory.php

use App\Models\User;

public function definition()
{
    return [
        'title' => $this->faker->sentence,
        'content' => $this->faker->paragraph,
        'user_id' => User::factory(), // ← creates a new user and links it
    ];
}

PostSeeder.php

Post::factory()->count(10)->create();

Using Gates

Define in AppServiceProvider.php:

Gate::define('update-post', fn($user, $post) => $user->id == $post->user_id);

Use in PostController.php:

if (!Gate::allows('update-post', $post)) {
    abort(403);
}

This checks authorization — specifically, whether the currently logged-in user is allowed to update a given post. If they are not authorized, the request is aborted with a 403 Forbidden error.


Using Policies

Use policies to group multiple authorization rules for a model.

When to Use Policies

  • When you need rules for multiple actions on a model.
  • For maintainability and clarity.

Creating a Policy

php artisan make:policy PostPolicy --model=Post

Defines methods like:

public function view(User $user, Post $post): bool { return true; }
public function create(User $user): bool { return $user->id > 0; }
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; }

Registration

Policies are auto-discovered if:

  • Located in app/Policies
  • Match naming: PostPolicy for Post model

Using Policy Methods

Gate::allows('update', $post)

Returns true if the current user is allowed to perform the 'update' action on the $post.

Gate::denies('delete', $post)

Returns true if the user is NOT allowed to delete the post.

In controller:

if (!Gate::allows('index', $posts[0])) {
    abort(403);
}

This checks if the current user is allowed to perform the 'index' action on the first post in the list ($posts[0]).


Return Responses from Policy

public function update(User $user, Post $post): Response
{
    return $user->id == $post->user_id
        ? Response::allow()
        : Response::deny('You do not own this post.');
}

Use:

Gate::inspect('update', $post)

Updating Post Creation

User model (User.php):

public function posts() {
    return $this->hasMany(Post::class);
}

Controller:

$post = new Post($request->validated());
$user = Auth::user();
$user->posts()->save($post);

Gate Helper Methods

Gate::check('create', $post)
Gate::any(['update', 'delete'], $post)
Gate::none(['update', 'delete'], $post)

Auth::user()->can('create', $post)
Auth::user()->cannot('create', $post)

Gate::forUser($user)->allows('update-post', $post)
Gate::forUser($user)->denies('update-post', $post)

allowIf() and denyIf()

Gate::allowIf(fn(User $user) => $user->isAdmin())
Gate::denyIf(fn(User $user) => !$user->isAdmin())

Equivalent:

function (User $user) {
    return !$user->isAdmin();
}

Gate::authorize()

Gate::authorize('update', $post);

Throws an exception if not authorized.