Search code examples
laravellaravel-11

How to use the can() method on update routes?


I read here that in Laravel we can use the can method instead of the traditional middleware call on a route. https://laravel.com/docs/11.x/authorization#middleware-actions-that-dont-require-models

use App\Models\Post;
 
Route::post('/post', function () {
    // The current user may create posts...
})->can('create', Post::class);

Now the traditional method for middlewares looks like this:

Route::post('/post', function () {
    // The current user may create posts...
})->middleware('can:create,App\Models\Post');

I really like this can method and it would be nice to be able to replace middleware calls for update too to be consequent:

use App\Models\Post;
Route::put('/post/{post}', function (Post $post) {
    // The current user may update the post...
})->middleware('can:update,post');

Is there a way to do this? The documentation does not contain anything about this...


Solution

  • There are few steps you need to follow.

    Create & get registered your Policy class.

    With this command create a Policy-class. The generated policy will be placed in the app/Policies directory.

    php artisan make:policy PostPolicy --model=Post
    

    This class look like this.

    namespace App\Policies;
    
    use App\Models\Post;
    use App\Models\User;
    
    class PostPolicy
    {
        /**
         * Determine whether the user can update the post.
         *
         * @param  \App\Models\User  $user
         * @param  \App\Models\Post  $post
         * @return mixed
         */
        public function update(User $user, Post $post)
        {
            // Example: Only allow the user to update their own post
            return $user->id === $post->user_id;
        }
    }
    

    Then register the policy-class to the boot method of your application's AppServiceProvider like this.

    use App\Models\Post;
    use App\Policies\PostPolicy;
    use Illuminate\Support\Facades\Gate;
     
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Gate::policy(Order::class, OrderPolicy::class);
    }
    

    And finally you can use the can in the route to use this PostPolicy class like this.

    use App\Models\Post;
    
    Route::put('/post/{post}', function (Post $post) {
        // The current user may update the post...
    })->can('update','post'); //here `post` is an instance of `App\Models\Post` model
    

    N:B :-

    1. The Post model must have a foreign-key named user_id upon which update authorization will be matched.If no matched users found then an HTTP response with a 403 status will be returned.
    2. For more information check the official docs about how to create & registered Policy
    3. Check the Generating Policies & Registering Policies section of the docs.