Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kreierson's avatar

Spatie Permission Extending Role Model

I am trying to extend the Spatie\Permission\Models\Role class. I created a new Role class in App\Models\Role like below

use Spatie\Permission\Models\Role as SpatieRole;

class Role extends SpatieRole
{
    use HasFactory;
}

Everything works and I can create roles and assign them to users. Fine, but as soon as I update the app\config\permission.php to use my model it breaks all tests, but the application works fine. Below is my permission.php

/*
         * When using the "HasRoles" trait from this package, we need to know which
         * Eloquent model should be used to retrieve your roles. Of course, it
         * is often just the "Role" model but you may use whatever you like.
         *
         * The model you want to use as a Role model needs to implement the
         * `Spatie\Permission\Contracts\Role` contract.
         */

        'role' => App\Models\Role::class,

In my test I am simply creating a user, assigning them a role which has permission to view a route.

// My Test
$adminUser = User::factory()->create(); // Create user
$adminUser->assignRole('Admin'); // assign admin role
$this->actingAs($adminUser); // Login as admin
$this->get('admin-dashboard'); // visit admin route

// My route
Route::get('admin/dashboard', AdminDashboardController::class)->name('admin-dashboard')->middleware(['can:Administer Site']);

Like i said this work fine when the models.role attribute in the permission.php config file uses the spatie Role class, but once I switch it to my role which extends the Spatie role i get a 403 forbidden in my tests. What's even weirder is this works in the browser just fine. I can visit the route.

0 likes
9 replies
tykus's avatar
tykus
Best Answer
Level 104

You don't signin the admin

$this->actingAs($adminUser)->get('admin-dashboard);

Do you seed the roles and permissions separately somewhere in the test suite?

kreierson's avatar

@tykus sorry I forgot to add that line. I do actually sign in as the admin. I willl update my original post.

And yes to answer your question I do seed the roles and permissions directly in the database migration that is provided by the package.

tykus's avatar

@kreierson okay, can you check which class is in config and being queried? Is it possible the config is cached?

dump(config('permission.models.role')); // App\Models\Role::class ???
$adminUser = User::factory()->create(); 
// ... etc
kreierson's avatar

It might be worth mentioning when I do the following

$adminUser->assignRole('Admin');
$adminUser->forgetCachedPermissions();

The test passes, but on line 144 of the "HasRole" trait it seems it should do this for me in the "assignRole()" method.

// HasRoles Trait
if (is_a($this, get_class($this->getPermissionClass()))) {
        $this->forgetCachedPermissions();
}
tykus's avatar

@kreierson what happens if you get a fresh instance of $adminUser?

$adminUser = User::factory()->create(); 
$adminUser->assignRole('Admin'); // assign admin role
$this->actingAs($adminUser->fresh()); // Login as admin
kreierson's avatar

@tykus Its also odd that when I do

dd($adminUser->can('Administer Site')) // Administer Site is a permission on the Admin role

It returns false, HOWEVER, when I do

dd($adminUser->getPermissionsViaRoles()->pluck('name')->toArray());

// Then I get the following output
array:1 [
  0 => "Administer Site"
]

Odd to me that the "can" method returns false, when in fact the user has that permission.

kreierson's avatar

@tykus I found the issue! In my migration where I was seeding the roles and permissions I was still using the Spatie\Permission\Models\Role to create the roles and Spatie\Permission\Models\Permission class to create the permissions. I updated these to use my models and it seems to work. Thank you for your time and getting me to think about where i was seeding my roles and permissions.

Please or to participate in this conversation.