Core concepts

Menus

How menu visibility works

Menus are a separate concern from route permissions — a role can be allowed to view a route but still have its menu entry hidden, and vice versa isn't enforced automatically, so pair both when you want a fully hidden feature.

Each Menu belongs to a role through the bwp_menu_role pivot, with a disabled flag. A brand-new role starts with every menu item disabled — RoleObserver provisions that row automatically, the same way it provisions "no access" for routes (see Roles).

Defining the base tree

'base_menus' => [
    [
        'key'   => 'leads',
        'label' => 'Leads',
        'icon'  => 'users',
        'route' => 'leads.index',
    ],
    [
        'key'    => 'leads-create',
        'label'  => 'New lead',
        'icon'   => 'plus',
        'route'  => 'leads.create',
        'father' => 'leads',
    ],
],

Use father to nest an item under a parent's key. The seeder resolves these into father_id foreign keys on bwp_menus.

Enabling and disabling for a user's role

$user->setMenuAccess('leads', true);
$user->setMenuAccess('leads', false);
$user->setMenuAccess(3, true); // by menu ID instead of key

Parent propagation

This is the detail that makes menu management pleasant instead of fiddly: enabling or disabling a child automatically syncs its parent.

  • Enabling a child → its parent is enabled too, so the tree never shows an orphaned child under a hidden parent.
  • Disabling a child → the parent is only disabled if no other children of that parent are still enabled.
$user->setMenuAccess('leads-create', true);
// → 'leads-create' enabled, 'leads' (its parent) enabled automatically

$user->setMenuAccess('leads-create', false);
// → 'leads-create' disabled; 'leads' stays enabled only if it has
//   another enabled child, otherwise it's disabled too

Setting multiple items at once

$user->setMenuAccesses([
    'leads'        => true,
    'leads-create' => true,
    'deals'        => false,
]);

Checking and reading

$user->hasMenuAccess('leads');   // bool
$user->hasMenuAccess(3);          // by ID

$menu = $user->getMenu();          // Collection of enabled Menu items for this user's role

getMenu() is what you render navigation from — it already reflects role-based visibility, so the view layer doesn't need to know about permissions at all:

@foreach ($user->getMenu() as $item)
    <a href="{{ route($item->route) }}">
        <i class="{{ $item->icon }}"></i> {{ $item->label }}
    </a>
@endforeach

Managing menus visually

The bundled admin UI exposes this at /bwp/menus (editing the tree itself) and /bwp/menus/roles (toggling visibility per role) — see Examples for a walk-through.