Back to Blog
Lesson 36 of the PHP: Modern PHP from the Ground Up course
PHPAugust 24, 20263 min read

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.

PHPMVCForm ValidationBackend DevelopmentWeb Development
Close-up of PHP code on a monitor, highlighting development and programming concepts.

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:

  1. Consistency: Validation rules are defined in one place.
  2. UX: Users see all errors at once, rather than one by one.
  3. 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.

PHP
namespace 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

  1. Extend the Validator: Add a minLength rule to the Validator class that checks if a string has at least X characters.
  2. Refactor: Update your existing POST handler (from our previous lesson on handling POST requests) to use this new Validator class.
  3. Persistence: Notice the old data 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.

Similar Posts