Stephen Fischer
Stephen Fischer

Reputation: 2546

Matching one of N route filters

In Laravel is there a way to allow access to a route if at least one out of N filters is successful? For example:

Route::get('/user/{id}', array('uses' => 'UserController@profile', 'before' => 'requireAdmin|requireIsMe'));

This filter will require both requireAdmin and requireIsMe to grant access to the route, but what I need is a way to denote that either requireAdmin or requireIsMe is sufficient to allow access. Is this possible, or will I have to create a new filter that combines them both (which is a terrible solution, since I'll have to do that for ALL filter combinations)?

The filters look a little something like:

Route::filter('requireAdmin', function () {
    if(Session::get('user')->GetRole() != Role.Admin)
        return Redirect::to('/');
});

Route::filter('requireIsMe', function($id) {
    if(Session::get('user')->GetId() != $id)
        return Redirect::to('/');
});

What I need is: given those two filters, is there a way to tell a route that only one of them needs to pass in order to allow the request to continue as normal (in this example, both Admins and users with a matching ID should be able to proceed to /user/{id}). Is this possible? Or is there a smarter way to handle this kind of filtering?

Upvotes: 1

Views: 101

Answers (2)

Homme Sauvage
Homme Sauvage

Reputation: 1916

This is a bit complex approach. So you'll have to declare a new function in global.php:

function getFilters($filters)
{
    $exploded = explode('|', $filters);
    foreach($exploded as $filter)
    {
            $filterClass = ucwords($filter).'Filter';
            $return = with(new $filterClass)->filter();
            if( $return === null ) return strtolower($filter);
    }

    return strtolower($filters);
}

Now the next thing is to use "classes" for your filters. This is filters.php:

Route::filter('test', 'TestFilter');
Route::filter('test2', 'Test2Filter');

class TestFilter {
    public function filter() {
        if( 1 == 1 ) return Redirect::to('/');
    }
}
class Test2Filter {
    public function filter() {
        if( 1 == 2 ) return Redirect::to('/');
    }
}

And finally, you'll have to call "getFilters()" on the route and pass to it the filters you want (instead of before=> 'test|test2'):

Route::get('test', array('before' => getFilters('test|test2'), function(){
    dd('Hello');
}));

Now the explanation:

The aim of the getFilters() function is to test filters before they get called on the route, if one of them doesn't return something this will make the function return just the filter who succeeded to before(). In the example above, test2 will fail, but test will succeed, so the function will just return test as a filter to the route. Now, if the 2 filters fail, the function will return the filters string so that Laravel will take care of the response.

Note: It's prefereable to pass CamelCaseLongName as filter string so that getFilters() calls the corresponding class correctly. The returned string to before will always be lowercase.

You can then customize getFilters() so that it parses strings to get what's after the colons as parameters, just like you'd do it with normal filters (ex: test|test2:param1,param2')

Upvotes: 1

Israel Ortuño
Israel Ortuño

Reputation: 1084

Do not really know but you could create a filter which calls this two and returns true if one of them do ;)

Upvotes: 0

Related Questions