Laravel/PHP - returning/redirecting from child cla

2019-05-27 05:04发布

问题:

This is my child controller:

class VolunteersController extends \BaseController
{
    public function index()
    {
        $this->checkForRoles(['admin']);
        //list some secret stuff for admin
    }
}

In my base controller I did this:

class BaseController extends Controller
{
    protected function checkForRoles($roles)
    {
        foreach ($roles as $role) {
            if (!(Auth::user()->hasRole($role))) {
                return Redirect::to('/');
            }
        }
    }
}

Now what I expected was that the line return Redirect::to('/'); in BaseController would redirect the user to home page if his role is not admin.

But it does not happen. //list some secret stuff for admin gets executed in any case.

Edit: Some people may wonder, why am I not using filters. Well yeah, the desired functionality is of filters but apparently filters do not support array arguments in Laravel yet. And as you can see, I need to pass an array of roles to the function.

Please help.

回答1:

The redirect will happen only if VolunteersController::index() will return a "Redirect". It does not do so in your code.

It would, if you had

class VolunteersController extends \BaseController
{
    public function index()
    {
        if ($res = $this->checkForRoles(['admin'])) return $res;
        //list some secret stuff for admin
    }
}


回答2:

I would move the logic to a filter, which will allow the Redirect to function properly. This is the kind of thing filters were designed for.

If you need to pass multiple roles to the filter, instead of passing an array to the filter (which Laravel won't allow), use a delimiter like "+" and then explode the parameter in the filter to simulate passing an array.

For example, your route would be:

Route::get('volunteer', array(
    'before' => 'roles:admin+author', 
    'uses' => 'VolunteersController@index'
));

...and then your filter can easily convert the multiple roles into an array:

Route::filter('roles', function($route, $request, $roles)
{
    $roles = explode('+', $roles);
    // 'admin+author' becomes ['admin', 'author'];
    // continue with your checkForRoles function from above:
    foreach ($roles as $role) {
        if (!(Auth::user()->hasRole($role))) {
            return Redirect::to('/');
        }
    }
}

Then you can remove the logic from the BaseController.

Alternatively, you can pass multiple parameters to a filter as a comma-delimited list. So if you called your route with 'before' => 'roles:admin,author', you could access them in your filter using func_get_args():

Route::filter('roles', function($route, $request, $roles)
{
    $roles = array_slice(func_get_args(), 2); // remove $route and $request
    //...continue as above.