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
227: Laravel Form Requests for Validation
I've seen this happen to almost every developer the first time they try to clean up their controllers. You've decided to stop bloating your controller methods with 20 lines of validation rules, so you generate a Form Request class. You type-hint it in your method, hit "Submit" on your form, and instead of your data saving, you get a cold, hard 403 Forbidden response.
The Mystery of the 403 Forbidden
// StorePostController.php
public function store(StorePostRequest $request)
{
// I never even reach this line!
Post::create($request->validated());
}
You check your routes, you check your middleware, and you check your permissions. Everything looks perfect. The problem isn't your routing; it's inside the Form Request class you just generated. When you run php artisan make:request StorePostRequest, Laravel gives you a skeleton that looks like this:
public function authorize(): bool
{
return false; // <--- This right here is your culprit
}
Laravel assumes that by default, the user is not authorized to make this request. It's a safety feature, but it's a confusing one when you're just trying to get a form to work. If you don't have complex permission logic tied to this specific request yet, you need to flip that switch to true. If you leave it as false, Laravel kills the request before it ever touches your controller logic.
Moving Validation Out of the Controller
Now that we've stopped the 403 errors, let's actually use the Form Request for what it's meant for: removing the noise from your controller. Usually, learners start by doing this in the controller:
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'category_id' => 'required|exists:categories,id',
]);
Post::create($validated);
}
It works, but as your app grows, your controllers become "fat." They start handling authorization, validation, and business logic all in one place. I prefer to move those rules into the rules() method of the StorePostRequest class.
// StorePostRequest.php
public function authorize(): bool
{
return true; // Now it actually works
}
public function rules(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'category_id' => 'required|exists:categories,id',
];
}
Once you've done this, your controller method shrinks down to a couple of lines. The magic happens because Laravel automatically calls the rules() method before the controller method is even executed. If validation fails, Laravel automatically redirects the user back to the previous page with the error messages flashed to the session. You don't have to write a single if ($validator->fails()) statement.
Handling the Validated Data
One mistake I often see is people continuing to use $request->all() inside the controller even after moving to a Form Request. Avoid that. Using $request->all() can lead to "mass assignment" vulnerabilities where a malicious user sends an extra field (like is_admin = 1) in the HTTP request, and your app blindly saves it to the database.
Instead, use the validated() method. This ensures you only ever touch data that has actually passed your rules:
public function store(StorePostRequest $request)
{
// This returns only the fields defined in your rules() method
$data = $request->validated();
Post::create($data);
return redirect()->route('posts.index');
}
By moving the logic to a Form Request, you've made your code more reusable. If you ever need to create a different controller method (like an API endpoint) that uses the same validation rules, you just type-hint the same Request class and you're done. No more copy-pasting arrays of validation strings across your project.
📋 Practical Task
Build a Validated Support Ticket System
You are building a support ticket system. Your goal is to move the validation logic for creating a ticket out of the controller and into a dedicated Form Request class.
- The Setup: Create a Form Request class named
StoreTicketRequestusing the Artisan command. - Authorization: Ensure the
authorize()method returnstrueso the request isn't blocked. - Validation Rules: In the
rules()method, implement the following requirements:subject: Required, string, maximum 100 characters.priority: Required, must be one of the following values: 'low', 'medium', 'high'.description: Required, minimum 20 characters.user_email: Required, must be a valid email address.
- The Controller: In your
TicketController@storemethod, replace the genericRequest $requesttype-hint withStoreTicketRequest $requestand use$request->validated()to retrieve the data for saving.
There are no comments for now.