PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
226: Laravel Policies and Gates for Authorization
You've probably been there: you've spent an hour polishing a feature, you hit the route in your browser, and you're greeted with a cold, hard 403 Forbidden. You know you're logged in as the administrator, so you're staring at the screen wondering why Laravel is locking you out of your own app.
Here is a snippet of a PostPolicy I saw a junior dev struggle with recently. They were trying to ensure only the owner of a blog post could edit it.
// PostPolicy.php
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
In the controller, they were doing everything "by the book":
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// ... update logic
}
The "Admin Lockdown" Problem
The code above is logically sound for a standard user. If I own the post, I can edit it. If I don't, I can't. But here's the bug: the site owner—the person with the is_admin flag set to true—is also getting a 403. Why? Because the update method in the Policy doesn't care if you're an admin; it only cares if your ID matches the user_id on the post.
The "naive" fix is to add if ($user->is_admin) return true; to every single method in every single policy you write. I'm telling you right now: don't do that. You'll end up with a maintenance nightmare where you're repeating the same admin check in twenty different files.
Using Gate::before for Global Overrides
The elegant way to handle this is by using Gate::before. This allows you to define a "master rule" that runs before any other policy check. If this closure returns a non-null value, that value is used as the result for the authorization check, and the actual policy method is never even called.
Head over to your AuthServiceProvider.php (or AppServiceProvider.php in Laravel 11) and add this to the boot method:
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
Gate::before(function ($user, $ability) {
if ($user->is_admin) {
return true;
}
});
}
Now, when $this->authorize('update', $post) is called, Laravel first checks the before gate. Since the admin returns true, they get in immediately. If the user isn't an admin, the gate returns null, and Laravel falls back to the specific PostPolicy@update logic. It's clean, centralized, and prevents you from forgetting the admin check in a new policy six months from now.
When to use Gates versus Policies
I often see developers get confused about which one to use. I like to think of it as "Model-based" vs "Action-based".
- Policies: Use these when the authorization is tied to a specific Eloquent model. "Can this user edit this specific Post?" "Can this user delete this specific Comment?" If you have a model, you probably need a policy.
- Gates: Use these for general actions that aren't tied to a record in the database. "Can this user access the admin dashboard?" "Can this user switch the site to maintenance mode?" These are essentially closures that act as quick checkpoints.
For a Gate, you define it in the provider and check it like this:
// Definition Gate::define('access-admin-panel', function (User $user) { return $user->role === 'admin'; }); // Usage in Controller if (Gate::allows('access-admin-panel')) { // Let them in }One last pro tip: stop putting authorization logic in your controllers if you can avoid it. You can use the
canmiddleware directly in your routes file. It keeps your controllers lean and ensures the check happens before the request even hits your method.Route::put('/posts/{post}', [PostController::class, 'update']) ->middleware('can:update,post');
📋 Practical Task
Implement a Project Manager Override for Task Management
You are building a project management tool. There are Tasks and Projects. Each task belongs to a project, and each project has one ProjectManager (a User).
Your goal: Create an authorization system where:
- A regular user can only
updatea task if they are thecreator_idof that task. - A user can
updateany task in a project if they are theProjectManagerof the project that task belongs to.
Requirements:
- Generate a
TaskPolicy. - Implement the
updatemethod in the policy to handle both the creator check and the Project Manager check (hint: you'll need to traverse the relationship from the Task to the Project). - Apply this authorization in a mock controller method using
$this->authorize()` or theGatefacade. - Ensure you are not using a global
Gate::beforefor this, as Project Managers should only have power over their projects, not every project in the system.
There are no comments for now.