Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bouncer Laravel Package

silber/bouncer

Roles and abilities for Laravel with a clean, expressive API. Bouncer manages user authorization, supports Eloquent models, caching, gates and policies, and fluent assignment/checks like can() and is(). Great for flexible, database-driven permissions.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require silber/bouncer
    

    Publish the migration and config:

    php artisan vendor:publish --provider="Bouncer\BouncerServiceProvider"
    php artisan migrate
    
  2. First Use Case: Define a model trait and grant abilities:

    use Bouncer\Traits\Authorizable;
    
    class User extends Authenticatable
    {
        use Authorizable;
    }
    
    // Grant a user ability
    Bouncer::allow($user)->to('edit', Post::class);
    
  3. Check Abilities:

    if (Bouncer::can($user, 'edit', Post::class)) {
        // User can edit posts
    }
    

Where to Look First

  • Documentation: Bouncer README (covers core concepts like roles, abilities, and scopes).
  • Facade: Bouncer facade (Bouncer::allow(), Bouncer::can(), etc.).
  • Traits: Authorizable (for models) and HasAbilities (for checking abilities directly on models).

Implementation Patterns

Core Workflows

  1. Granting Abilities:

    // Single ability
    Bouncer::allow($user)->to('publish', Post::class);
    
    // Multiple abilities
    Bouncer::allow($user)->to(['publish', 'edit'], Post::class);
    
    // Allow everyone (requires nullable `entity_id`/`entity_type` in DB)
    Bouncer::allowEveryone()->to('view', Post::class);
    
  2. Roles:

    $adminRole = Bouncer::createRole('admin');
    Bouncer::assign($adminRole)->to($user);
    Bouncer::allow($adminRole)->to('delete', Post::class);
    
  3. Checking Abilities:

    // Direct check
    if (Bouncer::can($user, 'edit', Post::class)) { ... }
    
    // Model check (if using `HasAbilities` trait)
    if ($user->can('edit', $post)) { ... }
    
    // Check any ability
    if (Bouncer::canAny($user, ['edit', 'publish'], Post::class)) { ... }
    
  4. Forbidding Abilities:

    Bouncer::forbid($user)->to('delete', Post::class);
    // Or via roles
    Bouncer::forbid($adminRole)->to('delete', Post::class);
    

Integration Tips

  • Policies: Bouncer runs after policies by default (configurable via Bouncer::runBeforePolicies()).

    // app/Policies/PostPolicy.php
    public function update(User $user, Post $post) {
        return $post->author_id === $user->id;
    }
    

    Bouncer checks will only run if the policy allows the action.

  • Middleware:

    public function handle(Request $request, Closure $next) {
        if (!Bouncer::can($request->user(), 'view', Post::class)) {
            abort(403);
        }
        return $next($request);
    }
    
  • Multi-Tenancy:

    // Set global scope
    Bouncer::scope()->to($tenantId);
    
    // Temporary scope
    Bouncer::scope()->onceTo($tenantId, function () {
        // Queries here use $tenantId
    });
    
  • Seeding:

    public function run() {
        $adminRole = Bouncer::createRole('admin');
        Bouncer::allow($adminRole)->to('*', Post::class); // Wildcard for all abilities
        Bouncer::assign($adminRole)->to(User::first());
    }
    
  • Custom Models:

    Bouncer::useRoleModel(CustomRole::class);
    Bouncer::useAbilityModel(CustomAbility::class);
    

Gotchas and Tips

Pitfalls

  1. Policy Precedence:

    • Bouncer runs after policies by default. If you need Bouncer to run first, call:
      Bouncer::runBeforePolicies();
      
    • This is a global setting; reset with Bouncer::runAfterPolicies().
  2. Multi-Tenancy Leaks:

    • Abilities/roles granted within a scope do not leak to the global scope (fixed in v1.0.1).
    • Use Bouncer::scope()->get() to inspect the current tenant ID.
  3. Wildcard Abilities (*):

    • Granting * allows all abilities for a model. Use sparingly.
    • Wildcards are not supported for roles (e.g., Bouncer::allow($role)->to('*', Post::class) works, but Bouncer::allow($role)->to('*') does not).
  4. Soft Deletes:

    • Soft-deleting roles/abilities does not delete pivot records (fixed in v1.0.0-rc.8).
    • Use Bouncer::forceDelete($role) to clean up pivots.
  5. Cache Invalidation:

    • Clear cache after upgrading Bouncer or changing core models:
      Bouncer::refresh();
      
    • Cross-request caching may cause stale data if not refreshed.
  6. PostgreSQL/Oracle:

    • Ensure your migrations use the correct JSON column syntax for these databases.
    • Example for PostgreSQL:
      $table->json('abilities')->nullable();
      
  7. Table Prefixes:

    • If using Laravel's table prefixes, ensure Bouncer's migrations are run after your prefix is applied.

Debugging Tips

  1. Check Abilities:

    // Dump all abilities for a user
    dd(Bouncer::abilitiesFor($user));
    
    // Dump roles for a user
    dd(Bouncer::rolesFor($user));
    
  2. Log Denied Checks:

    Bouncer::setLogger(function ($message) {
        Log::debug($message);
    });
    
  3. Inspect Scopes:

    // Get current tenant ID
    $tenantId = Bouncer::scope()->get();
    
  4. Common Errors:

    • "Class not found": Ensure your custom models are registered with Bouncer::useRoleModel()/Bouncer::useAbilityModel().
    • "Column not found": Run migrations or check for table prefix conflicts.
    • "JSON column default value": Remove defaults from JSON columns (fixed in v1.0.0-rc.3).

Extension Points

  1. Custom Guards: Bouncer supports Laravel's guard system. Extend the BouncerGuard class to integrate with custom auth:

    Bouncer::extend('custom', function () {
        return new CustomBouncerGuard();
    });
    
  2. Events: Listen to Bouncer events (e.g., Bouncer\Events\AbilityGranted):

    event(new Bouncer\Events\AbilityGranted($user, 'edit', Post::class));
    
  3. Macros: Extend the Bouncer facade:

    Bouncer::macro('isAdmin', function ($user) {
        return Bouncer::has($user, 'admin');
    });
    
  4. Custom Storage: Override the default clipboard storage (e.g., for caching):

    Bouncer::useClipboard(new CustomClipboard());
    
  5. Morph Map: If using custom models, ensure they’re registered in the morph map:

    class CustomRole extends Role {
        public function getMorphClass() {
            return 'custom_role';
        }
    }
    

Performance Tips

  1. Avoid N+1 Queries: Use with() when eager-loading roles/abilities:

    $user->load('abilities', 'roles');
    
  2. Batch Operations: Use sync() to replace all abilities/roles at once:

    Bouncer::syncAbilities($user, ['edit', 'publish'], Post::class);
    
  3. Disable Caching: For development, disable caching to avoid stale data:

    Bouncer::disableCache();
    
  4. Indexing: Ensure your permissions table has indexes on:

    • entity_type
    • entity_id
    • ability
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport