dniccum/phone-number
Simplify phone number handling in Laravel with validation and formatting helpers. This package provides a dedicated validation rule for international numbers, supports country codes, and helps store phone fields consistently across forms, APIs, and user profiles.
Architecture fit. This is a presentation-layer component for the Nova admin panel, not a domain service. It fits cleanly only where phone numbers are incidental metadata captured by internal staff on low-to-moderate-volume CRUD screens (Contact, Lead, Customer). It does not fit, and should not be stretched to fit, anywhere phone data is a first-class business asset — outbound messaging, deduplication, CRM sync, public forms, imports, or APIs. The correct architectural framing is a UI veneer over infrastructure you own: canonical parsing/validation stays in application code, and the field handles entry ergonomics. If a team proposes this package as the phone-validation story, that's a scoping error to correct before approval.
Integration feasibility. Genuinely low-effort. composer require, auto-discovery, one field class plus compiled JS/CSS shipped through nova:publish, and a class swap from Text::make('Phone') to PhoneNumber::make('Phone'). Realistic estimate is half a day to a day including a spike, smoke tests, and asset verification — against one to two days of internal custom-field work. The integration surface is shallow enough that it is forkable in hours, which materially de-risks adoption.
Technical risk. Four items dominate, in order:
phone validation rule and an E.164 formatter. Those are different products with different blast radii. Assume the field is what ships and require a source read to confirm whether any standalone validation rule, formatter, or Twilio call actually exists in the artifact. If the Twilio Lookup path is real, it introduces a synchronous third-party network call on the Nova save path, per-lookup billing, and PII egress to a vendor — that is a security and legal review item, not a technical detail. If it is aspirational framing, the package contributes zero to canonical phone handling beyond the admin panel, and must not be counted toward a normalization roadmap.x-extensions. Masking libraries are a known regression vector for paste, autofill, screen readers, mobile numeric keyboards, and CSP (eval/inline script). Each of these is a gate, not a nice-to-have.Key questions to answer before approval.
Illuminate\Contracts\Validation\ValidationRule and an E.164 formatter, or only a Nova field?Stack fit. Laravel with Nova 4 or 5, PHP, standard Composer workflow — no infrastructure change, no new runtime, no queue or storage implications. The only external dependency, if the Twilio path is live, is a credential pair and an outbound HTTPS call; the recommendation is to disable it and normalize locally with giggsey/libphonenumber-for-php, which is what you should be on regardless.
Migration path. Narrow and reversible:
composer.json to mirror whatever the package allows, so the admin UI cannot silently drift on a Nova upgrade.fillUsing callback, because the mutator protects every write path — Nova, imports, seeders, tinker, API endpoints — and a Nova-only normalizer leaves CSV import as a second, un-normalized source that defeats the exercise. Render with an accessor plus displayUsing; never persist the rendered form, since search, dedupe, and tel: links all want canonical.PhoneNumberRule backed by libphonenumber and use it in both the Nova field and form requests/API controllers. The Nova field only covers the admin; three independent definitions of "a valid phone number" is the failure state.PhoneField extends PhoneNumber) so ten resources reference one class. When the package is outgrown or replaced, that is one edit, not a grep-and-replace.Compatibility. Verify PHP and Nova version ranges against the pinned stack. Confirm autoload/import path from source rather than the README — the description's phone(...)->formatE164() helper may not exist in the shipped artifact. Audit the compiled JS/CSS for eval() and inline script before deploying into a CSP-strict environment; any relaxation needs explicit sign-off. After every composer update or Nova upgrade, php artisan nova:publish must run — "the mask stopped working" is almost always stale assets, so put it in the deploy script rather than in someone's memory.
Sequencing.
Text for legacy), put a hard expiry on it — dual entry paths are how format drift becomes permanent.Maintenance. The code surface is one class plus compiled assets, so ongoing maintenance is close to zero — until it isn't. The recurring chores are asset republication after any dependency change, and watching an upstream with a single maintainer and a sparse release cadence. Add a CI job on the pinned Nova major that publishes assets and asserts the field renders and the mask is active; run the field's own tests against your pinned version. Without this you will discover breakage from an upgrade rather than from CI.
Support. Users are internal admins, so support volume is driven by mask UX complaints — paste, autofill, mobile keyboards, non-US numbers — not by crashes. Route those to a documented phone data standard rather than ad-hoc field tweaks. The vendor risk (single maintainer, 70 stars, zero dependents) is acceptable given forkability, but it must be recorded as an accepted risk with a named owner and a fork trigger defined up front.
Scaling. Compute and storage impact is negligible. Two scaling considerations matter more:
Failure modes. Ranked by likelihood × cost:
| Failure | Signal | Mitigation |
|---|---|---|
| Masked display string persisted | dd() shows (555) 123-4567 |
Mutator + spike before production |
| Silent extension truncation | Support rep can't reach a customer | Separate column for extensions |
| Stale assets after upgrade | "Mask isn't working" | nova:publish in deploy script |
| Nova major upgrade breaks field | Field fails to render | Pinned constraint + CI render assertion |
| Mask blocks non-US entry | Wrong numbers keyed for EU/APAC | Scope market before adoption |
| Twilio outage/latency/cost on save | Slow or failing saves, surprise invoice | Disable lookup; normalize locally |
| CSP blocks masking script | Mask silently inert | Asset audit before deploy |
| Accessibility regression | Paste/autofill/screen reader broken | Manual device + screen reader test |
| Maintainer abandonment | No release tracking your Nova major | Fork at adoption |
Ramp-up. Engineering ramp is under a day: composer require, one resource, one deploy, one smoke test. The org-level ramp is the real cost and it is not the package's — it is establishing and documenting the canonical phone format standard, wiring the shared validation rule into non-Nova surfaces, and writing the audit query. Budget that separately, because the package does not deliver any of it. Frame the adoption itself as a reversible, low-stakes tactical shortcut worth a half-day to a day for a visible admin data-quality win, explicitly not a platform bet and explicitly not a step toward a normalization roadmap.
How can I help you explore Laravel packages today?