naif/saudi_id_number
A Laravel/PHP package for validating Saudi ID numbers. It checks 10-digit national ID and Iqama formats, verifies the checksum, and provides a reusable validation rule to keep Saudi ID validation simple and reliable in forms and APIs.
Install
composer require naif/saudi_id_number
The package is Laravel-auto-discovery era (2018), so if composer.json declares a dont-discover entry or you're on an old app, register Naif\SaudiIdNumber\FieldServiceProvider manually in config/app.php.
Look here first, in this order
composer show naif/saudi_id_number — check the laravel/nova constraint. A 2018 release almost certainly pins Nova ^1.0, which is a hard blocker on Nova 4/5 unless you fork it.vendor/naif/saudi_id_number/src/ — read the field class (a Text subclass) and the service provider. The provider's boot() is where the JS/Vue component gets registered with Nova::script(...). That one line tells you whether the field will render at all on your Nova version.vendor/naif/saudi_id_number/resources/js/ — the actual validation logic. It is small (regex + Luhn), which matters because you will likely re-implement it.First use case
A Nova resource where an operator types a Saudi national ID — customer profile, employee onboarding, KYC record:
use Naif\SaudiIdNumber\SaudiIdNumber;
public function fields(NovaRequest $request)
{
return [
SaudiIdNumber::make('National ID')
->rules('required', 'digits:10'), // see Gotchas: the package does NOT do this
];
}
Load the resource form and confirm the field renders as a normal text input. If it renders but never turns red on 123, the JS asset isn't loading — that's the failure mode you're testing for in step one.
Treat it as a UI hint, not a validator. The package's own description says it does not prevent submission. The correct day-to-day pattern is: package field for the red-border affordance, Laravel validation rule for correctness.
SaudiIdNumber::make('National ID')
->rules(['required', 'digits:10', new SaudiNationalIdRule])
->updateRules(['required', 'digits:10', new SaudiNationalIdRule])
->help('10 digits, starting with 1 (citizen) or 2 (resident)');
Write the rule yourself — it's ~15 lines and covers what the JS does: length 10, first char 1 or 2, and the Luhn mod-10 checksum over the digits.
Keep the field in a shared trait or field-set method so the ID field and its rules are defined once and reused across Customer, Employee, and Lead resources. Duplicating it invites drift where one resource validates and another doesn't.
Pair with a masked display on index. The field inherits Text, so ->onlyOnForms() or ->hideFromIndex() is usually what you want — you don't want full national IDs in a Nova index table.
Fork-and-vendor if Nova ≥ 4. Because the package is 1 star, unreleased since 2018, and has zero dependents, the realistic integration pattern is: copy src/ + resources/js/ into nova-components/SaudiIdNumber/, run php artisan nova:field, and rewire the component. You keep the behaviour, you own the maintenance, and you get Nova 4/5's asset pipeline instead of Nova::script.
Alternative worth considering: skip the package entirely and use Text::make('National ID')->rules(...) plus a small FormField/after hook for the client-side hint. That is roughly the same amount of code and no dead dependency.
0000000000, or a wrong checksum all submit fine unless you add a server-side rule. Never rely on this field for data integrity — Nova's API, imports, and artisan tinker bypass it entirely.Nova::script registration and the component name no longer line up. Check the browser console before debugging PHP.Nova::script, not just forms using the field. Minor, but it means a broken bundle breaks unrelated screens.$component = 'saudi-id-number' but the built JS registers something else, you get a silent fallback to text. public $component in the field class vs. Vue.component(...) in resources/js/field.js must match exactly.dist/js/field.js; editing source without npm run dev/prod changes nothing. Watch for a committed dist that's older than src.Rule class must do the real work.composer.json and note the fork date in a comment.$component, or override resolve/fill if you need to normalise input (strip spaces/dashes) before saving. Normalising in fill() plus a digits:10 rule handles the common "user typed spaces" case.dist stale; validates in browser but bad data in DB → missing server-side rule.How can I help you explore Laravel packages today?