Use Laravel Observers and Global Scopes to Create User Multi-Tenancy
Multi-tenant application are pretty common these days, where database are entries can be accessed only by users who created them. In this article, I will show the simplest way to achieve it in Laravel.
First, I want to admit that there are a lot of ways to implement or even think about multi-tenancy. For more thoughts on this, I recommend a wonderful presentation called Multi-Tenant Laravel by Tom Schlick. In this article, I will show multi-tenancy based on single database and Laravel Global Scopes.
Now, let's add multi-tenancy to our database entries. Let's imagine we have DB table called posts, and we want each user to see only their own posts.
Step 1. User's field in DB table
First, we need to add user's field into posts table.
class PostObserver
{
/**
* Handle to the post "creating" event.
*
* @param \App\Post $post
* @return void
*/
public function creating(Post $post)
{
$post->user_id = auth()->id();
}
}
And we register the observer in app/Providers/AppServiceProvider.php:
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Post::observe(PostObserver::class);
}
}
Step 3. Filter by User: Access only your own posts
Final goal is to allow access only to posts that you created yourself. To do that, we could add something like ->where('user_id', auth()->id()) in all Eloquent queries, but there's a better way - to use Eloquent Global Scopes.
We create a file app/Scopes/PostUserScope.php:
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class PostUserScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
if (auth()->check()) {
$builder->where('user_id', auth()->id());
}
}
}
And we attach it to app/Post.php model by loading in in model's boot() method:
namespace App;
use App\Scopes\PostUserScope;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['name', 'user_id'];
/**
* The "booting" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new PostUserScope);
}
}
And that's it! From now, all the queries for Post model will be filtered by logged in user. So we've achieved our goal, to build a basic multi-tenancy system, where every user can create/manage only their own data.
No comments or questions yet...