Advanced Form Handling: Validation and Error Management in PHP
Master advanced form handling in PHP by building a robust validation layer. Learn to catch errors, manage state, and display feedback in your MVC views.

Previously in this course, we covered finalizing MVC integration to connect our routes, models, and views. Now, we're taking that architecture a step further by implementing a professional, robust validation layer to handle user input gracefully.
In a real-world application, "if-else" blocks inside your controller for every single form field lead to unmaintainable code. To handle form submissions effectively, we need to separate the data processing from the validation logic.
The Principle of Centralized Validation
Instead of checking empty($_POST['email']) inside your controller, we want to create a dedicated validation mechanism. Our goal is to collect all validation errors into an array, attempt to process the data if the array is empty, or return the errors to the view if validation fails.
This approach ensures:
- Consistency: Validation rules are defined in one place.
- UX: Users see all errors at once, rather than one by one.
- Clean Controllers: The controller acts as a conductor, not a clerk.
Implementing a Validation Service
Let’s create a Validator class. This class will store data, run rules, and hold a collection of error messages.
PHPnamespace App\Services; class Validator { protected array $errors = []; protected array $data; public function __construct(array $data) { $this->data = $data; } public function validate(array $rules): bool { foreach ($rules as $field => $ruleset) { foreach ($ruleset as $rule) { #6A9955">// Simplified logic for demonstration if ($rule === 'required' && empty($this->data[$field])) { $this->errors[$field] = ucfirst($field) . " is required."; } } } return empty($this->errors); } public function getErrors(): array { return $this->errors; } }
Handling Validation in the Controller
Now, our controller becomes significantly cleaner. We instantiate the validator, pass the $_POST data, and check the status before interacting with our models.
PHP#6A9955">// Inside your Controller method public function store() { $validator = new Validator($_POST); $rules = [ 'username' => ['required'], 'email' => ['required'] ]; if (!$validator->validate($rules)) { #6A9955">// Return errors to the view return $this->view('register', [ 'errors' => $validator->getErrors(), 'old' => $_POST ]); } #6A9955">// Proceed with database logic... }
Displaying Errors in the View
To complete the cycle, your view needs to be aware of the error state. Using the data passed from the controller, you can conditionally render messages.
HTML<!-- Inside your view template --> style="color:#808080"><style="color:#4EC9B0">input type="text" name="username" value="<?= htmlspecialchars($old['username'] ?? '') ?>"> <?php if (isset($errors['username'])): ?> style="color:#808080"><style="color:#4EC9B0">span class="error"><?= $errors['username'] ?>style="color:#808080"></style="color:#4EC9B0">span> <?php endif; ?>
Hands-on Exercise
- Extend the Validator: Add a
minLengthrule to theValidatorclass that checks if a string has at least X characters. - Refactor: Update your existing
POSThandler (from our previous lesson on handling POST requests) to use this newValidatorclass. - Persistence: Notice the
olddata array in the controller example. Implement it in your view so the user doesn't have to re-type everything when a validation error occurs.
Common Pitfalls
- Trusting User Input: Never assume the data is valid just because your form has client-side validation. Always re-validate on the server as covered in sanitization and validation.
- Displaying Too Much: Be careful not to expose system paths or raw database errors in your validation feedback. Keep messages user-friendly.
- Over-Engineering: Start with simple array-based validation before building a complex schema-based validation engine.
FAQ
Why not just use JavaScript for validation? JavaScript provides a great user experience, but it can be bypassed easily. Server-side validation is your only line of defense for data integrity and security.
Should I stop the script if validation fails?
No, you should redirect or re-render the view with the error context. Stopping the script (e.g., using die()) is a poor user experience.
Recap
We moved from scattered logic to a structured approach for data processing within our MVC framework. By centralizing validation in a dedicated service and passing feedback back to the view, we've created a maintainable way to handle complex forms.
Up next: Managing Database Migrations to keep your schema changes organized and version-controlled.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.


