Laravel - Apply Global Scopes only on certain routes with Middleware



Article image

Laravel include a Query Scopes system. It allow you to add to one or all (depend the type of scope you use) queries a scope with constraints. Query Scopes has 2 types of Scopes :

  • Global Scopes : It allow you to add constraints to all queries for a given model.
  • Local Scopes : It allow you to define common sets of query constraints that you may easily re-use throughout your application.

In this tutorial, we will use Global Scopes. The documentation of Laravel tell you how to set a Global Scope with that method :

<?php
 
namespace App\Models;
 
use App\Scopes\DisplayScope;
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model
{
     /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope(new DisplayScope);
    }
}

With this declaration, Laravel will apply the DisplayScope to all the Article requests. Now imagine you are an admin and want to hide an article (not pusblish it) on your site but still want to see it in the Admin Panel for editing it. With this method you can not do that, because the scope will also be applied to your admin panel and when you will hide your article or unpublisehd it, it will also dissapear from your admin panel, kind annoying, isn't it ? sweat_smile

So let's see what we need to do : we want this scope to apply only on the public blog but not in the admin panel. Instead of appliying the GlabalScope to the booted() function of the Model, we will apply it to a predefined routes.

How to apply a GlobalScope to a route ? Actually that is not possible to apply a scope to a route.

To counter this problem, we will use... Middleware ! We will add the GlobalScope only if the Middleware is applied. (linked to a route)

Let's create a Middleware named EnableDisplayScopeMiddleware.

php artisan make:middleware EnableDisplayScopeMiddleware

Now we will add the GlobalScope in this middleware :

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Models\Scopes\DisplayScope;

class EnableDisplayScopeMiddleware
{
	/**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     *
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
		// We apply our scope here.
        Article::addGlobalScope(new DisplayScope);

        return $next($request);
    }
}

And that's all, now we just need to link this Middleware to the routes we want it to be applied.

Let's register this Middleware in the App\Http\Kernel in the $routeMiddleware variable :

<?php
namespace Xetaravel\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
	/**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        //...
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,

        // Applications
        'display' => \App\Http\Middleware\EnableDisplayScopeMiddleware::class,
    ];
}

And now we can finally apply the Middleware to our routes :

Route::group([
    'namespace' => 'Blog',
    'prefix' => 'blog',
    'middleware' => ['display'] // Our Middleware
], function () {

    // Article Routes
    Route::get('/', 'ArticleController@index')
        ->name('blog.article.index');
    Route::get('article/{slug}.{id}', 'ArticleController@show')
        ->name('blog.article.show');

});

Now every routes in this Route::group will apply the DisplayScope on our Article model requests.

If you want a real exemple, you can find the code I used for this site :

Author
Emeric Avatar Offline

Full-stack Web Developer specialized in PHP. Work with Laravel, CakePHP & Symfony. In formation at OpenClassRoom

0 Comment(s)