harryd's avatar

Laravel policy on User model

I'm wondering how to have a policy on the user model.

I understand that the first argument is the authenticated user, however I want to obtain the user in the route. When I add the 2nd argument, it'll only pass in 1 argument and error.

I have an API with the endpoint POST /users/{user}/something

class UserPolicy
{
    use HandlesAuthorization;

    public function show(User $user, User $routeUser)
    {
        // Type error: Too few arguments to function UserPolicy::show(), 1 passed in and exactly 2 expected"
    }
}

Am I missing something?

0 likes
9 replies
tykus's avatar

I suspect the fact that the route wildcard is $user and the second argument is $routeUser is an issue. Can you rename the first argument $authUser and the second to match the route wildcard, i.e. $user?

harryd's avatar

I just tried that and get the same error :/ Also have it typehinted on controller action, with same name

minjon's avatar

The following code works with me:

User.php

public function me($user)
{
    return $this->id === $user->id;
}

UserPolicy.php

public function show(User $user, User $routeUser)
{
    return $user->me($routeUser);
}
tykus's avatar

So the second parameter name is not the issue; can you show how you are invoking the policy in your controller?

harryd's avatar

I have it wired up via middleware in the route declaration:

Route::post('users/{user}/something', 'SomethingController@something')
    ->middleware('can:show,' . User::class);`

and of course defined in AuthServiceProvider:

protected $policies = [
    // ...
    User::class => UserPolicy::class,
];
minjon's avatar

@tykus Let's say you want to authorize show method in the UserController:

UserController.php

public function show(User $user)
{
    $this->authorize($user);

    // Your code here
}

Although I tend to authorize all methods by using $this->authorizeResource(User::class).

tykus's avatar
tykus
Best Answer
Level 104
Route::post('users/{user}/something', 'SomethingController@something')
    ->middleware('can:update,user');

You need to give the user that was implicitly bound to the route

1 like
harryd's avatar

@tykus you nailed it, thanks =)

@minjon I don't think there is any need for me() method, just do return $user->is($routeUser); on the policy

3 likes
dev-mo's avatar

this worked for me policy:

	public function view(User $user, User $routeUser): bool
		{
    return $user->role_id == UserRole::ADMIN->value
        ||
        ($routeUser->role_id == UserRole::USER->value
            && $routeUser->agent_id === $user->agent_id)
    ;
}

controller method:

	 public function show(User $user)
		{
        Gate::authorize('view', $user);
      return view('pages.agent.user.show', compact('user'));
     }

Please or to participate in this conversation.