# Structure of a Custom Validation Rule in laravel-validate: Complete Guide

> Learn the structure of a custom validation rule in laravel-validate. Discover how to create and implement your own validation logic with this comprehensive guide.

- Repository: [Milwad Khosravi/laravel-validate](https://github.com/milwad-dev/laravel-validate)
- Tags: how-to-guide
- Published: 2026-03-07

---

**A custom validation rule in laravel-validate consists of a PHP class stored in `src/Rules` that implements `Illuminate\Contracts\Validation\Rule`, which the `LaravelValidateServiceProvider` automatically registers with Laravel's validator when the `using_container` option is enabled.**

The `milwad-dev/laravel-validate` package provides over 45 pre-built validation rules for common scenarios like UUIDs, strong passwords, and IBANs. Understanding the internal structure of these rules helps you extend the package or create your own implementations that follow the same architectural patterns.

## Anatomy of a Custom Validation Rule

Each rule in the package follows a consistent structural pattern located in the `src/Rules` directory.

### Rule Class Location and Naming Convention

Rule classes reside in `src/Rules/` and use PascalCase filenames that map directly to snake_case rule names. For example, [`ValidUuid.php`](https://github.com/milwad-dev/laravel-validate/blob/main/ValidUuid.php) becomes the `valid_uuid` validator, while [`ValidStrongPassword.php`](https://github.com/milwad-dev/laravel-validate/blob/main/ValidStrongPassword.php) exposes `valid_strong_password`.

The provider scans this directory dynamically:

```php
// In LaravelValidateServiceProvider.php (lines 75-89)
File::files(__DIR__.'/Rules')

```

This iteration automatically registers every file found, using the basename without the `.php` extension as the rule identifier.

### Interface Implementation

Every rule class implements Laravel's `Illuminate\Contracts\Validation\Rule` interface, requiring two core methods:

```php
use Illuminate\Contracts\Validation\Rule;

class ValidUuid implements Rule
{
    public function passes($attribute, $value): bool
    {
        return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $value);
    }

    public function message(): string
    {
        return __('validate.uuid');
    }
}

```

The `passes()` method contains the validation logic, while `message()` returns the error string—typically resolved via translation keys stored in `lang/*/validate.php`.

## Registration Mechanism in LaravelValidateServiceProvider

The `LaravelValidateServiceProvider` located at [`src/LaravelValidateServiceProvider.php`](https://github.com/milwad-dev/laravel-validate/blob/main/src/LaravelValidateServiceProvider.php) handles rule registration through its `boot()` method. Registration only occurs when `config('laravel-validate.using_container')` evaluates to `true`.

When enabled, the provider:

1. Scans `src/Rules` for all PHP files
2. Converts filenames to snake_case rule names
3. Registers each with Laravel's `Validator` facade

This container-based approach allows you to reference rules as strings rather than instantiating objects manually.

## Using Custom Validation Rules

The package supports two primary consumption patterns depending on your configuration.

### Object-Based Validation (Direct Instantiation)

Without enabling the container, instantiate rule classes directly in your validation logic:

```php
use Milwad\LaravelValidate\Rules\ValidUuid;

$request->validate([
    'order_id' => ['required', new ValidUuid],
    'username' => ['required', new ValidUsername],
]);

```

This method works immediately after installation without configuration changes.

### String-Based Validation via Container

Enable container registration in [`config/laravel-validate.php`](https://github.com/milwad-dev/laravel-validate/blob/main/config/laravel-validate.php):

```php
return [
    'using_container' => true,
];

```

Then reference rules as pipe-delimited strings:

```php
$request->validate([
    'order_id' => 'required|valid_uuid',
    'password' => 'required|valid_strong_password',
    'slug' => 'required|valid_slug',
]);

```

### Parameterized Rule Structure

Some rules accept parameters passed via colon separation. The `ValidPattern` rule demonstrates this structure:

```php
$request->validate([
    'reference_code' => 'required|valid_pattern:/^[A-Z]{3}\d{4}$/',
]);

```

Country-specific validators like `ValidPhoneNumber` use similar parameter passing:

```php
// Configuration in config/laravel-validate.php
return [
    'phone-country' => [
        'IR' => ['regex' => '/^09\d{9}$/'],
    ],
];

// Usage
$request->validate([
    'mobile' => 'required|valid_phone_number:IR',
]);

```

## Configuration and Localization Support

The package structure includes dedicated paths for customization:

- **[`config/laravel-validate.php`](https://github.com/milwad-dev/laravel-validate/blob/main/config/laravel-validate.php)** – Toggles container registration (`using_container`) and defines country-specific regex patterns for phone and landline validation
- **`lang/*/validate.php`** – Contains translation strings referenced by rule classes (e.g., `__('validate.uuid')`)

When creating custom rules following this structure, place your language files in the same directory pattern to ensure error messages resolve correctly through Laravel's localization system.

## Summary

- **Rule Location**: All validation logic resides in `src/Rules/` with PascalCase class names
- **Interface Contract**: Each rule implements `Illuminate\Contracts\Validation\Rule` with `passes()` and `message()` methods
- **Auto-Registration**: `LaravelValidateServiceProvider` scans the Rules directory and registers classes when `using_container` is enabled in config
- **Naming Convention**: Filenames convert to snake_case rule names (e.g., [`ValidVatId.php`](https://github.com/milwad-dev/laravel-validate/blob/main/ValidVatId.php) → `valid_vatid`)
- **Dual Usage**: Rules work as instantiated objects immediately or as string shortcuts after enabling container registration
- **Parameter Support**: Rules accept parameters via colon-separated strings, parsed within the rule's constructor or `passes` method

## Frequently Asked Questions

### How do I create a custom validation rule that follows the laravel-validate structure?

Create a new class in `src/Rules/` (or your own package's equivalent directory) that implements `Illuminate\Contracts\Validation\Rule`. Implement the `passes($attribute, $value)` method with your validation logic and the `message()` method to return a translatable string. If extending the package directly, the service provider will auto-register your rule if `using_container` remains enabled.

### Where does laravel-validate store its validation error messages?

Translation strings reside in `lang/*/validate.php` files within the package directory. Each rule references these via `__('validate.rule_name')` in its `message()` method. You can publish these language files to your application to customize the error text.

### Can I use laravel-validate rules without enabling the container registration?

Yes. Instantiate rule classes directly using the `new` keyword, such as `new ValidUuid` or `new ValidCreditCard`. This bypasses the `LaravelValidateServiceProvider` registration logic entirely and works regardless of the `using_container` configuration setting.

### What is the performance impact of the container registration feature?

The service provider scans the `src/Rules` directory using `File::files()` only during the boot process when `using_container` is true. This file system scan occurs once per request in development or during container compilation in production (when config is cached), resulting in minimal runtime overhead.