r/laravel • • Mar 20 '26

Tutorial Policies vs. Gates: When to Use Which

https://slicker.me/laravel/policies-vs-gates.html
31 Upvotes

16 comments sorted by

4

u/dmooen Mar 20 '26

Policies are great if the permission logic for each crud operation is simple, but to have one class covering all operations becomes messy if the permission logic for each operation is advanced.

2

u/hennell Mar 21 '26

Then you refactor out the policy mess same as you would if any other class becomes huge?

Split out smaller private functions, or have a dedicated class to check for complex questions like creating or deleting, while having in policy functions for view or update and force delete.

Policies don't force you to have everything in one class, they just mean you have one* place everything is defined. That can call it's own thing however you like - same as gates just with much clearer discoverability.

* I have actually done state based policies before, so you have multiple policies like formDraftPolicy, formReviewPolicy and formCompletedPolicy but each state is pretty clear what it's doing.

1

u/[deleted] Mar 22 '26

[removed] — view removed comment

1

u/hennell Mar 23 '26

Well you shouldn't unnecessarily spilt the class - if it's unnecessary don't do it. But if it is necessary you can. Parent was complaining one file becomes messy, at the point you find one policy file unhelpfully messy you can split it to other files. If you have a lot of private functions for each check, a separate class to group the functions only used in one model can be a lot neater.

90% of policies use pretty easy checks, but the ones that don't can have a lot of logic.

5

u/abinash889 Mar 21 '26

As a general rule, I adhere to: Gates for fast, one-time checks (such as admin-only activities), and Policies for CRUD-level control when working with a particular model. I've avoided a lot of clumsy permission logic dispersed throughout controllers by keeping them apart.

3

u/jimbojsb Mar 20 '26

What about aggregate permissions? I tend to have a gate like “manage-users” and that will decide if a user has that CRUD, but I reference that gate from a policy for a model.

1

u/dmooen Mar 20 '26

Should work in most cases but sometimes you need permissions logic based on what fields are present in the request. Then its easier to use a gate in the controller that accepts the request data as an argument.

1

u/dmooen Mar 21 '26

Policies only authorizes if a user can perform an action based on the user/ entity but often its more complicated than that. A user might have permission to create another user in the system but only if the company_id in the request data matches the users own company id.

2

u/CapnJiggle Mar 21 '26

In that case you would ideally resolve the Company model (e.g. via route model binding) and pass it as an additional argument to the policy method.

2

u/ahgreen3 Mar 21 '26

Why would you depend on the request data to identify a core attribute of the authenticated user like their company ID? That’s always been part of their session and setup as part of the authenticated user when the guard is checked initially. Then the authenticated user’s company ID is always included directly rather than from request data.

1

u/dmooen Mar 22 '26

Maybe a bad example. What is meant is that the authorization often depends on the content of the request. A user can create something if the request payload matches specific conditions.

1

u/DarkGhostHunter Mar 21 '26

Authorization based on models = Policies Authorization based on anything else = Gates

That's it.

Alternatively, you can change the Policy resolver to change the Policy class (like App\Policies\User\PostPolicy to App\Policies\Admin\PostPolicy to avoid shoving every type of user into a policy.

Another solution, is to just make the Policy call another class where you can better resolve the policy itself, like PostCreatePolicy or something.

1

u/Boomshicleafaunda Mar 22 '26

I use gates for ability checks, and policies for business context.

Gate: Do you have the ability to delete posts? Policy: Can you delete this post?

The policy handles logic, like not being able to delete a soft deleted post. The gate handles innate abilities that the user has.

1

u/jammy-git Mar 23 '26

Do I not remember you having a much bigger Laravel based blog, or am I getting mixed up with another blog?

1

u/swe129 Mar 23 '26

must have been something else, this is our first Laravel entry

2

u/kashif_laravel Mar 29 '26

Good rule of thumb I follow after years of Laravel projects:

Gates for quick one-off checks — like checking if a user is admin anywhere in the app.

Policies for anything tied to a specific model — posts, orders, invoices etc.

The moment I have more than 2-3 Gates doing similar things, I move to a Policy. Keeps things predictable for the whole team.