Routes, permissions and admin screens
Wiring a module into the front end, the group matrix and the admin navigation.
Routes#
Two files, loaded from boot():
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
$this->loadRoutesFrom(__DIR__.'/../routes/admin.php');
Name every route with your slug as the prefix (directory.index,
admin.directory.settings). That is what makes Route::has() a reliable guard
for other modules, and what lets the panel link to your settings screen.
Admin routes go behind the panel middleware, exactly like the core ones. Never roll your own admin authentication.
Permissions#
Declare them in the manifest:
"provides": { "permissions": ["directory.view", "directory.manage"] }
They are registered into the group matrix on install and appear in Members > Member groups alongside every other module's permissions. Check them the usual way:
Gate::allows('directory.manage');
Three rules:
- Never invent a parallel permission system. The group matrix is the one answer to "who can do this", and buyers already understand it.
- Check on the server, always. Hiding a button is presentation, not security.
- Scope where it makes sense. A permission per area of your module beats a single on-off switch.
Admin navigation#
The panel navigation is built from a registry, not hardcoded. You contribute
through the admin.menu slot in boot():
Hooks::on('admin.menu', fn (HookContext $ctx) => [
'label' => __('directory::admin.title'),
'url' => route('admin.directory.settings'),
'order' => 40,
]);
Only add entries the current administrator can actually reach. A menu item that leads to a 403 is noise.
UI slots#
The core declares these, and a module can declare its own:
admin.menu, admin.dashboard.widgets, profile.tabs, profile.sidebar,
settings.pages, search.sources, editor.toolbar, member.actions,
head.meta, body.end.
Registering:
Hooks::on('profile.tabs', fn (HookContext $ctx) => [
'label' => __('directory::labels.items'),
'url' => route('directory.member', $ctx->get('member')),
'order' => 20,
]);
Consuming, in a Blade view:
@hook('profile.tabs', ['member' => $member])
Contributions are collected, ordered by order and rendered.
Search#
Contribute to the site search through search.sources. Your source is
responsible for applying your own visibility rules: a result must never appear
for someone who could not open the page it points at.
Screens#
Admin screens use the panel components so your module looks like the product rather than like a plugin. In particular:
- square icon buttons of a uniform size, with a chevron dropdown for the secondary actions, in every list;
- the product modal for confirmation, never
window.confirm; - selects with the custom chevron, never the bare native one;
- zero axe violations on every screen you add, in both light and dark.