list users by a specific role with laravel\spatie
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Listing Users by Role with Laravel and Spatie: Fixing the hasRole Error
As a senior developer working with Laravel, we frequently encounter scenarios where we need to filter Eloquent collections based on complex relational data, such as roles or permissions. When integrating powerful packages like the Spatie package for role management, understanding how Eloquent relationships work is crucial.
This post addresses a very common stumbling block: attempting to use methods from a relationship (like hasRole()) directly on an Eloquent Collection, which leads to the error: Method Illuminate\Database\Eloquent\Collection::hasRole does not exist. We will diagnose why this happens and provide the correct, efficient way to list users based on their assigned roles.
The Misconception: Why hasRole() Fails on Collections
The error you encountered stems from a misunderstanding of where methods like hasRole() reside in the Laravel ecosystem. Methods that operate on specific models (like User models) or relationships are instance methods, not static methods available directly on the entire collection object.
When you call App\User::all(), you retrieve a standard Eloquent Collection. This collection itself does not inherently possess role-checking logic across all its items. The hasRole() method is defined on the individual User model or within specific scopes applied to that model, allowing you to check the permissions for a single user.
To filter the results efficiently, we must shift the filtering logic from the presentation layer (Blade view) into the data retrieval layer (Controller or Eloquent query). This aligns perfectly with Laravel's philosophy of keeping business logic close to the data source.
The Correct Approach: Filtering in the Database Query
The most performant way to retrieve users belonging to a specific role is by using Eloquent's powerful querying capabilities, specifically whereHas(). This method allows you to constrain your main query based on the existence of related models that satisfy certain conditions.
Step 1: Identifying Users with a Specific Role (Controller Logic)
Instead of loading all users and then trying to check roles in the view, we instruct the database to only return users who have the desired role.
Here is how you would fetch all users who have the 'doctor' role using the Spatie package:
use App\Models\User;
class UserController extends Controller
{
public function listDoctors()
{
// Use whereHas to filter the User collection based on the existence of a 'doctor' role.
$doctors = User::whereHas('roles', function ($query) {
$query->where('name', 'doctor');
})->get();
return view('users.doctors', compact('doctors'));
}
}
Explanation:
User::whereHas('roles', ...): This tells Eloquent to only select users for whom the relatedrolesrelationship exists and matches the criteria we define.function ($query) { $query->where('name', 'doctor'); }: This is the closure that filters the relationship. It ensures that among the roles assigned to the user, at least one role has the name'doctor'.
This method leverages the database indexes and relationships efficiently, making it significantly faster than fetching all users and iterating over them in PHP memory. For more complex filtering involving multiple roles or permissions, mastering Eloquent relations is key—a fundamental skill when building robust applications with Laravel.
Step 2: Displaying the Data in the View (Blade)
Once you have the correctly filtered collection ($doctors), displaying the list becomes straightforward and safe within your Blade template. You no longer need to rely on an invalid method call; you simply iterate over the results you have already retrieved.
Here is how you can use the result from the controller in your view:
{{-- resources/views/users/doctors.blade.php --}}
<label for="select_doctor">{{ __('Select doctor') }}</label>
<select name="select_doctor" id="select_doctor" class="form-control">
<option value="">----- * ----</option>
@foreach ($doctors as $doctor)
{{-- Access the user's name directly from the loaded model --}}
<option value="{{ $doctor->id }}">{{ $doctor->name }}</option>
@endforeach
</select>
Conclusion: Embracing Eloquent Relationships
The core takeaway here is that when working with complex data structures in Laravel, avoid trying to execute relational logic inside simple loops on collections. Instead, delegate filtering and relationship checking to the Eloquent query builder using methods like whereHas(). This practice ensures your application remains fast, scalable, and adheres to best practices, whether you are building simple forms or complex APIs. As we continue to explore powerful tools like Laravel and its ecosystem, focusing on how Eloquent handles relationships will always lead to more elegant and efficient code.