PHPackages                             bradietilley/laravel-rules - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. bradietilley/laravel-rules

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

bradietilley/laravel-rules
==========================

Fluent rules for form requests in Laravel

v1.3.0(1y ago)8153MITPHP

Since Jun 5Pushed 1y ago1 watchersCompare

[ Source](https://github.com/bradietilley/laravel-rules)[ Packagist](https://packagist.org/packages/bradietilley/laravel-rules)[ RSS](/packages/bradietilley-laravel-rules/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (5)Dependencies (6)Versions (9)Used By (0)

Laravel Rules
=============

[](#laravel-rules)

Rules provides an elegant chainable object-oriented approach to defining rules for Laravel Validation.

[![Static Analysis](https://github.com/bradietilley/laravel-rules/actions/workflows/static.yml/badge.svg)](https://github.com/bradietilley/laravel-rules/actions/workflows/static.yml/badge.svg)[![Tests](https://github.com/bradietilley/laravel-rules/actions/workflows/tests.yml/badge.svg)](https://github.com/bradietilley/laravel-rules/actions/workflows/tests.yml/badge.svg)[![Laravel Version](https://camo.githubusercontent.com/1a0dd3f2234cab82fce80f256db4918d50a06680a4984f5db9f1ca7f58a1daea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c25323056657273696f6e2d31312e782d463933323243)](https://camo.githubusercontent.com/1a0dd3f2234cab82fce80f256db4918d50a06680a4984f5db9f1ca7f58a1daea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c25323056657273696f6e2d31312e782d463933323243)[![PHP Version](https://camo.githubusercontent.com/0742096904180c78a3bf41632578254b265ae67e07f02a3832a3dc0224fd7c29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50485025323056657273696f6e2d382e332d344635423933)](https://camo.githubusercontent.com/0742096904180c78a3bf41632578254b265ae67e07f02a3832a3dc0224fd7c29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50485025323056657273696f6e2d382e332d344635423933)

```
    'email' => Rule::make()
        ->bail()
        ->when(
            $this->method() === 'POST',
            Rule::make()->required(),
            Rule::make()->sometimes(),
        )
        ->string()
        ->email()
        ->unique(
            table: User::class,
            column: 'email',
            ignore: $this->route('user')?->id,
        ),
    'password' => rule()
        ->bail()
        ->when(
            $this->method() === 'POST',
            rule()->required(),
            rule()->sometimes(),
        )
        ->string()
        ->password(
            min: 8,
            letters: true,
            numbers: true,
        ),
```

Installation
------------

[](#installation)

Grab it via composer

```
composer require bradietilley/laravel-rules

```

### Versions

[](#versions)

- PHP 8.1 and 8.2 @ Laravel 10 → v1.2.0
- PHP 8.2 and 8.3 @ Laravel 11 → v1.3.0

Documentation
-------------

[](#documentation)

### Rules → A quick overview

[](#rules--a-quick-overview)

```
use BradieTilley\Rules\Rule;

return [
    'my_field' => Rule::make()->required()->string(),
];
```

This produces a ruleset of the following (when passed to a `Validator` instance or returned from a your `\Illuminate\Foundation\Http\FormRequest->rules()` method):

```
[
    'my_field' => [
        'required',
        'string',
    ],
]
```

### Rules → Available rules

[](#rules--available-rules)

Every rule you're familiar with in Laravel will work with this package. Each core rule, such as `required`, `string`, `min`, etc, are also available using their respective methods of the same name in camelCase. Parameters for each rule (such as min `3` and max `4`) in `digitsBetween:3,4` are made available as method arguments, such as: `->digitsBetween(min: 3, max: 4)`.

The `->rule()` method acts as a catch-all to support any rule you need to chuck in there, such as in the short interim after new rules are added and support is added to this package.

For example:

```
Rule::make()
    /**
     * You can use the methods provided
     */
    ->required()

    /**
     * You can pass in any raw string rule as per default in Laravel
     */
    ->rule('min:2')

    /**
     * You can pass in another Rule object which will be merged in
     */
    ->rule(Rule::make()->max(255))

    /**
     * You can pass in a `\Illuminate\Contracts\Validation\Rule` object
     *
     * Note: This Laravel interface is deprecated and will be dropped in future versions of Laravel. It is recommended to not use this interface.
     */
    ->rule(new RuleThatImplementsRule())

    /**
     * You can pass in a `\Illuminate\Contracts\Validation\InvokableRule` object
     *
     * Note: This Laravel interface is deprecated and will be dropped in future versions of Laravel. It is recommended to not use this interface.
     */
    ->rule(new RuleThatImplementsInvokableRule())

    /**
     * You can pass in a `\Illuminate\Contracts\Validation\ValidationRule` object
     */
    ->rule(new RuleThatImplementsValidationRule())

    /**
     * You can pass in any array of rules. The array values can be any of the
     * above rule types: strings, Rule objects, ValidationRule instances, etc
     */
    ->rule([
        Rule::make()->rule([
            'min:1',
        ]),
        'max:25',
        new Unique('table', 'column'),
    ]);
```

### Rules → Conditional rules

[](#rules--conditional-rules)

You may specify rules that are conditionally defined. For example, you may wish to make a field `required` on create, and `sometimes` on update. In this case you may define something like:

```
public function rules(): array
{
    $create = $this->method() === 'POST';

    return [
        'name' => Rule::make()
            ->when($create, Rule::make()->required(), Rule::make()->sometimes())
            ->string(),
    ];
}

// or just chuck in the rules as string literals if you feel that's cleaner

public function rules(): array
{
    $create = $this->method() === 'POST';

    return [
        'name' => Rule::make()
            ->when($create, 'required', 'sometimes')
            ->string(),
    ];
}
```

The conditional rules that you provide (in the example above: `required` and `sometimes`) may be of any variable type that is supported by the `->rule()` method ([as documented here](#rules--available-rules)).

### Rules → Reusable rules

[](#rules--reusable-rules)

The `->with(...)` method in a rule offers you the flexibility you need to specify rule logic that can be re-used wherever you need it.

Here is an example:

```
/**
 * Example using a closure
 */
public function rules(): array
{
    $integerRule = function (Rule $rule) {
        $rule->integer()->max(100);
    }

    return [
        'percent' => Rule::make()
            ->with($integerRule),
    ];
}

/**
 * Example using a first class callable
 */
function integerRule(Rule $rule)
{
    $rule->integer()->max(100);
}

public function rules(): array
{
    return [
        'percent' => Rule::make()
            ->with(integerRule(...)),
    ];
}

/**
 * Example using a callable invokable class
 */
class IntegerRule
{
    public function __invoke(Rule $rule)
    {
        $rule->integer()->max(100);
    }
}

public function rules(): array
{
    return [
        'percent' => Rule::make()
            ->with(new IntegerRule()),
    ];
}

/**
 * The above examples would all return:
 */
[
    'percent' => [
        'integer',
        'max:100',
    ],
]
```

The `->with(...)` method accepts any form of `callable`, such as

- Closures (e.g. `function () {  }`)
- Traditional callable notations (e.g. `[$this, 'methodName']`)
- First-class callables (e.g. `$this->methodName(...)`)
- Invokable classes (e.g. a class with the `__invoke` magic method)
- Whatever else PHP defines as `callable`.

### Customisation → Macros

[](#customisation--macros)

This package allows you to define "macros" which can serve as a fluent way to configure common rules.

For example, the following code adds an `australianPhoneNumber` method to the `Rule` class:

```
Rule::macro('australianPhoneNumber', function () {
    /** @var Rule $this */
    return $this->rule('regex:/^\+614\d{8}$/');
});

return [
    'phone' => Rule::make()
        ->required()
        ->string()
        ->australianPhoneNumber(),
];

/**
 * The above would return:
 */
[
    'phone' => [
        'required',
        'string',
        'regex:/^\+614\d{8}$/',
    ],
]
```

The downside to using Macros is the lack of auto-completion and intellisense. Macros are not for everyone.

### Customisation → Custom `Rule` class

[](#customisation--custom-rule-class)

You may wish to use your own `Rule` class to provide your own customisation. This can be achieved by registering your Rule class via your `AppServiceProvider` or a similar place.

```
\BradieTilley\Rules\Rule::using(\App\Rules\CustomRule::class);

// via ::make()
\BradieTilley\Rules\Rule::make(); // instanceof App\Rules\CustomRule

// via the helper function
rule(); // instanceof App\Rules\CustomRule
```

This allows you to customise any aspect you wish:

```
    public function email(string ...$flags): static
    {
        return parent::email(...$flags)->min(5)->max(255);
    }
```

```
    CustomRule::make()->required()->email();

    // result:
    [
        'required',
        'email',
        'min:5',
        'max:255',
    ],
```

### About → Benefits

[](#about--benefits)

**Better syntax**

Similar to chaining DB column schema definitions in migrations, this package aims to provide a clean, elegant chaining syntax.

**Parameter Insights**

When dealing with string-based validation rules such as `decimal`, remembering what the available parameters can become a nuisance. As methods, you can get autocompletion and greater insights into the parameters available, along with a quick `@link` to the validation rule documentation, to better understand how the validation rule works.

**Variable Injection**

Instead of concatenating variables in an awkward manner like `'min:'.getMinValue()` you can clearnly inject variables as method arguments like `->min(getMinValue())`

**Conditional Logic**

Easily add validation rules based on conditions, using the `->when()` and `->unless()` methods, as well as by passing in conditional states into methods such as `->requiredIf($this->method() === 'POST')`.

**Wide Support of Rules**

Not only does it support all core-Laravel rules, but it also supports any custom rule classes that you define.

**Fully Customisable**

Full customisation using [macros](#customisation--macros), [conditional rules](#rules--conditional-rules), [reusable rules](#rules--reusable-rules) and [custom rule classes](#customisation--custom-rule-class)

### About → Performance

[](#about--performance)

The performance of this packages varies as does natural PHP execution time. A single validator test that tests a string and integer with varying validity (based on min/max rules) results in a range of -20 microseconds to 20 microseconds difference, with an average of a 14 microsecond delay.

The more the package is utilised in a single request, the less relative overhead is seen. For example, running the same validation rules with 20 varying strings and integers can result in an average of 9 microseconds or even less.

The overhead here is considered negligible.

### Side Feature → The `ValidationRule` class

[](#side-feature--the-validationrule-class)

An **optional**, different approach to a typical implementation of the `ValidationRule` interface is the `BradieTilley\Rules\Validation\ValidationRule` class which hanldes the horrible signature of the `Closure $fail` argument and *forced* `void` return type inside the abstract class, allowing your rule classes to ship with cleaner syntax.

To get started, simply extend the `BradieTilley\Rules\Validation\ValidationRule` class in your custom rule class. And instead of defining a `validate` method, define the `run` method.

So instead of this:

```
public function validate(string $attribute, mixed $value, Closure $fail): void
{
    if ($this->someCondition) {
        return; // pass
    }

    if ($this->otherCondition) {
        $fail('Some error message');

        return; // you can't return anything so you can't join the $fail and return lines together
    }
}
```

You would have this:

```
public function run(string $attribute, mixed $value): static
{
    if ($this->someCondition) {
        return $this->pass();
    }

    if ($this->otherCondition) {
        return $this->fail('Some error message');
    }

    return $this->pass();
}
```

#### Why

[](#why)

**Single line failures**

Because of the `void` return type of the `validate` method, you cannot `return $fail('Some error message');` in a single line, and if you adhere to any of the common code styles out there you also have to have an empty line before a `return;` statement (unless it's the first line in a body). This cleans things up a bit by allowing that clean single line return:

```
-$fail('Some error message')
-
-return;
+return $this->fail('Some error message');
```

**Readability**

By enforcing a return type (`static` in this case), this forces you to specify at least some form of a response rather than ambiguous empty `return;` statements that get used for pass and failure results. Although you could `return $this;`, it obviously encourages you to do the right thing and return a result. This improves readability by forcing you to explain the exit result:

```
-if ($this->someCondition) {
-    return;
-}
+if ($this->someCondition) {
+    return $this->pass();
+}
```

**Failure syntax**

Invoking a method is always visually cleaner than invoking a `Closure` variable. This provides a cleaner syntax:

```
-$fail('Some error message')
+$this->fail('Some error message');
```

**Method signature**

The `$fail` parameter of the `validate` method is messy. It's a `Closure` that requires importing at the top, and if you enforce generics in your project, the `$fail` parameter requires a type hint that explains any arguments and return types. Removing this type hint means the docblock is purely to say *"Run the validation rule."* which is superfluous and can be removed too.

```
-use Closure;

// ...

-    /**
-     * Run the validation rule.
-     *
-     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
-     */
-    public function validate(string $attribute, mixed $value, Closure $fail): void
+    public function run(string $attribute, mixed $value): static
    {
        // ...
    }
```

Issues
------

[](#issues)

If you spot any issues please feel free to open an Issue and/or PR and I'll address the issues.

Author
------

[](#author)

- [Bradie Tilley](https://github.com/bradietilley)

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance37

Infrequent updates — may be unmaintained

Popularity13

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 90.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~127 days

Total

5

Last Release

565d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f93c7d77d882d4106664cca209c6dccc0e531f307f56dc2247937358d193ef0b?d=identicon)[bradietilley](/maintainers/bradietilley)

---

Top Contributors

[![bradietilley](https://avatars.githubusercontent.com/u/44430471?v=4)](https://github.com/bradietilley "bradietilley (70 commits)")[![hungthai1401](https://avatars.githubusercontent.com/u/22017922?v=4)](https://github.com/hungthai1401 "hungthai1401 (4 commits)")[![owenvoke](https://avatars.githubusercontent.com/u/1899334?v=4)](https://github.com/owenvoke "owenvoke (2 commits)")[![bennothommo](https://avatars.githubusercontent.com/u/15900351?v=4)](https://github.com/bennothommo "bennothommo (1 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/bradietilley-laravel-rules/health.svg)

```
[![Health](https://phpackages.com/badges/bradietilley-laravel-rules/health.svg)](https://phpackages.com/packages/bradietilley-laravel-rules)
```

###  Alternatives

[illuminate/validation

The Illuminate Validation package.

18936.7M1.4k](/packages/illuminate-validation)[spatie/laravel-honeypot

Preventing spam submitted through forms

1.6k6.0M60](/packages/spatie-laravel-honeypot)[proengsoft/laravel-jsvalidation

Validate forms transparently with Javascript reusing your Laravel Validation Rules, Messages, and FormRequest

1.1k2.3M49](/packages/proengsoft-laravel-jsvalidation)[stevebauman/purify

An HTML Purifier / Sanitizer for Laravel

5325.6M19](/packages/stevebauman-purify)[axlon/laravel-postal-code-validation

Worldwide postal code validation for Laravel and Lumen

3853.3M1](/packages/axlon-laravel-postal-code-validation)[laravel-validation-rules/credit-card

Validate credit card number, expiration date, cvc

2412.2M5](/packages/laravel-validation-rules-credit-card)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
