Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Dieter's avatar

Laravel 11 policies

I'm trying to use policies for the first time, but for some reason it seems like my gate isn't reaching the policy. As you can see I put a dd before the return of the create policy but it seems to be never read.

when I return the gate in the controller I get an empty response in postman.

TasklistController:

public function store(Request $request) { $data = $request->validate([ 'name' => 'required', 'description' => 'nullable', 'team_id' => 'required|integer' ]);

    $team = Team::find($data['team_id']);
    if (!Gate::allows('create-tasklist', $user = Auth::user(), $team)) {
        return response(['error' => 'You are not a part of this team!'], 403);
    }

    $tasklist = $team->tasklists()->create([
        'name' => $data['name'],
        'description' => $data['description']
    ]);

    return response($tasklist);
}

TasklistPolicy:

public function create(User $user, Team $team)
{
    dd('test'); // <- not working
    return $user->teams->contains($team);
}
0 likes
8 replies
coni's avatar

If your model name is Team, then your policy should be App\Policies\TeamPolicy (if you use standard convention). Then in policy you can have:

class TeamPolicy
{
    public function create(User $user, Team $team): bool
    {
        return true; // logic for authorization
    }
}

then in your controller, you check ability/inability, like this:

if (Gate::allows('create', $team)) {
	//
}

if (Gate::denies('create', $team)) {
	//
}

You can not pass user to this method. User is passed automatically to policy. You are passing ability name and a model instance or model class name, depend on method.

Dieter's avatar

@coni Hi, maybe I'm doing this wrong. For some context:

The user is part of a team and can create tasklists for said team. So when creating a tasklist there is a team_id passed in the body. Now I want to make sure the user is part of this team.

So basicly the user can only create a tasklist for the team he is a part of.

Should I write this logic inside the team policy of the tasklist policy? Or you think there is a better way to do this in Laravel?

Thanks

Dieter's avatar

@coni I now have done it like this:

public function store(Request $request)
{
    $data = $request->validate([
        'name' => 'required',
        'description' => 'nullable',
        'team_id' => 'required|integer|exists:teams,id'
    ]);

    $team = Team::findOrFail($data['team_id']);

    if (!Gate::allows('create', new Tasklist())) {
        return response(['error' => 'You are not a part of this team!'], 403);
    }
    $tasklist = $team->tasklists()->create([
        'name' => $data['name'],
        'description' => $data['description']
    ]);

    return response($tasklist);
}

public function create(User $user, Tasklist $tasklist = null)
{
    $teamId = request()->get('team_id');
    $team = Team::find($teamId);

    if (!$team || !$user->teams->contains($team)) {
        return false;
    }
    return true;
}

This works as intendend. But I would love some feedback if you have the time. :)

coni's avatar

@Dieter I would do something like this in controller:

class TasklistController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string',
            'description' => 'nullable|string',
            'team_id' => 'required|integer|exists:teams,id'
        ]);

        Gate::authorize('create', [Tasklist::class, $validated['team_id']]);

        return Tasklist::create($validated);
    }
}

and in App\Policies\TasklistPolicy:

class TasklistPolicy
{
    public function create(User $user, int $team_id): bool
    {
        return $user->teams->contains($team_id);
    }
}

In policy you don't have to check team existence, because you already done that in validation. Only check, if user belongs to a team is needed.

Dieter's avatar

@coni Hi, this indeed works thanks :) Only thing is that Tasklist::class does not seem to work.

"message": "App\Policies\TasklistPolicy::create(): Argument #2 ($tasklist) must be of type ?App\Models\Tasklist, App\Models\Team given, called in /Users/dietervrancken/sites/shoppinglist-backend/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php on line 811",

Changing it to new Tasklist() does work though.

coni's avatar

@Dieter So, could you show me your authorization line from controller? It gives to policy Team instance.

jlrdw's avatar

@Dieter I would strongly suggest watching that new series on laravel in 30 days. Jeffrey covers gates and policies. Actually kind of surprised you haven't seen them yet if you haven't.

maxxxir's avatar
maxxxir
Best Answer
Level 1

i'd suggest to inject your team it to your controller via route , it's just cleaner since you have to read the object from db laravel will do this for you just pass the id in your route and catch it in controller argument , and yes i'd put it in team policy since the condition is in the team entity not the user or tasklist

/tasklists/store/{team}

function store(Request $request , Team $team){
		  Gate::authorize('isMember', $team);
		 return $team->tasklists()->create( validated data );
}

you dont have to pass the authenticated user to policy , it will be injected to policy method automatically

public function isMember(User $user, Team $team)
{
	return $user->teams->contains($team->id)
}

if you want to have specific message for failed policy you can put it in your policy instead of controller , you might want to use it in multiple controllers why add a new line to all of them

public function isMember(User $user, Team $team): bool|Response
{
	  if(! $user->teams->contains($team->id))
     {
         return Response::deny('you are not a team member');
     }

     return true ; 
}

if you need to pass extra argument to policy you have to pass them as array with the model inside that array

Gate::authorize('isMember'  , [ $team , $extraArgument ] );

but generally the first argument you pass to policy (after action/ability ) will determine which policy should be executed

Also i 'd recommand to flag the model when you creating policy with artisan if you are a beginner

 php artisan make:policy TeamPolicy --model=Team 
1 like

Please or to participate in this conversation.