Fine-Grained Authorization
Authentication answers "who are you?" Authorization answers "what are you allowed to do?" Laravel provides Gates and Policies.
Policies (Preferred Approach)
php artisan make:policy PostPolicy --model=Post
class PostPolicy
{
public function viewAny(User $user): bool
{
return true;
}
public function view(?User $user, Post $post): bool
{
return $post->is_published || $user?->id === $post->user_id;
}
public function create(User $user): bool
{
return $user->hasVerifiedEmail();
}
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 || $user->isAdmin();
}
}
Register in AppServiceProvider:
Gate::policy(Post::class, PostPolicy::class);
Using Policies
// In controllers
public function update(UpdatePostRequest $request, Post $post): RedirectResponse
{
$this->authorize('update', $post);
$post->update($request->validated());
return redirect()->route('posts.show', $post);
}
// In Blade
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan
@can('delete', $post)
<form method="POST" action="{{ route('posts.destroy', $post) }}">
@csrf @method('DELETE')
<button>Delete</button>
</form>
@endcan
Gates for Simple Checks
// In AppServiceProvider
Gate::define('admin-access', function (User $user) {
return $user->role === 'admin';
});
// Usage
if (Gate::allows('admin-access')) {
// Admin-only logic
}
@can('admin-access')
<a href="/admin">Admin Panel</a>
@endcan
Policies keep authorization organized, testable, and co-located with the model they protect.