Skip to main content
Create your own
Lesson illustration

Organizing Authorization with Policies

Hello again! Welcome back.

In our previous lesson, we established a solid foundation for authorization in Laravel using Gates. You learned to answer the question, "What is this user allowed to do?" for specific, discrete actions. We saw how Gates centralize simple permission logic, but we also hinted at their limitations when managing all the permissions for a single resource, like a blog post or a product.

This lesson takes the next logical step. We'll move from the procedural, closure-based approach of Gates to the more organized, object-oriented world of Policies. Policies are dedicated classes that group all authorization logic for a particular model.

By the end of this lesson, you will be able to create a policy class, define authorization methods within it for a specific model, and register it with your application. This will give you a powerful and scalable way to manage complex permissions.

1. From Gates to Policies: Organizing Your Logic

Gates are perfect for actions that aren't tied to a specific model, like access-admin-panel, or for simple one-off checks. But what happens when you have a Post model? A user might be able to:

  • view any post.
  • create a new post.
  • update their own posts.
  • delete their own posts.
  • publish a post (if they are an editor).

Creating a separate Gate for each of these (view-post, create-post, update-post, etc.) can quickly clutter your AppServiceProvider. A Policy solves this by grouping all of these related checks into a single class: PostPolicy.

Authorization - Laravel 12.x

The official Laravel documentation provides a great summary of this distinction. It frames Gates and Policies like routes and controllers: Gates are simple and closure-based, while Policies group logic around a model.

Read the short 'Introduction' section. This will reinforce the conceptual difference between Gates and Policies and why both exist.

This concept of grouping logic is fundamental. Let's see a practical migration from a Gate to a Policy.

30 Days to Learn Laravel, Ep 23 - 6 Steps to Authorization Mastery

In the previous lesson, we saw how to extract authorization logic into a Gate. This video segment picks up right from there and demonstrates the final step: migrating that Gate logic into a dedicated Policy class for better organization.

Watch the segment from 18:12 to 21:51. Pay close attention to how the logic from AppServiceProvider is moved into a new JobPolicy class and how the calls to authorize the action are updated.

2. Creating a Policy

As the video demonstrated, the process involves two main steps: generating the policy class and defining its methods.

2.1 Generating the Policy Class

Laravel's Artisan command-line tool makes this easy. You can generate a policy using the make:policy command.

Authorization - Laravel 12.x

Let's consult the documentation for the precise Artisan command. It's important to know about the --model flag, which is a powerful shortcut.

Read the 'Generating Policies' section. Focus on the two variations of the make:policy command, especially the one with the --model option.

To summarize, you'll run this command in your terminal:

php artisan make:policy PostPolicy --model=Post

This command does two things:

  1. It creates a new file at app/Policies/PostPolicy.php.
  2. The --model=Post flag tells Artisan to pre-fill this class with method stubs for common resourceful actions (viewAny, view, create, update, delete, etc.), already type-hinted with the User and Post models. This saves you a lot of boilerplate code.

2.2 Writing Policy Methods

Once your policy is generated, you fill in the logic. Each public method in the policy corresponds to a permission you want to check. These methods receive the authenticated user as the first argument and, typically, the relevant model instance as the second. They must return true or false.

Here is an example PostPolicy with the update method filled out. This logic should look familiar—it's the same check we previously would have put in a Gate.

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

class PostPolicy
{
    /**
     * Determine whether the user can view any models.
     */
    public function viewAny(User $user): bool
    {
        return true; // Anyone can view the list of posts
    }

    /**
     * Determine whether the user can view the model.
     */
    public function view(User $user, Post $post): bool
    {
        return true; // Anyone can view a single post
    }

    /**
     * Determine whether the user can create models.
     */
    public function create(User $user): bool
    {
        return $user->isVerified(); // Example: only verified users can create posts
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(User $user, Post $post): bool
    {
        // The user can update the post only if they are the owner.
        return $user->id === $post->user_id;
    }

    /**
     * Determine whether the user can delete the model.
     */
    public function delete(User $user, Post $post): bool
    {
        // The user can delete the post only if they are the owner.
        return $user->id === $post->user_id;
    }

    // ... other methods like restore, forceDelete
}

Notice how all the logic related to the Post model is now neatly contained in one class.

3. Registering a Policy

Creating the policy class isn't enough. You must tell Laravel that this policy is responsible for authorizing actions on the Post model. There are two ways to do this: auto-discovery and manual registration.

1. Automatic Discovery (Recommended)

As of recent Laravel versions, this is the default and preferred method. As long as your model and policy follow standard Laravel naming conventions, you don't have to do anything!

  • Model: app/Models/Post.php
  • Policy: app/Policies/PostPolicy.php

Laravel is smart enough to connect Post with PostPolicy automatically. This is why using the --model flag and sticking to conventions is so effective.

2. Manual Registration

If you use non-standard names or just want to be explicit, you can manually register the policy in your AuthServiceProvider. This is done in the $policies property of the provider.

Laravel Policies: AuthorizeResource and "can:ABC" Usage

This short clip from Laravel Daily clearly shows where the $policies array is located in the AuthServiceProvider and how to add a mapping.

Watch from 01:07 to 01:25. Observe how the Server model is mapped to the ServerPolicy class.

Here is a static image showing the same concept for clarity.

Registering a Policy in Laravel's AuthServiceProvider
The `\$policies` property in `app/Providers/AuthServiceProvider.php` is where you can manually map your Eloquent models to their corresponding policy classes.

To register our PostPolicy manually, you would add it to the array like this:

app/Providers/AuthServiceProvider.php:

<?php

namespace App\Providers;

// use Illuminate\Support\Facades\Gate;
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The model to policy mappings for the application.
     *
     * @var array<class-string, class-string>
     */
    protected $policies = [
        Post::class => PostPolicy::class, // Add your mapping here
    ];

    /**
     * Register any authentication / authorization services.
     */
    public function boot(): void
    {
        //
    }
}

4. How Policies Are Used

Here's the best part: once your policy is created and registered, you don't need to learn a new way to check permissions. The same methods and directives you used for Gates will automatically use your policy.

Laravel's authorization system first checks if a policy is registered for the model you're checking against. If so, it calls the corresponding policy method. If not, it looks for a Gate with that name.

Let's see the "before and after":

With a Gate named update-post:

{{-- In your Blade file --}}
@can('update-post', $post)
    <a href="...">Edit Post</a>
@endcan
// In your controller
Gate::authorize('update-post', $post);

With a registered PostPolicy:

{{-- In your Blade file --}}
@can('update', $post) {{-- Note: 'update', not 'update-post' --}}
    <a href="...">Edit Post</a>
@endcan
// In your controller
Gate::authorize('update', $post); // The Gate facade automatically calls the policy

The ability name now directly matches the method name in your policy class (update). This is cleaner and more intuitive.

Conclusion

You have now leveled up your authorization skills by moving from simple Gates to structured Policies. This approach is essential for building maintainable and scalable Laravel applications.

Key Takeaways:

  • Policies Organize Logic: Policies are PHP classes that group all authorization logic for a single Eloquent model.
  • Generate with Artisan: Use php artisan make:policy PostPolicy --model=Post to create a policy pre-filled with helpful method stubs.
  • Registration is Key: Laravel must know which policy maps to which model. This is usually handled by auto-discovery if you follow conventions, but can be done manually in the AuthServiceProvider.
  • Use is Seamless: Authorization checks via Gate::authorize(), $user->can(), and the @can directive automatically use the registered policy methods when a model instance is provided.

Up Next:

Now that you can create both Gates and Policies, the next lesson will focus on applying them effectively throughout your application. We will dive deeper into authorizing actions in controllers, specifically using the powerful authorizeResource method to protect all of your CRUD endpoints with a single line of code. We will also explore how to authorize actions for users within Blade templates to build dynamic and secure user interfaces.

Can't find a good explanation? Sign up and we'll make it for you

Sign up