Policies in Laravel provide a simple, organized, and effective way to authorize actions within your application. They are designed to manage authorization logic for specific models, keeping your codebase clean and maintainable. In this guide, we’ll explore the creation, use, and management of policies in Laravel.
Introduction to Policies
Policies in Laravel are classes that organize authorization logic around a particular model or resource. For example, if your application includes a Post
model, you might create a corresponding PostPolicy
to authorize actions such as viewing, creating, updating, or deleting posts.
Why Use Policies?
- Separation of Concerns: Keeps authorization logic separate from business logic.
- Readability: Enhances code readability by placing all authorization rules in a single location.
- Reusability: Policies can be reused across different parts of your application, ensuring consistent authorization logic.
- Testing: Easier to test authorization rules when they are centralized in policies.
Creating Policies
To create a policy, you can use the make:policy
Artisan command. By default, this command will create a policy class in the app/Policies
directory.
php artisan make:policy PostPolicy
This command will create a new PostPolicy
class with the following structure:
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any posts.
*/
public function viewAny(User $user)
{
//
}
/**
* Determine whether the user can view the post.
*/
public function view(User $user, Post $post)
{
//
}
/**
* Determine whether the user can create posts.
*/
public function create(User $user)
{
//
}
/**
* Determine whether the user can update the post.
*/
public function update(User $user, Post $post)
{
//
}
/**
* Determine whether the user can delete the post.
*/
public function delete(User $user, Post $post)
{
//
}
}
Registering Policies
Once you have created a policy, you need to register it. This is typically done in the AuthServiceProvider
class. By default, this class is located at app/Providers/AuthServiceProvider.php
.
<?php
namespace App\Providers;
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
Post::class => PostPolicy::class,
];
/**
* Register any authentication / authorization services.
*/
public function boot()
{
$this->registerPolicies();
}
}
This code maps the Post
model to the PostPolicy
. Now, Laravel will automatically resolve the correct policy class for the Post
model.
Defining Policy Methods
Policy methods correspond to the various actions a user can perform on a model. Each method receives a User
instance and the model instance as arguments.
Example: Defining Policy Methods
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any posts.
*/
public function viewAny(User $user)
{
return true; // Allow viewing any post
}
/**
* Determine whether the user can view the post.
*/
public function view(User $user, Post $post)
{
return $user->id === $post->user_id; // Allow viewing if user owns the post
}
/**
* Determine whether the user can create posts.
*/
public function create(User $user)
{
return $user->role === 'editor'; // Allow creation if user is an editor
}
/**
* Determine whether the user can update the post.
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id; // Allow updating if user owns the post
}
/**
* Determine whether the user can delete the post.
*/
public function delete(User $user, Post $post)
{
return $user->id === $post->user_id; // Allow deleting if user owns the post
}
}
Using Policies in Controllers
In controllers, you can use the authorize
method to check permissions. This method accepts the name of the policy method you wish to call and the relevant model instance.
Example: Using Policies in a Controller
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function show(Post $post)
{
$this->authorize('view', $post);
return view('posts.show', compact('post'));
}
public function store(Request $request)
{
$this->authorize('create', Post::class);
// Validate and store the post...
}
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// Validate and update the post...
}
public function destroy(Post $post)
{
$this->authorize('delete', $post);
// Delete the post...
}
}
Using Policies in Views
You can use the @can
Blade directive to check permissions in your views. This directive accepts the name of the policy method and the relevant model instance.
Example: Using Policies in a View
<!-- Show post if user is authorized to view -->
@can('view', $post)
<h1>{{ $post->title }}</h1>
<p>{{ $post->content }}</p>
@endcan
<!-- Show edit button if user is authorized to update -->
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Edit Post</a>
@endcan
<!-- Show delete button if user is authorized to delete -->
@can('delete', $post)
<form action="{{ route('posts.destroy', $post) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit">Delete Post</button>
</form>
@endcan
Policy Middleware
Laravel includes a middleware that verifies a user can perform a given action before executing the request. This middleware is useful for protecting entire routes or controllers.
Example: Applying Policy Middleware
<?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::middleware(['can:create,App\Models\Post'])->group(function () {
Route::get('posts/create', [PostController::class, 'create']);
Route::post('posts', [PostController::class, 'store']);
});
Global Policies
Sometimes you might need to define a policy that applies to all models. Laravel allows you to define global authorization rules using the before
method in your policy.
Example: Defining Global Policies
<?php
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
/**
* Intercept all policy methods before executing.
*/
public function before(User $user, $ability)
{
if ($user->isAdmin()) {
return true; // Allow all actions for admin users
}
}
// Other policy methods...
}
Best Practices
- Centralize Authorization Logic: Keep all authorization rules in policies to maintain a clean and organized codebase.
- Use Descriptive Method Names: Ensure policy method names are descriptive and intuitive.
- Leverage Global Policies: Use the
before
method for global authorization rules. - Test Policies: Write tests for your policies to ensure your authorization logic works as expected.
- Document Policies: Document your policies and their methods to help other developers understand the authorization logic.
Conclusion
Policies in Laravel offer a robust and organized way to manage authorization logic in your application. By centralizing authorization rules, you can maintain a clean codebase and ensure consistent behavior across your application. This guide covered the basics of creating, registering, and using policies, along with best practices to help you effectively implement authorization in your Laravel projects.