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

Novaspatiepermissions Laravel Package

fullstackllc/novaspatiepermissions

NovaSpatiePermissions integrates Spatie's Laravel Permission package with Laravel Nova, providing ready-made Nova resources and fields to manage roles, permissions, and model assignments directly from your admin panel. Streamline access control without writing custom Nova tooling.

View on GitHub
Deep Wiki
Context7

Getting Started

Install alongside its two hard dependencies. This package is a Nova tool, not a standalone authorization system — it wraps spatie/laravel-permission and needs Nova installed to do anything.

composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate

composer require fullstackllc/novaspatiepermissions

Register the tool in app/Providers/NovaServiceProvider.php:

public function tools()
{
    return [
        new \FullstackLLC\NovaSpatiePermissions\NovaSpatiePermissions(), // verify exact namespace from the package's src/
    ];
}

Look here first, in this order:

  1. The package's src/ directory — confirm the tool class name, whether it registers Nova resources (Role/Permission) or a custom tool route, and whether it ships a Role field for user forms.
  2. config/permission.php — guard names, table names, and cache.expiration_time. Almost every "why doesn't this work" question traces back here.
  3. Your NovaServiceProvider::tools() / resources() — you need to know what the package auto-registers so you don't double-register a Role resource of your own.

First real use case: open Nova as an admin → find the Roles screen the tool adds → create editor, attach posts.update and posts.publish permissions → go to a User detail page → assign the editor role → log in as that user and confirm $user->can('posts.update') returns true. If that loop works end-to-end, the integration is correct.

Reality check on adoption: this package shows zero dependents and no stars. That doesn't make it bad, but it means you are the QA team. Budget an hour to read the source (it's small) before committing, and pin the version in composer.json. Compare against vyuldashev/nova-permission or building a 60-line Role resource yourself — for many teams the DIY route is cheaper than debugging an unmaintained wrapper.

Implementation Patterns

Roles on the User resource. Most teams don't want users editing permissions directly; they want a role picker. Put a BelongsToMany (or the package's own field, if it ships one) on app/Nova/User.php:

use Laravel\Nova\Fields\BelongsToMany;

public function fields(Request $request)
{
    return [
        // ...
        BelongsToMany::make('Roles', 'roles', \Spatie\Permission\Models\Role::class)
            ->searchable(),
    ];
}

Alternatively, if the package provides a Role field, prefer it — it handles multi-select + guard filtering for you. Check src/Fields/ before hand-rolling.

Keep seeding authoritative, use the UI for exceptions. The failure mode of permission UIs is drift: someone clicks a checkbox in production, nobody records it, and php artisan migrate:fresh on staging produces a different permission set. Treat a seeder as the source of truth and the Nova tool as an escape hatch:

// database/seeders/RolesAndPermissionsSeeder.php
foreach (['posts.view','posts.create','posts.update','posts.delete'] as $p) {
    Permission::findOrCreate($p, 'web');
}

$editor = Role::findOrCreate('editor', 'web');
$editor->syncPermissions(['posts.view','posts.create','posts.update']);

Role::findOrCreate('admin', 'web')->syncPermissions(Permission::all());

app(\Spatie\Permission\PermissionRegistrar::class)->forgetCachedPermissions();

Run the seeder in CI on every deploy — it's idempotent because of findOrCreate.

Wire the checks where developers actually live. The UI only stores grants; enforcement is Spatie's job.

// Controller / policy
if (! $request->user()->hasPermissionTo('posts.publish')) {
    abort(403);
}

// Blade — requires no extra setup once roles exist
@can('posts.publish')
    <button>Publish</button>
@endcan

// Policy
public function update(User $user, Post $post): bool
{
    return $user->hasAnyPermission(['posts.update', 'posts.manage-all']);
}

Gate the tool itself. If the tool is visible to every Nova user, you've handed your permission model to whoever can log in. Add canSee / canRun (or the tool's authorize callback) checking for a super-admin role:

public function tools()
{
    return [
        (new \FullstackLLC\NovaSpatiePermissions\NovaSpatiePermissions())
            ->canSee(fn ($request) => $request->user()?->hasRole('super-admin')),
    ];
}

Extend the package's models rather than editing vendor code. Spatie lets you swap models in config/permission.php ('models' => ['role' => App\Models\Role::class, ...]). If you need a description, label, or tenant_id column, subclass and point the config at yours — then confirm the Nova tool still resolves correctly (it may type-hint Spatie\Permission\Models\Role directly, which is the classic breakage point).

Combine with Gate::before for super-admins so you never have to grant every permission explicitly:

// AuthServiceProvider::boot()
Gate::before(function ($user, $ability) {
    return $user->hasRole('super-admin') ? true : null;
});

Returning null (not false) is essential — it lets normal policy checks continue.

Gotchas and Tips

The permission cache will bite you. Spatie caches the whole permission table. Mutations made through Eloquent in a separate process (a seeder, a tinker session, a queue worker) don't invalidate the cache in your web process. If a checkbox in Nova doesn't take effect until you restart, this is why:

php artisan permission:cache-reset

Make this part of your deploy script after migrate --force and after seeding. The Nova tool generally resets the cache for changes it makes through its own UI — but not for anything else, and not across processes.

Guard mismatch is the #1 silent failure. hasPermissionTo('posts.update') uses the default guard from config/auth.php. If your web guard isn't the same one the role/permission rows were created under, every check returns false and nothing errors. Verify with:

Role::first()->guard_name;       // must match
Permission::first()->guard_name;
config('auth.defaults.guard');

If you run api + web, either create permissions per-guard explicitly (Permission::findOrCreate($name, 'api')) or accept that they're separate universes.

hasPermissionTo() throws. Wrap it or use can() when you only care about a boolean — hasPermissionTo raises PermissionDoesNotExist for typos, which is arguably a feature during development but a 500 in production.

Nova 4 vs 5 compatibility. This package predates recent Nova majors. Confirm the nova constraint in its composer.json and the ToolServiceProvider/Nova::script API it uses. If it targets Nova 3, it may load but render a blank tool panel. Test the actual UI, not just composer require.

Eager-load roles on list views. Nova's index queries hydrate every user; without $user->load('roles') or with('roles') you'll see an N+1 that grows with user count. Nova's BelongsToMany field handles this, but a hand-rolled Text::make('Roles', fn ($u) => $u->roles->pluck('name')->join(', ')) will not.

Don't delete a role you're still assigning. The pivot tables have foreign keys; the UI may let you remove a role while users still reference it depending on FK constraints. Prefer "deactivate" flags over deletes if roles are part of your business vocabulary.

Wildcards aren't native. Spatie has no posts.* matching. If your permission list is exploding, either implement a Gate::before that expands wildcards, or model coarser permissions (posts.manage). Don't assume the Nova UI's checkbox grid will scale past a few hundred permissions — it won't be pleasant.

Teams / multi-tenancy. If you enable Spatie's teams feature, the schema changes (team_id on the pivot tables) and most third-party Nova permission tools break or silently ignore the team scope. Read the package source for any team_id handling; assume it's absent until proven otherwise.

Extension points worth knowing: the tool's canSee/canRun, the config-driven model swap, and Nova's own Resource::authorizable() / fieldsForIndex. If the package fights you on any of these, the fallback is a hand-written Role resource with a BelongsToMany permission field — roughly 80 lines and fully under your control.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels