Skip to content
Devix Open Source

Guide

Getting started

composer require devix-labs/laravel-validators

Nothing to publish, nothing to configure. Every kind is a validation rule straight away:

$request->validate([
    'iban' => 'required|iban:AE,SA',
    'emirates_id' => 'required|emirates_id',
    'trn' => 'nullable|uae_trn',
    'vat' => 'nullable|vat:DE',
    'cnic' => 'nullable|cnic',
    'gstin' => 'nullable|gstin',
]);
Rule Number What is checked
iban IBAN, all 78 registry countries length, MOD 97-10, and the BBAN pattern where published
vat European VAT, 28 countries shape; check digit for AT, BE, DE, DK, FI, FR, GB, IE, IT, LU, NL, PL, PT, SE, SK
emirates_id Emirates ID Luhn
uae_trn UAE TRN shape (there is no public check digit)
saudi_id Saudi ID or Iqama Luhn
saudi_vat Saudi VAT shape
qatar_id, bahrain_cpr, oman_id Gulf IDs shape
kuwait_id Kuwait Civil ID weighted modulo 11
cnic, ntn Pakistan shape
pan India shape
gstin India base-36 check character

A parameter means "only these countries" for iban, and "assume this country" for vat:

'iban' => 'iban:AE,SA,PK',
'vat' => 'vat:DE',          // accepts 136695976 as well as DE136695976

Outside a form request

use Devix\Validators\Laravel\Facades\Validators;

$result = Validators::check('iban', $request->string('iban'));

$result->valid;            // bool
$result->reason;           // Reason::BadChecksum
$result->checked;          // Checked::Checksum — a check digit was verified
$result->formatted;        // 'AE07 0331 2345 6789 0123 456'
$result->country;          // 'AE'
$result->detail('bankCode'); // '033'

Validators::valid(), Validators::format() and Validators::mask() are there for the common cases. The class is framework-free — Devix\Validators\Validators::check() works in any PHP application.

Storing what you validated

Store $result->value — the cleaned form, without separators — and show $result->formatted:

$customer->fill([
    'iban' => $result->value,          // AE070331234567890123456
    'iban_bank' => $result->detail('bankCode'),
]);

A cast keeps that tidy:

protected function iban(): Attribute
{
    return Attribute::make(
        get: fn (?string $value) => $value === null ? null : Validators::format('iban', $value),
        set: fn (?string $value) => $value === null ? null : Validators::check('iban', $value)->value,
    );
}

Messages

The rules bring their own, written from the reason code:

That IBAN has a digit wrong — check it against the card or document.

Override them the way you override any Laravel message, or publish the language file:

php artisan vendor:publish --tag=validators-lang
// resources/lang/vendor/validators/ar/validators.php
'reasons' => [
    'bad-checksum' => 'رقم الآيبان غير صحيح — تحقق منه مرة أخرى.',
],

The rule object, when you want it fluent

use Devix\Validators\Laravel\Rules\IdRule;

$request->validate([
    'iban' => [(new IdRule('iban'))->only('AE', 'SA')],
    'vat' => [(new IdRule('vat'))->country('DE')],
]);

Its messages are the specific ones — too short, wrong check digit, unknown country — rather than a single "invalid" for every case.

Agreeing with the browser

Use @devix-labs/validators in the form and this package on the server, and both sides accept exactly the same numbers for exactly the same reasons. That is asserted, not assumed: 224 shared vectors run in this package's test suite.

Updated 15 Sep 2026