首页 > 解决方案 > Can I use dynamic model on middleware?

问题描述

I have 2 routes that requires a person to be tagged to access the discussions.

  1. http://localhost:8000/api/fieldReports/{fieldReport}/discussions
  2. http://localhost:8000/api/agendas/{agenda}/discussions

Currently, I have created this middleware, but instead of pointing right to a specific model, and duplicate it for each model with the exact same functionality, I want it to be more reusable.

Middleware\ForbidUntaggedUser.php

class ForbidUntaggedUser
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $user = $request->user();
        $report = $request->report; // <-- I hardcoded the model, I want this to be dynamic

        // The `taggedUsers` remains the same (identical) for each model that has tagging system on it.
        if (!$report || !$report->taggedUsers->contains($user->id)) {
            return response()->json('Your action is unauthorized.', 403);
        }

        return $next($request);
    }
}

I've tried to use Policy but it doesn't work, so I think I need a middleware for this.

Policies\FieldReportDiscussionPolicy.php

class FieldReportDiscussionPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view any models.
     *
     * @param  \App\Models\User  $user
     * @return mixed
     */
    public function viewAny(User $user, FieldReport $fieldReport)
    {
        return $user->can('view any fieldReportDiscussion')
            && $fieldReport->taggedUsers->contains($user->id);
    }

    ... // and so on..
}

Controllers\FieldReportDiscussionController.php

class FieldReportDiscussionController extends Controller
{
    protected $model;

    /**
     * Create new instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->authorizeResource(
            FieldReportDiscussion::class,
            ['fieldReportDiscussion', 'fieldReport'] // This gave me error "Array to string conversion"
        );

        $this->model = new FieldReportDiscussion;
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(FieldReport $fieldReport)
    {
        $discussions = $this->model->registries($fieldReport)->paginate(100);
        return response()->json($discussions);
    }
}

I need the dependency injection on the controller because the route is nested with each model as the parent, like this one..

routes\api.php

Route::apiResource('fieldReports', FieldReportController::class);
Route::apiResource('fieldReports.discussions', FieldReportDiscussionController::class)->except(['update'])->parameter('discussions', 'fieldReportDiscussion');

So, what's the solution for this? Can I make it dynamic (the first request object)?

标签: laravel

解决方案


I think you're on the right track with using middleware, although you'll need some conditional checks, something along the lines like:

class ForbidUntaggedUser
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $instance = null;

        if ($request->report !== null) {
            $instance = $request->report;
        } else if ($request->agenda !== null) {
            $instance = $request->agenda;
        }

        if (!$instance || !$instance->taggedUsers->contains(auth()->id())) {
            return response()->json('Your action is unauthorized.', 403);
        }

        return $next($request);
    }
}

推荐阅读