laranail/validation is not published to Packagist, so there is no registry-version badge to show: see Install.
Write Laravel validation rules with IDE autocompletion instead of memorising string syntax — each rule type exposes only the methods that apply to it,
each()andchildren()keep parent and child rules in one place, and large wildcard arrays validate tens of times faster — see the benchmarks.
Targets PHP ^8.5 on Laravel ^13.
// Before
'name' => 'required|string|min:2|max:255',
'email' => ['required', 'email', Rule::unique('users')->ignore($id)],
'role' => Rule::when($isAdmin, 'required|string|in:admin,editor'),
'items' => 'array',
'items.*.id' => 'required|integer|exists:items,id',
'items.*.name' => 'required|string|max:255',
// After
'name' => FluentRule::string('Full Name')->required()->min(2)->max(255),
'email' => FluentRule::email('Email')->required()->unique('users', 'email', fn ($r) => $r->ignore($id)),
'role' => FluentRule::string()->when($isAdmin, fn ($r) => $r->required()->in(['admin', 'editor'])),
'items' => FluentRule::array()->each([
'id' => FluentRule::integer()->required()->exists('items', 'id'),
'name' => FluentRule::string()->required()->max(255),
]),composer require laranail/validationNothing to configure: the service provider registers itself through package discovery and the builders work without publishing anything. Publish the config only for the global safety fuse or the opt-in string rule aliases:
php artisan vendor:publish --tag=laranail::validation-configAdd HasFluentRules to a form request and return fluent rules from rules(). The trait is
what enables the optimized path — without it the builders still work, they just compile to
ordinary Laravel rules.
use Illuminate\Foundation\Http\FormRequest;
use Simtabi\Laranail\Validation\FluentRule;
use Simtabi\Laranail\Validation\HasFluentRules;
use Simtabi\Laranail\Validation\Rules\Banking\Iban;
final class StorePayoutRequest extends FormRequest
{
use HasFluentRules;
public function rules(): array
{
return [
'account' => FluentRule::string('Account')->required()->rule(new Iban()),
'email' => FluentRule::email()->required()->notDisposable(),
'lines' => FluentRule::array()->required()->max(50)->each([
'sku' => FluentRule::string()->required()->exists('products', 'sku'),
'amount' => FluentRule::numeric()->required()->min(0.01),
]),
];
}
}$request->validated() returns the same shape it always did. For Livewire use
HasFluentValidation; outside both, RuleSet::from([...])->check($data).
Full documentation is at opensource.simtabi.com/documentation/laranail/validation.
- Installation — Composer install, supported versions, optional Boost skills
- Getting started — a form request end to end, then the same builders elsewhere
- Configuration — the publishable config, opt-in string rule aliases, and the per-chain options
- Architecture — builders, compiler, optimized execution, and why the fast path is safe
- Performance — what makes it fast, the benchmarks, and when none of it helps
- Comparison — how this differs from rule strings and Laravel's
Ruleclass - Troubleshooting — common failure modes and their causes
- Release — versioning, tagging, and the CI-managed changelog
- Rule reference — every entry point, modifier, conditional, and macro hook
- Rule library — the extended rules: IBAN, IMEI, postal codes, IP classification, and the rest
- Phone rule — countries, line types, strictness, and E.164-normalised uniqueness
- Person names — any number of name fields, several names in one field, and "at least one"
- Identity and network fields — usernames, URLs, IP and MAC addresses, and the email additions
RuleSet— build, compose, inspect, export, and validate- Array validation —
each()for wildcards,children()for fixed keys - Error messages — labels and per-rule messages on the rule itself
- Livewire — the
HasFluentValidationtrait, plus Filament - Testing —
FluentRulesTesterand the Pest expectations - Static analysis — the opt-in arch test for untyped
field()chains
- Migrate existing rules — convert incrementally; both forms coexist
- Extend parent form-request rules — add to or override inherited rules
- Validate a phone number — country, line type, and the duplicate a plain
uniquemisses - Reject profanity — your word list, the package's matching
- Contribute a locale — the completeness bar every shipped language meets
- Changelog · Upgrading · Credits · Contributing · Security · Code of conduct
2.0 states what SemVer covers. The stable surface is: FluentRule,
FluentSchema, RuleSet (including its events and before()/after() hooks), the rule
classes and their constructor signatures, the contracts (ClientCheckable,
PrecognitionSkippable, TermList, FluentRuleContract), Check, Regex,
Validation::fake(), RuleRegistrar, the console commands, and the laranail.validation.*
config keys.
Everything marked @internal — the fast-check compiler, the optimizer validators, the batch
machinery, everything under Internal\ — may change in a minor, and an arch test enforces the
boundary. Build on the stable list; the optimizer is an implementation detail behind it.
Constrain to ^0.1 and read UPGRADING.md before moving between versions —
rector-migrate-0.1.php auto-migrates the mechanical break (the service provider moved into
Providers/), and rector-migrate-1.0.php still covers the 0.x one. Deprecations post-1.0 are
marked @deprecated with the replacement and removal version, kept for at least one minor,
and removed only in the next major. Behaviour-correcting fixes can still mean input an
application previously accepted is now correctly rejected; UPGRADING calls those out.
composer install
composer test # Pest
composer phpstan # level max, 100% type coverage
composer format # Pint
composer rector
php benchmark.php # the optimizer's headline numbersTests run on Orchestra Testbench — there is no host application, so use vendor/bin/testbench
rather than php artisan. Fixtures live under workbench/.
| Package | What it owns |
|---|---|
laranail/atlas |
Country, currency and language data — and the rules over it |
laranail/enumerator |
Enum values, names and transitions |
laranail/chrono |
Timezones, date existence and ambiguity |
laranail/toolkit |
Password strength and common-password rejection |
laranail/captcha |
Turnstile, hCaptcha and reCAPTCHA |
laranail/email |
Maintained disposable and role-account lists, and a production DNS resolver |
laranail/package-tools |
The package scaffolding this one is built on |
Email deliverability ships here as Rules\Network\DeliverableEmail, over a bundled cached
resolver. laranail/email supplies maintained
disposable-domain and role-account lists and a production resolver, replacing those fallbacks
through the same contracts — installing it changes nothing you call.
Questions and ideas belong in Discussions; bugs in Issues.
Originally written by Sander Muller, and maintained here under the laranail org with thanks to all contributors.
Issues and PRs are welcome — see CONTRIBUTING.md. Report vulnerabilities per SECURITY.md (opensource@simtabi.com); participation follows the Code of Conduct.
MIT © Simtabi LLC. See LICENSE.