Skip to content

Latest commit

 

History

History
333 lines (259 loc) · 11 KB

Validation.md

File metadata and controls

333 lines (259 loc) · 11 KB

Validation

⬆️ Go to top ⬅️ Previous (Routing) ➡️ Next (Collections)

  1. Image validation
  2. Custom validation error messages
  3. Validate dates with "now" or "yesterday" words
  4. Validation Rule with Some Conditions
  5. Change Default Validation Messages
  6. Prepare for Validation
  7. Stop on First Validation Error
  8. Throw 422 status code without using validate() or Form Request
  9. Rules depending on some other conditions
  10. With Rule::when() we can conditionally apply validation rules
  11. Use this property in the request classes to stop the validation of the whole request attributes
  12. Rule::unique doesn't take into the SoftDeletes Global Scope applied on the Model
  13. Validator::sometimes() method allows us to define when a validation rule should be applied
  14. Array elements validation
  15. Password::defaults method
  16. Form Requests for validation redirection
  17. Mac validation rule
  18. Validate email with TLD domain required

Image validation

While validating uploaded images, you can specify the dimensions you require.

['photo' => 'dimensions:max_width=4096,max_height=4096']

Custom validation error messages

You can customize validation error messages per field, rule and language - just create a specific language file resources/lang/xx/validation.php with appropriate array structure.

'custom' => [
     'email' => [
        'required' => 'We need to know your e-mail address!',
     ],
],

Validate dates with "now" or "yesterday" words

You can validate dates by rules before/after and passing various strings as a parameter, like: tomorrow, now, yesterday. Example: 'start_date' => 'after:now'. It's using strtotime() under the hood.

$rules = [
    'start_date' => 'after:tomorrow',
    'end_date' => 'after:start_date'
];

Validation Rule with Some Conditions

If your validation rules depend on some condition, you can modify the rules by adding withValidator() to your FormRequest class, and specify your custom logic there. Like, if you want to add validation rule only for some user role.

use Illuminate\Validation\Validator;
class StoreBlogCategoryRequest extends FormRequest {
    public function withValidator(Validator $validator) {
        if (auth()->user()->is_admin) {
            $validator->addRules(['some_secret_password' => 'required']);
        }
    }
}

Change Default Validation Messages

If you want to change default validation error message for specific field and specific validation rule, just add a messages() method into your FormRequest class.

class StoreUserRequest extends FormRequest
{
    public function rules()
    {
        return ['name' => 'required'];
    }
    
    public function messages()
    {
        return ['name.required' => 'User name should be real name'];
    }
}

Prepare for Validation

If you want to modify some field before default Laravel validation, or, in other words, "prepare" that field, guess what - there's a method prepareForValidation() in FormRequest class:

protected function prepareForValidation()
{
    $this->merge([
        'slug' => Illuminate\Support\Str::slug($this->slug),
    ]);
}

Stop on First Validation Error

By default, Laravel validation errors will be returned in a list, checking all validation rules. But if you want the process to stop after the first error, use validation rule called bail:

$request->validate([
    'title' => 'bail|required|unique:posts|max:255',
    'body' => 'required',
]);

If you need to stop validation on the first error in FormRequest class, you can set stopOnFirstFailure property to true:

protected $stopOnFirstFailure = true;

Throw 422 status code without using validate() or Form Request

If you don't use validate() or Form Request, but still need to throw errors with the same 422 status code and error structure, you can do it manually throw ValidationException::withMessages()

if (! $user || ! Hash::check($request->password, $user->password)) {
    throw ValidationException::withMessages([
        'email' => ['The provided credentials are incorrect.'],
    ]);
}

Rules depending on some other conditions

If your rules are dynamic and depend on some other condition, you can create that array of rules on the fly

    public function store(Request $request)
    {
        $validationArray = [
            'title' => 'required',
            'company' => 'required',
            'logo' => 'file|max:2048',
            'location' => 'required',
            'apply_link' => 'required|url',
            'content' => 'required',
            'payment_method_id' => 'required'
        ];

        if (!Auth::check()) {
            $validationArray = array_merge($validationArray, [
                'email' => 'required|email|unique:users',
                'password' => 'required|confirmed|min:5',
                'name' => 'required'
            ]);
        }
        //
    }

With Rule::when() we can conditionally apply validation rules

Thanks to Rule::when() we can conditionally apply validation rules in laravel.
In this example we validate the value of the vote only if the user can actually vote the post.

use Illuminate\Validation\Rule;

public function rules()
{
    return [
        'vote' => Rule::when($user->can('vote', $post), 'required|int|between:1,5'),
    ]
}

Tip given by @cerbero90

Use this property in the request classes to stop the validation of the whole request attributes

Use this property in the request classes to stop the validation of the whole request attributes.

Hint Direct
This is different from Bail rule that stops the validation for just a single attribute if one of its rules doesn't validate.

/**
* Indicated if the validator should stop
 * the entire validation once a single
 * rule failure has occurred.
 */
protected $stopOnFirstFailure = true;

Tip given by @Sala7JR

Rule::unique doesn't take into the SoftDeletes Global Scope applied on the Model

Strange that Rule::unique doesn't take into the SoftDeletes Global Scope applied on the Model, by default.
But withoutTrashed() method is available

Rule::unique('users', 'email')->withoutTrashed();

Tip given by @Zubairmohsin33

Validator::sometimes() method allows us to define when a validation rule should be applied

The laravel Validator::sometimes() method allows us to define when a validation rule should be applied, based on the input provided.
The snippet shows how to prohibit the use of a coupon if the quantity of the purchased items is not enough.

$data = [
    'coupon' => 'PIZZA_PARTY',
    'items' => [
        [
            'id' => 1,
            'quantity' => 2
        ],
        [
            'id' => 2,
            'quantity' => 2,
        ],
    ],
];

$validator = Validator::make($data, [
    'coupon' => 'exists:coupons,name',
    'items' => 'required|array',
    'items.*.id' => 'required|int',
    'items.*.quantity' => 'required|int',
]);

$validator->sometimes('coupon', 'prohibited', function (Fluent $data) {
    return collect($data->items)->sum('quantity') < 5;
});

// throws a ValidationException as the quantity provided is not enough
$validator->validate();

Tip given by @cerbero90

Array elements validation

If you want to validate elements of an array that you submited use dot notation in rules with '*'

// say you have this array
// array in request 'user_info'
$request->validated()->user_info = [
    [
        'name' => 'Qasim',
        'age' => 26,
    ],
    [
        'name' => 'Ahmed',
        'age' => 23,
    ],
];
// Rule
$rules = [
    'user_info.*.name' => ['required', 'alpha'],
    'user_info.*.age' => ['required', 'numeric'],
];

Tip given by HydroMoon

Password::defaults method

You can enforce specific rules when validating user-supplied passwords by using the Password::defaults method. It includes options for requiring letters, numbers, symbols, and more.

class AppServiceProvider
{
    public function boot(): void
    {
        Password::defaults(function () {
            return Password::min(12)
                ->letters()
                ->numbers()
                ->symbols()
                ->mixedCase()
                ->uncompromised();
        })
    }
}
request()->validate([
    ['password' => ['required', Password::defaults()]]
])

Tip given by @mattkingshott

Form Requests for validation redirection

when using Form Requests for validation, by default the validation error will redirect back to the previous page, but you can override it.
Just define the property of $redirect or $redirectRoute.
Link to docs

// The URI that users should be redirected to if validation fails./
protected $redirect = '/dashboard';
// The route that users should be redirected to if validation fails.
protected $redirectRoute = 'dashboard';

Mac validation rule

New mac_address validation rule added in Laravel 8.77

$trans = $this->getIlluminateArrayTranslator();
$validator = new Validator($trans, ['mac' => '01-23-45-67-89-ab'], ['mac' => 'mac_address']);
$this->assertTrue($validator->passes());

Tip given by [@Teacoders]

Validate email with TLD domain required

By default, the email validation rule will accept an email without tld domain (ie: taylor@laravel, povilas@ldaily)

But if you want to make sure the email must have a tld domain (ie: taylor@laravel.com, povilas@ldaily.com), use email:filter rule.

[
    'email' => 'required|email', // before
    'email' => 'required|email:filter', // after
],

Tip given by @Chris1904