mattkingshott/axiom
Axiom is a Laravel package by Matt Kingshott for defining business rules as reusable, testable classes. It offers a fluent API for validating models against domain invariants, keeping complex application logic clean and organized. It integrates with Laravel's validation and testing tools.
This release adds a Vietnamese citizen rule (12‑digit CCCD, plus legacy 9‑digit CMND) to the package's family of national‑identifier validators. Nothing else in the integration story changes.
composer update and confirm you land on v3.2.1 — country rules in this package have historically been added without a major bump, so pin the minor if you rely on rule behaviour.src/Rules/ (the new Vietnamese rule class) and the package service provider — it is usually where rule aliases and the translation namespace are registered.new VietnameseCitizenId() and as a string rule after the provider boots.FormRequest:public function rules(): array
{
return [
'full_name' => ['required', 'string', 'max:255'],
'citizen_id' => ['required', 'string', new VietnameseCitizenId()],
];
}
If you already had a hand‑rolled regex:/^\d{12}$/, swap it in the same commit — you get structural validation (province code, birth‑date segment, gender/century digit) for free.
Inline in FormRequests. The common case: one rule object on one field. Keep the field as string — never integer — and pair it with required/nullable as appropriate.
Conditional by country. For multi‑market apps, don't branch inside the FormRequest body; compose rules so only the matching rule runs:
'national_id' => [
'required',
'string',
Rule::when($this->country === 'VN', [new VietnameseCitizenId()]),
Rule::when($this->country === 'JP', [new JapaneseMyNumber()]),
],
Normalise before you validate. Users paste IDs with spaces, dots, and non‑breaking spaces. Use prepareForValidation() to strip separators so the rule sees a clean string, and so the value you persist matches what you validated:
protected function prepareForValidation(): void
{
$this->merge([
'citizen_id' => preg_replace('/[\s.\-]/u', '', (string) $this->citizen_id),
]);
}
Custom messages and attributes. Publish the package translations (or override messages() on the FormRequest) so the failure reads "Số CCCD không hợp lệ" rather than the raw rule key. Attribute names matter here — users see "citizen_id" otherwise.
Testing. Use datasets rather than one happy‑path test. Keep a small fixture set of valid CCCDs (including one issued post‑2000, to exercise the century/gender digit), plus invalids: 11 digits, 13 digits, wrong province prefix, impossible YYMMDD segment.
Storage and DTOs. Persist as string(12) (or string(20) if you must also hold legacy CMNDs). When mapping to an API resource, echo the ID back as a string and mask it in list views — validation rules don't protect you from leaking PII.
int. 12‑digit IDs exceed 32‑bit and lose a leading zero on a 9‑digit CMND. Use string in migrations, casts, and JSON. This is the single most common post‑integration bug.YYMMDD segment is only as good as the rule's implementation. Add a domain‑level sanity check (holder is ≥ 14 years old for KYC) on top; the rule won't know your business thresholds.bootstrap/cache after upgrading, or a newly added alias won't resolve.composer update with a caret constraint, a fresh rule can appear mid‑sprint; pin to ~3.2.0 for production branches.trim you forgot, not the rule.How can I help you explore Laravel packages today?