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.