// tutorial

Building a custom validation rule

Once a check needs more than a builtin rule can express — like a password strength policy — it belongs in its own Rule class, not stuffed into a closure inside rules(). Here's one that requires an uppercase letter, a lowercase letter, a symbol and a minimum length, wired into a Form Request.

php laravel validation 5 min read
  1. Generate the rule

    Passing a namespaced name creates the class inside a matching subdirectory of app/Rules — handy for grouping related rules, like keeping every auth-related rule under App\Rules\Auth.

    bash
    php artisan make:rule Auth/SecurePassword

    That creates app/Rules/Auth/SecurePassword.php implementing the Rule contract, with passes() and message() stubbed out.

  2. Implement passes() and message()

    passes() gets the attribute name and its value, and returns a boolean. message() supplies the error text used when it returns false — this one requires at least one lowercase letter, one uppercase letter, one symbol, and 8 characters minimum.

    php
    <?php
    
    namespace App\Rules\Auth;
    
    use Illuminate\Contracts\Validation\Rule;
    
    class SecurePassword implements Rule
    {
        public function passes($attribute, $value)
        {
            return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,}$/', $value);
        }
    
        public function message()
        {
            return 'The password you entered does not meet the security requirements.';
        }
    }
  3. Drop it into a Form Request's rules()

    A custom rule instance slots into the array alongside builtin rules the same way a Rule::in() or Rule::unique() object does.

    php
    use App\Rules\Auth\SecurePassword;
    
    public function rules(): array
    {
        return [
            'password' => ['required', 'string', 'confirmed', new SecurePassword()],
        ];
    }
  4. Keep the class stateless and reusable

    Because SecurePassword doesn't depend on the request it's validating, the same instance can be reused across every Form Request that collects a password — registration, password reset, account settings — without duplicating the regex anywhere.