Core Concepts
Route Access
Route access stores every route of your application in system_routes and lets you mark, per role, whether the role may use it (system_routes_in).
Status: syncing routes and storing access rows works. The admin screens for routes (
/systemroles/admin/roles/routes/indexand/manage) do not ship with views yet. Opening them sends you back with "This screen is not available yet". Until they ship, manage route access with the sync command and code, as shown below, or publish the views and add your own.
Syncing routes#
php artisan systemroles:syncThis reads every registered route (like php artisan route:list) and stores it in system_routes:
| Column | Example |
|---|---|
route_ |
`GET |
route_ |
admin/ |
route_ |
invoices. |
route_ |
admin |
route_ |
App\ |
route_ |
web, auth |
route_ |
`GET |
route_ |
JSON with the full scan result |
A route is identified by method + URL, not by name, because many routes have no name or share one. Running the command again updates changed routes and adds new ones. It prints how many were added and updated. Routes removed from your code stay in the table.
Run it after every deploy, or set scan_on_request to true to sync whenever the Routes screen opens.
Granting route access#
Each row in system_routes_in links one role to one route:
| in_route | Meaning |
|---|---|
1 |
Allowed |
0 |
Denied |
2 |
Reserved |
There is one row per route per role (unique on route_id + role_id).
From code:
use BabeRuka\SystemRoles\Models\SystemRoutes;
use BabeRuka\SystemRoles\Models\SystemRoutesIn;
$route = SystemRoutes::where('route_name', 'invoices.index')->firstOrFail();
SystemRoutesIn::updateOrCreate(
['route_id' => $route->route_id, 'role_id' => 3],
['in_route' => '1']
);Granting a role every route under a prefix:
SystemRoutes::where('route_url', 'like', 'reports/%')->each(function ($route) {
SystemRoutesIn::updateOrCreate(
['route_id' => $route->route_id, 'role_id' => 3],
['in_route' => '1']
);
});Checking route access#
The package does not include a route check helper yet. This middleware checks the current route against system_routes_in:
namespace App\Http\Middleware;
use BabeRuka\SystemRoles\Models\SystemRoutes;
use BabeRuka\SystemRoles\Models\SystemRoutesIn;
use BabeRuka\SystemRoles\Services\SystemRolesService;
use Closure;
use Illuminate\Http\Request;
class RouteAccess
{
public function handle(Request $request, Closure $next)
{
$route = $request->route();
$roleId = SystemRolesService::roleIdForUser(optional($request->user())->getKey());
if (SystemRolesService::isSuperAdminRole($roleId)) {
return $next($request);
}
$stored = SystemRoutes::where('route_method', implode('|', $route->methods()))
->where('route_url', $route->uri())
->first();
$allowed = $stored && SystemRoutesIn::where('route_id', $stored->route_id)
->where('role_id', $roleId)
->where('in_route', '1')
->exists();
abort_unless($allowed, 403);
return $next($request);
}
}Routes that are not synced are denied by this middleware. Decide what you want for those (deny is safer) and run systemroles:sync after adding routes.
For busy apps, cache the allowed route ids per role:
$allowedIds = cache()->remember("systemroles.routes.$roleId", 300, fn () =>
SystemRoutesIn::where('role_id', $roleId)->where('in_route', '1')->pluck('route_id')->all()
);Clear that cache key when you change route access.