Courses

Laravel 11 Multi-Tenancy: All You Need To Know

Filter Model By user_id: With where() and Global Scope

So now that we have the user_id in our DB table, let's filter by that, so that each user would access only their projects.


Option 1. Straightforward where()

Of course, the most typical filter method is to filter with the where() statement.

app/Http/Controllers/ProjectController.php:

class ProjectController extends Controller
{
public function index()
{
$projects = Project::where('user_id', auth()->id())->get();
 
return view('projects.index', compact('projects'));
}
 
// ...
}

If I register with a new user and visit the Projects page, I will not see any projects.

But the problem is I can still enter the Edit page for other projects by guessing the URL of /projects/1/edit even if different user has created that project.

So, you must add those where() conditions in all methods. For example, you would need to do some if-statements and possibly abort with a "403 Forbidden" status.

app/Http/Controllers/ProjectController.php:

class ProjectController extends Controller
{
// ...
 
public function edit(Project $project)
{
if ($project->user_id !== auth()->id()) {
abort(403);
}
 
return view('projects.edit', compact('project'));
}
 
// ...
}

But there are more elegant solutions than this repeating condition.


Option 2. Query Scopes.

A general way of repeating conditions like this is to have Eloquent scope. You can generate a new global scope or use an anonymous global scope.

app/Models/Project.php:

use Illuminate\Database\Eloquent\Builder;
 
class Project extends Model
{
protected $fillable = [
'name',
'user_id',
];
 
protected static function booted(): void
{
static::creating(function (Project $project) {
$project->user_id = auth()->id();
});
 
static::addGlobalScope(function (Builder $builder) {
$builder->where('user_id', auth()->id());
});
}
}

As you can see, this global scope is defined in the same place as creating the records.

Now, we don't need any checks in the Controller. Global scope will be launched on any Eloquent query in any Controller, Artisan command, etc.

app/Http/Controllers/ProjectController.php:

class ProjectController extends Controller
{
public function index()
{
$projects = Project::where('user_id', auth()->id())->get();
$projects = Project::all();
 
return view('projects.index', compact('projects'));
}
 
// ...
 
public function edit(Project $project)
{
if ($project->user_id !== auth()->id()) {
abort(403);
}
 
return view('projects.edit', compact('project'));
}
 
// ...
}

If we now visit any previously created projects edit page, we will get a 404 page. After creating a new project in the table, we will only see that new project. We won't see projects from other users.

Even in the task creation form, we can see only projects that are created by the currently authenticated user because of the applied Global Scope.


In the next lesson, we'll see how to deal with repeating conditions on multiple models. So what if you have that ->where('user_id', auth()->id()) on tasks, projects, or other models? How do you do that more elegantly with traits in Laravel?

avatar

This is really cool and I like the approach with the global scope very much. But what if an Admin signs in, that is able to see a list of all projects?

avatar

You can add an if statement at the scope, that if the user is admin then not apply the global scope.

avatar

Hi, the video links seems faulty :

This site can’t be reached The webpage at https://player.vimeo.com/video/709093819?h=313c197da8&badge=0&loop=false&byline=false&portrait=false&title=false&speed=true&transparent=0&gesture=media might be temporarily down or it may have moved permanently to a new web address.

avatar

Hi, this could be due to country limitations done by Vimeo. Please check if vimeo.com works in your country (there is nothing we can do to change which countries they support, sorry)

avatar

The problem with the global scope is that in jobs or command there is not authenticated user, auth() will return null.

Why not just use auth()->user()->projects in the controller ?