Laravel cookbook · Beginner
Authorize actions with policies
Permission checks are scattered across controllers and views and drift apart.
Approach
Put model-related rules in a policy, link it to the model, and use authorize() / can middleware in routes
and @can in Blade. Use before() for super-admins.
Solution
<?php
// app/Policies/ProjectPolicy.php
namespace App\Policies;
use App\Models\Project;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ProjectPolicy
{
public function before(User $user, string $ability): ?bool
{
return $user->is_admin ? true : null;
}
public function view(User $user, Project $project): bool
{
return $project->team->members()->whereKey($user->id)->exists();
}
public function update(User $user, Project $project): Response
{
return $project->owner_id === $user->id
? Response::allow()
: Response::denyAsNotFound(); // 404 instead of revealing the project exists
}
}
<?php
// routes/web.php
use App\Http\Controllers\ProjectController;
use Illuminate\Support\Facades\Route;
Route::put('/projects/{project}', [ProjectController::class, 'update'])->can('update', 'project');
// In a controller method
// Gate::authorize('view', $project);
Pitfalls
- Checking permissions only in Blade (hiding buttons) and not on the routes.
- Querying the database repeatedly in policies called inside loops; eager load what the policy needs.
- Returning 403 for resources the user shouldn't know exist; use
denyAsNotFound().
Variations
- Add
#[UsePolicy(ProjectPolicy::class)]on the model when the policy isn't auto-discovered. - For role-based permissions across the app,
spatie/laravel-permissionintegrates with gates.
Further reading: Authorization: policies
Reference: https://laravel.com/docs/13.x/authorization#creating-policies
More Laravel recipes
- Cache expensive data with stale-while-revalidate
- Call external APIs with timeouts, retries and fakes
- Catch N+1 queries with strict Eloquent mode
- Delete old records automatically with prunable models
- Dispatch events and listeners after the transaction commits
- Issue scoped API tokens with Sanctum
- Make queued jobs reliable with retries, backoff and uniqueness
- Prevent race conditions with transactions and row locks