# Laravel Form Validation in Practice

> A repeatable pattern for safely handling user input: Laravel validation rules and error messages.

- Published: 2014-11-02
- Category: Web Development
- Tags: Laravel
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/laravel-form-validation-in-practice/
- Language: en-US
- Author: Muhammet Şafak

---
Every web application requires you to validate user input. I used to do this separately for every form: combinations of `empty()`, `strlen()`, `filter_var()`, hand-written error messages, and repetitive chains of `if` statements. It worked, but maintenance became painful and I kept rewriting the same patterns for every new form.

[Laravel's](/en/blog/getting-started-with-my-first-laravel-4-project/) **validation** component turned that process into a standard, repeatable pattern. I've been using it on a live project for a few weeks now and wanted to share how practical it is.

## Basic Usage

In your controller, you use the `Validator` class:

```php
<?php

class KayitController extends BaseController
{
    public function store()
    {
        $kurallar = [
            'ad'     => 'required|min:2|max:50',
            'email'  => 'required|email|unique:kullanicilar',
            'sifre'  => 'required|min:6|confirmed',
        ];

        $dogrulayici = Validator::make(Input::all(), $kurallar);

        if ($dogrulayici->fails()) {
            return Redirect::back()
                ->withErrors($dogrulayici)
                ->withInput();
        }

        // Validation passed — create the record
        Kullanici::create(Input::only('ad', 'email', 'sifre'));

        return Redirect::to('/giris')->with('mesaj', 'Kayıt başarılı.');
    }
}
```

`Validator::make()` takes two parameters: the data to validate and an array of rules. If `fails()` returns false, you continue processing.

## Validation Rules

Rules are chained together with `|`. Here are the ones I use most often:

| Rule | Meaning |
|---|---|
| `required` | Cannot be empty |
| `email` | Must be a valid email format |
| `min:6` | At least 6 characters |
| `max:100` | At most 100 characters |
| `unique:tablo` | Must be unique in the table |
| `confirmed` | Must match the `field_confirmation` field |
| `numeric` | Must be a number |
| `in:a,b,c` | Must be one of the specified values |
| `date` | Must be a valid date format |

The `confirmed` rule is especially useful for password confirmation. If you apply it to the `sifre` field, the form must also include a field named `sifre_confirmation`; if the two don't match, validation fails.

## Displaying Error Messages in the View

With `withErrors($dogrulayici)`, the errors are written to the session and automatically become available in the view as the `$errors` variable:

```html
@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $hata)
                <li>{{ $hata }}</li>
            @endforeach
        </ul>
    </div>
@endif

<form method="POST" action="/kayit">
    <div class="form-group {{ $errors->has('email') ? 'has-error' : '' }}">
        <label>E-posta</label>
        <input type="email" name="email" value="{{ Input::old('email') }}">
        @if ($errors->has('email'))
            <span class="help-block">{{ $errors->first('email') }}</span>
        @endif
    </div>
    ...
</form>
```

`$errors->has('email')` checks whether there is an error for a specific field. `$errors->first('email')` returns the first error message for that field. `Input::old('email')` repopulates the form with the value the user typed when validation fails — saving the user from having to retype everything.

## Custom Error Messages

To define custom messages instead of the default English ones, you pass a third parameter to `Validator::make()`:

```php
$mesajlar = [
    'ad.required'   => 'Ad alanı zorunludur.',
    'email.required' => 'E-posta alanı zorunludur.',
    'email.email'   => 'Geçerli bir e-posta adresi giriniz.',
    'email.unique'  => 'Bu e-posta adresi zaten kayıtlı.',
    'sifre.min'     => 'Şifre en az 6 karakter olmalıdır.',
];

$dogrulayici = Validator::make(Input::all(), $kurallar, $mesajlar);
```

You write them in `field.rule` format. You can define a separate message for every field–rule combination.

## A Global Solution via Language Files

Rather than rewriting all those messages in every controller, you can create an `app/lang/tr/validation.php` file. Laravel loads it automatically:

```php
<?php
return [
    'required' => ':attribute alanı zorunludur.',
    'email'    => ':attribute geçerli bir e-posta adresi olmalıdır.',
    'min'      => [
        'string' => ':attribute en az :min karakter olmalıdır.',
    ],
    // ...
];
```

The field name is substituted in place of `:attribute` automatically. Set this up once and every validation message across the entire project uses your custom language — no need to repeat it in every controller.

## A Gotcha with the `unique` Rule

On edit forms, the `unique` rule can behave unexpectedly. On a registration form, `unique:kullanicilar` works perfectly — it rejects an email address that already exists. But on an edit form, when a user saves their profile without changing their email, the rule sees that the email is already in the table and throws an error.

To work around this, you include the current record's ID in the rule definition:

```php
$kurallar = [
    'email' => 'required|email|unique:kullanicilar,email,' . $kullanici->id,
];
```

The `unique:table,column,excluded_id` format excludes the row belonging to that ID from the uniqueness check. I discovered this on an edit form — a rule that worked fine on registration was blocking users from saving their profile without making any changes.

## Why Does This Matter?

If you don't validate user input, two problems arise: the application runs with inconsistent data, and security vulnerabilities open up. Laravel's validation layer provides a standard, repeatable solution for both. I no longer write `if` chains from scratch for every form; I write a list of rules and the framework handles the rest.

It might seem like a small thing, but in a large project with dozens of forms the value of that standardization compounds quickly. Especially when you join a project mid-stream — having rules defined in a central, consistent place makes it straightforward to understand how each form behaves, without having to hunt through controllers for scattered `if` blocks.
