minaremonshaker's avatar

Spatie QueryBuilder Missing findByName() on Role Model

Hi everyone,

In my Laravel API project using Spatie Laravel Permission (with Sanctum guard) and Spatie Query Builder, I'm trying to fetch permissions for a specific role like 'user', but running into an issue with QueryBuilder.

These attempts fail with Call to undefined method Spatie\QueryBuilder\QueryBuilder::findByName():

QueryBuilder::for(Role::class)->where('name', 'user')->permissions()->get()

or

QueryBuilder::for(Role::class)->findByName('user', 'sanctum')->permissions()

However, this direct Eloquent call works perfectly and returns all related permissions:

Role::findByName('user', 'sanctum')->permissions()

Why can't I chain findByName() or similar scopes directly on the QueryBuilder instance?

Is there a proper way to combine Spatie Query Builder with Role model scopes/finders from laravel-permission to get a role by name and then load its permissions? Should I use allowedIncludes('permissions') or a different approach like where('name', 'user')->with('permissions')?

Using Laravel 12, latest Spatie packages. Thanks!

0 likes
4 replies
LaryAI's avatar
Level 58

Great questions! Let's clarify what's happening:

Why can’t you call findByName() or similar methods on QueryBuilder?

  • Spatie's QueryBuilder is a query builder wrapper and does not inherit custom Eloquent methods (like findByName from Spatie's Permission package).
  • findByName is a static method on the Role model, not a query scope or Eloquent builder macro. The static method executes an internal query and returns a Role instance.
  • Query scopes (e.g., scopeActive) can be chained onto query builders, but static helper methods (like findByName) cannot.

What to do instead?

1. Fetch a Role by Name and its Permissions

The Standard Eloquent way is the right way:

$role = Role::findByName('user', 'sanctum');
$permissions = $role->permissions;

Or eager load:

$role = Role::with('permissions')->findByName('user', 'sanctum');

2. Using QueryBuilder for Filtering (if you need query params/filters)

If you want to use Spatie QueryBuilder (maybe to support filters), filter by name instead, then eager load permissions:

$role = QueryBuilder::for(Role::class)
    ->allowedIncludes('permissions')
    ->where('name', 'user')
    ->first();

Or, if you want to use filters:

$role = QueryBuilder::for(Role::class)
    ->allowedFilters('name')
    ->allowedIncludes('permissions')
    ->where('name', 'user')
    ->first();

Or, with a query parameter:

/api/roles?filter[name]=user&include=permissions

Your code:

$role = QueryBuilder::for(Role::class)
    ->allowedFilters('name')
    ->allowedIncludes('permissions')
    ->first();

3. Summary Table

Task Solution
Role by name, permissions Role::findByName(...)->permissions
QueryBuilder filters, includes Use allowedFilters, allowedIncludes, and where('name', ...)

Conclusion

  • You cannot use findByName on the QueryBuilder. It's a static model method.
  • Use QueryBuilder’s filtering and relationships to filter by name and include permissions, or stick with the Eloquent solution directly.

Example:

$role = QueryBuilder::for(Role::class)
    ->allowedFilters('name')
    ->allowedIncludes('permissions')
    ->where('name', 'user')
    ->first();

$permissions = $role ? $role->permissions : collect();

Let me know if you want to see a controller or route example!

tykus's avatar
tykus
Best Answer
Level 104

Here's your answer...

However, this direct Eloquent call works perfectly

Why are you using Spatie's QueryBuilder for these queries at all; you are wrapping a perfectly fine Eloquent query for no apparent reason because AFAIK you are taking nothing from the Request to filter, sort, include in the results?

minaremonshaker's avatar

Hi, I’ve created a new version where I need to sort and search the results. Of course, I can normally handle this using the built-in Eloquent model — that’s the current case.

note: this is not the last version i still making updates to it

  • this from the service
    public static function getPermissionsOfRolesName(string $role, ?array $filters)
    {
        extract($filters, EXTR_IF_EXISTS);
        
        return QueryBuilder::for(Role::findByName($role)->permissions())
            ->defaultSort('id')
            ->allowedSorts([
                'name',
                AllowedSort::field('created', 'created_at'),
                AllowedSort::field('updated', 'updated_at'),
            ])
            ->paginate(10);
    }

Please or to participate in this conversation.