Weave 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

Laravel Static Permission Laravel Package

sourceboat/laravel-static-permission

Laravel Static Permission lets you define permissions in code or configuration instead of database tables. Check access with familiar Gate, middleware, and Blade helpers—ideal for apps with fixed, predictable authorization rules and no permission-table overhead.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it alongside the permission backend it abstracts — in practice this is almost always spatie/laravel-permission, since the package is a thin authoring/sync layer rather than a full ACL implementation:

composer require sourceboat/laravel-static-permission
php artisan vendor:publish --tag=static-permission

Publish the config first and read it end-to-end before touching anything else — that file is where your roles and permissions actually live, and it is the closest thing this package has to documentation. Most confusion ("where do I add a role?", "why isn't my permission showing up?") is answered by the config's array shape.

The first real use case is replacing DB-seeded permissions with a config-driven source of truth:

// config/static_permission.php (shape varies by version; check the published file)
return [
    'permissions' => [
        'posts.view', 'posts.create', 'posts.update', 'posts.delete',
        'users.manage',
    ],
    'roles' => [
        'admin'  => ['*'],
        'editor' => ['posts.*'],
        'viewer' => ['posts.view'],
    ],
];

Then sync it into whatever tables the underlying permission package uses:

php artisan static-permission:sync   # command name depends on version — check `php artisan list`

From that point on the developer workflow is normal Laravel authorization: $user->can('posts.update'), $user->hasRole('editor'), @can in Blade, can: middleware on routes. The package does not change how you check permissions — only how you declare them.

Implementation Patterns

Config-as-source-of-truth on deploy. Commit the config, and call the sync command as part of your deploy pipeline (after migrate, before any code that assigns roles to users). This is the central pattern the package exists to support: reviewers see permission changes in diffs, and environments converge automatically.

# deploy step
- php artisan migrate --force
- php artisan static-permission:sync
- php artisan cache:forget spatie.permission.cache  # if not handled internally

Role definitions as groups, not per-user grants. Define roles with wildcard/grouped permission lists and assign roles to users at the application layer (registration flows, admin UI, seeders for demo data). Keep user→role assignment out of the static config — it's per-instance state, not deployable code.

$user->assignRole('editor');

Pair with Policies for row-level rules. Static permissions answer "may this user ever update posts?"; policies answer "may they update this post?". Use the permission as the first gate in the policy, then layer ownership checks:

public function update(User $user, Post $post): bool
{
    return $user->can('posts.update')
        && ($user->hasRole('admin') || $post->author_id === $user->id);
}

Environment-specific maps. Where the config supports it, vary the role/permission map per environment (e.g. richer roles in local/demo) by keeping environment overrides in .env-selected config files, or by conditionally reading from an env var. Don't fork the config wholesale — you'll drift.

Testing pattern. In setUp() of feature tests, run the sync (or call the underlying registrar) so the test database has the permissions your gates expect. Tests that assert $user->can(...) will silently pass false if the permission row was never created — that's the most common false-negative in this style of setup.

Gotchas and Tips

  • Sync is a required step, not a convenience. New permissions won't exist until synced; can() returns false for unknown abilities rather than erroring. If a feature "works locally but not in prod," check whether the deploy ran sync before rolling out the code that checks the new ability.
  • Removals are usually not destructive by default. Deleting a permission or role from config often leaves the DB row behind unless you opt into pruning/force flags. Audit before enabling destructive sync, and read the config for a prune/delete_* option.
  • Cache invalidation. The underlying spatie/laravel-permission caches the permission map. If your sync command doesn't flush it automatically, you'll see stale results after deploying a config change. Wire cache:forget spatie.permission.cache (or the modern equivalent) into the sync flow.
  • Config caching. If you use php artisan config:cache, the static config is frozen at cache time. A deploy that caches config before pulling the new file will sync the old map — order your deploy steps explicitly.
  • Guard names matter. Multi-guard apps must define permissions per guard (or accept the default). A role defined for web won't resolve for an api guard user; check the config's guard key.
  • Wildcards are a convention, not magic. posts.* is typically expanded by the package at sync time, not resolved at check time. can('posts.update') only works because the wildcard was expanded into a concrete row — so verify expansion happened if checks fail.
  • Extension points to know. The package is small; expect to override the sync command (bind your own subclass in a service provider) if you need to run post-sync hooks, notifications, or audit logging. Similarly, wrap the config loader if you need dynamic maps from outside PHP files.
  • Version churn. With a small, low-star package, the artisan command name, config key names, and array shape can change between minor releases. Pin a version in composer.json and re-read the published config after any upgrade rather than assuming backward compatibility.
  • Not a substitute for a database-backed admin UI. If non-developers need to mint roles or grant permissions at runtime, this package fights you. Use it only where "permissions live in git" is an acceptable governance model.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect