Search code examples
phplaravellaravel-5.4laravel-authorization

Policies with extra parameters


I know, how to use Laravel policies and everything works, but I am stuck with create(...) method.

My app is a training diary for ski racers. Each racer can manage (view, add, edit..) his own diary. Each trainer can manage his own diary, diary of all racers but not other trainers. I use native Laravel Policies and it works great during *update(...) and delete(...) method, but not create(...).

TrainingRecordPolicy.php:

public function update(User $user, TrainingRecord $record)
{
    if ($user->id == $record->user->id) {
        return true;
    }
    if ($user->isTrainer() && $record->user->isRacer()) {
        return true;
    }
    return false;
}

I'm using it in controllers like $this->authorize('update', $record). But if I want to check, if user can create new record into others user diary, I have no idea how to deal with it.

This doesn't work $this->authorize('create', TrainingRecord::class, $diaryOwner) and if I use $this->authorize('createDiaryRecord', $diaryOwner) it calls method inside UserPolicy.

How do I send $diaryOwner as extra parameter to create() method within the policy?

Note: $diaryOwner is retrieved from the route parameter user in route with signature /diary/{user}


Solution

  • You can access the $diaryOwner within the policy using request() helper.

    public function create(User $user) 
    {
        $diaryOwner = request()->user; // because route is defined as /diary/{user}
    }
    

    There may be a problem because:

    When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the route parameters.

    So instead of using request()->user use request()->route()->parameter('user').

    Also if you are using \Illuminate\Routing\Middleware\SubstituteBindings::class you will get User instance.