Installation
composer require cura/user-policy-bundle
Add the bundle to config/app.php under providers:
Cura\UserPolicyBundle\UserPolicyBundle::class,
Publish the migration and config:
php artisan vendor:publish --provider="Cura\UserPolicyBundle\UserPolicyBundle" --tag="migrations"
php artisan vendor:publish --provider="Cura\UserPolicyBundle\UserPolicyBundle" --tag="config"
Run the migration:
php artisan migrate
First Use Case: Assigning a Policy
Define a policy in config/user-policies.php:
'policies' => [
'admin' => [
'name' => 'Administrator Access',
'description' => 'Full access to all features',
],
'editor' => [
'name' => 'Content Editor',
'description' => 'Can edit and publish content',
],
],
Assign a policy to a user in a controller or seeder:
use Cura\UserPolicyBundle\Models\UserPolicy;
$user->policies()->attach('admin'); // Attach by policy key
// OR
$user->policies()->attach(1); // Attach by ID
Check Policy in Middleware Create middleware to verify policies:
namespace App\Http\Middleware;
use Closure;
use Cura\UserPolicyBundle\Services\UserPolicyService;
class CheckPolicy
{
protected $policyService;
public function __construct(UserPolicyService $policyService)
{
$this->policyService = $policyService;
}
public function handle($request, Closure $next, $policyKey)
{
if (!$this->policyService->userHasPolicy($request->user(), $policyKey)) {
abort(403);
}
return $next($request);
}
}
Register in app/Http/Kernel.php:
'check.policy' => \App\Http\Middleware\CheckPolicy::class,
Dynamic Policy Assignment Use events to assign policies dynamically (e.g., on user registration or role change):
// In EventServiceProvider
protected $listen = [
'App\Events\UserRegistered' => [
'App\Listeners\AssignDefaultPolicy',
],
];
Listener example:
public function handle(UserRegistered $event)
{
$event->user->policies()->attach('editor');
}
Policy-Based Route Groups
Group routes by policy requirements in routes/web.php:
Route::middleware(['auth', 'check.policy:admin'])->group(function () {
Route::get('/admin/dashboard', 'AdminController@dashboard');
});
Policy Scopes for Eloquent Extend Eloquent queries with policy-based scopes:
namespace App\Models;
use Cura\UserPolicyBundle\Scopes\UserPolicyScope;
class Post extends Model
{
public function scopeEditableBy($query, $user)
{
return $query->whereHas('user.policies', function ($q) use ($user) {
$q->whereIn('key', ['editor', 'admin'])
->wherePivotUserId($user->id);
});
}
}
Laravel Nova Integration Extend Nova toolbars or detail views to display user policies:
public function fields(Request $request)
{
return array_merge(parent::fields($request), [
Text::make('Policies', function () {
return $this->user->policies->pluck('name')->implode(', ');
}),
]);
}
API Resource Policies Use policies in API resources for authorization:
public function toArray($request)
{
return [
'data' => $this->resource,
'authorized' => $request->user()->hasPolicy('editor'),
];
}
Policy-Based Notifications Trigger notifications based on policy changes:
$user->policies()->syncWithoutDetaching(['admin']);
event(new PolicyUpdated($user, 'admin'));
Circular Dependencies
Avoid circular references when defining policies (e.g., admin policy granting access to modify other admin policies). Use middleware to break cycles:
if ($request->user()->hasPolicy('super_admin')) { ... }
Mass Assignment Risks
Policies are pivot tables; ensure guarded or fillable is configured in UserPolicy model:
protected $guarded = ['user_id', 'policy_id'];
Caching Quirks Clear policy-related caches after bulk updates:
php artisan cache:clear
php artisan view:clear
Policy Assignment Issues Verify pivot table structure:
php artisan schema:dump
Check for missing foreign keys or constraints.
Middleware Debugging Log policy checks in middleware:
\Log::debug('Policy check for ' . $policyKey, [
'user_id' => $request->user()->id,
'has_policy' => $this->policyService->userHasPolicy($request->user(), $policyKey),
]);
Custom Policy Providers
Override the default policy provider in config/user-policies.php:
'provider' => \App\Services\CustomPolicyProvider::class,
Implement Cura\UserPolicyBundle\Contracts\PolicyProvider:
public function getPolicies()
{
return [
'custom_policy' => [
'name' => 'Custom Policy',
'description' => 'Dynamic policy',
],
];
}
Policy Events Extend the bundle’s events for custom logic:
// Listen for policy attachment
PolicyAttached::class => function (PolicyAttached $event) {
\Log::info("User {$event->user->id} assigned policy {$event->policy->key}");
},
Policy Validation Add validation rules for policy keys:
use Cura\UserPolicyBundle\Rules\ValidPolicy;
$request->validate([
'policy' => ['required', new ValidPolicy],
]);
Policy Key Case Sensitivity
Policy keys are case-sensitive by default. Normalize keys in CustomPolicyProvider if needed:
return array_change_key_case($policies, CASE_LOWER);
Default Policy Handling
Set a default policy in config/user-policies.php:
'default_policy' => 'guest',
Use in middleware:
if (!$this->policyService->userHasPolicy($user, $policyKey)) {
$user->policies()->attach(config('user-policies.default_policy'));
}
How can I help you explore Laravel packages today?