Mateus Felipe
Mateus Felipe

Reputation: 1171

Inject Route Action on Controller

I've made a role middleware to check if user has a specific Role:

namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->user() === null) {
            return response("Insufficient permissions", 401);
        }
        $actions = $request->route()->getAction();
        $roles = isset($actions['roles']) ? $actions['roles'] : null;
        if ($request->user()->hasAnyRole($roles) || !$roles) {
            return $next($request);
        }
        return response("Insufficient permissions", 401);
    }
}

If I want to check for some role in a specific role it's easy. I only have to add the middleware and an additional action called roles. E. g.:

Route::get('payments/{id}/pay', [
    'uses' => 'PaymentController@aprove',
    'as' => 'payments.aprove',
    'middleware' => 'roles',
    'roles' => [User::ADMINISTRATOR, User::SPOT_MANAGER, User::SELECTION_PROCESS_MANAGER],
]);

Doing this way works as expected. However, I have some routes that are Route::resource instead of get, post, or something like this. Laravel don't allow for specifying roles => [...] in resource.

The documentation says that if I want to inject middleware on resources I should do this on controller. But I can't specify roles => [...] as I used to do in normal routes anywhere! How could I do this?

Thanks in advance.

Upvotes: 0

Views: 252

Answers (2)

lagbox
lagbox

Reputation: 50491

Since you dont have control over the 'action' array to use when defining resource routes, and the middleware paremeters are not fitting you can define your own ResourceRegistrar and bind it to the container so the router will use it on Route::resource calls.

You will extend Illuminate\Routing\ResourceRegistrar and override the getResourceAction method to check the $options array to see if ... lets say a key named action is defined. If so merge this array into the $action array and now all the routes defined via Route::resource can accept an options key of action that takes your extra action values.

Allows for a definition like:

Route::resource('blah', 'BlahController', [
    'action' => ['roles' => ['admin', 'editor']]
]); 

Something like this 'should' do:

protected function getResourceAction($resource, $controller, $method, $options)
{
    $action = parent::getResourceAction($resource, $controller, $method, $options);

    if (isset($options['action'])) {
        $action += $options['action'];
    }

    return $action;
}

In a service provider at register the binding:

$this->app->bind(
    \Illuminate\Routing\ResourceRegistrar::class,
    \App\Your\ResourceRegistrar::class
);

You could take that further and make it specific to roles so you could do this instead:

Route::resource('blah', 'BlahController', ['roles' => ['admin', 'editor']]);

Upvotes: 1

lagbox
lagbox

Reputation: 50491

You can avoid this by using middleware parameters, so you can pass parameters to your middleware. This 'action' method is a nice work around for some things but middleware take paremeters so there really isn't a need unless you need those params in more places.

'middleware' => 'roles:admin,editor,...'

Literally the example for middleware parameters in the docs is a user role check.

Laravel 5.5 Docs - Middleware - Parameters

Also there is no 'injection'. You are just telling the router you want middleware, there isn't anything injected into anything else.

Upvotes: 1

Related Questions