A
A
Anton Anton2021-07-29 14:49:09
Laravel
Anton Anton, 2021-07-29 14:49:09

How to make it so that the global scope is applied when binding the route (and throws an error)?

There was a task - to make sure that certain elements by the criterion were not available through certain API routes. I decided to do it by applying a global scope to models, I wrote middleware:

AddApiScopes.php

<?php

namespace App\Http\Middleware;

use App\Models\Order;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;

class AddApiScopes
{
  /**
   * Handle an incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  \Closure  $next
   * @return mixed
   */
  public function handle(Request $request, Closure $next)
  {
    Order::addGlobalScope('active', function (Builder $builder) {
      $builder->where('status', '<>', 0);
    });
    return $next($request);
  }
}



added it to the route group:
api.php

Route::prefix('/')->middleware(AddApiScopes::class)->group(function () {
//...
  Route::get('orders', [ApiController::class, 'orders']);
  Route::get('orders/{order}', [ApiController::class, 'order']);
//...
});



Everything works in the 'orders' method, only elements with the required statuses are displayed
public function orders()
  {
    return OrderResource::collection(Order::all()); // тут скоуп применяется
  }


However, in the order method, if you manually enter the id of an element with an incorrect status in the address bar - no 404 error is generated, you have to duplicate the code. This threatens that I will forget to register this code in some place, or there may be jambs when conditions change.
public function order(Order $order)
  {
    if ($order->status === 0) { // костыль
      abort(404);
    }
    return new OrderResource($order);
  }


How can I make the global scope work for model resolution as well, and I don't even get into this procedure?

Until I got to this crutch:
Route::get('test/{order_id}', function ($order_id) { // выдает 404
    $order = Order::findOrFail($order_id);
    return $order;
  });
  Route::get('test2/{order}', function (Order $order) { // выдает данные модели
    return $order;
  });

but I want it to work https://laravel.com/docs/8.x/routing#implicit-binding

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Anton Anton, 2021-07-29
@Fragster

Need to substitute my middleware before

\Illuminate\Routing\Middleware\SubstituteBindings::class
in priorities:
https://laravel.com/docs/8.x/middleware#sorting-mi...

K
Konstantin B., 2021-07-29
@Kostik_1993

Crap decision. It seems to me better to register the apiOrder or activeOrder parameter and make the necessary request in the service provider.
For example

Route::bind('activeOrder', function ($value) {
        return Order::active()->firstOrFail();
});

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question