Reference

Examples

Creating a user with a cloned role

The common flow: a new user is created and immediately needs its own role, so that permission edits later never touch the shared base role.

use HenryHt\BitwisePermission\Services\RoleCloneService;
use HenryHt\BitwisePermission\Models\Role;

class RegisterUserAction
{
    public function __construct(protected RoleCloneService $roleClone) {}

    public function handle(array $data): User
    {
        $user = User::create([
            'name'     => $data['name'],
            'email'    => $data['email'],
            'password' => bcrypt($data['password']),
        ]);

        $baseRole = Role::where('name', 'agent')->firstOrFail();

        $this->roleClone->cloneForUser($user, $baseRole);

        return $user;
    }
}

Protecting a resource controller

Route::middleware('bwp.permission')->group(function () {
    Route::get('/leads', [LeadController::class, 'index'])->name('leads.index');
    Route::get('/leads/{lead}', [LeadController::class, 'show'])->name('leads.show');
});

Route::middleware('bwp.permission:create')->group(function () {
    Route::get('/leads/create', [LeadController::class, 'create'])->name('leads.create');
    Route::post('/leads', [LeadController::class, 'store'])->name('leads.store');
});

Route::middleware('bwp.permission:update')->group(function () {
    Route::put('/leads/{lead}', [LeadController::class, 'update'])->name('leads.update');
});

Route::middleware('bwp.permission:delete')->group(function () {
    Route::delete('/leads/{lead}', [LeadController::class, 'destroy'])->name('leads.destroy');
});

Run php artisan bwp:sync-routes after adding these so the wildcards (leads.*) are registered and available to assign in /bwp/accesses.

Conditional UI

<div class="toolbar">
    @if(auth()->user()->canCreate())
        <a href="{{ route('leads.create') }}" class="btn">New lead</a>
    @endif

    @if(auth()->user()->canDelete())
        <button wire:click="delete({{ $lead->id }})">Delete</button>
    @endif
</div>

@if(auth()->user()->isSuperAdmin())
    <span class="badge">Super Admin</span>
@endif

Adding a custom bit

Add it to bits alongside the existing ones, as the next power of two:

'bits' => [
    'view'          => 1,
    'export'        => 2,
    'create'        => 4,
    'update'        => 8,
    'delete'        => 16,
    'modify_access' => 32,
    'approve'       => 64, // new
],

Then check it with canCustom():

if ($user->canCustom('approve', 'expenses.approve')) {
    // ...
}

Adjusting a single user's permission

$user->setPermission('reports.*', 'view + export');

This only ever touches the Access row for the user's own (cloned) role — the manager base role that user started from is untouched, so every other manager keeps the original permission set.

Building a permission-management form

$route  = 'leads.*';
$current = $user->getPermissionFor($route); // ?Permission, for pre-selecting a value

// on submit
$user->setPermission($route, $request->input('permission'));

For editing several routes in one screen:

$all = $user->getAllPermissions(); // route_name => Permission

// on submit
$results = $user->setPermissions($request->input('permissions'));

The admin UI

With the bundled Livewire components (published automatically, gated by the gate config key), you get:

Route Purpose
/bwp/roles Create, rename and inspect roles
/bwp/permissions Manage named permission groups and their bit combinations
/bwp/routes Review and edit registered route wildcards
/bwp/accesses Assign a permission to a role for a given route
/bwp/menus Build and reorder the navigation tree
/bwp/menus/roles Toggle menu visibility per role

Restrict access with the gate closure in config/bitwise-permission.php:

'gate' => fn ($user) => $user->hasRole('super_admin'),