Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phone Number Advanced Laravel Package

gepur-it/phone-number-advanced

A Laravel package for advanced phone number handling, with parsing, formatting, validation, and country-aware utilities to keep numbers consistent across your app. Integrates with Eloquent models and Blade views for cleaner input and display.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and confirm discovery

composer require gepur-it/phone-number-advanced:^5.3

Use ^5.3 or later: v5.3.0 is the first release with PHP 8 support (release notes: upd php 8). If you are on PHP 8.0+, this is the minimum version to target. Before writing code, open composer.json and check for extra.laravel.providers / extra.laravel.aliases to confirm auto-discovery. If none is present, register the service provider and facade manually in config/app.php.

2. Publish and set the default region first

Phone parsing is region-dependent: "333 123 4567" is ambiguous until you tell the library it is Italian. Find the config file (php artisan vendor:publish --provider="GepurIt\PhoneNumber\..." — check the provider namespace in src/) and set the default region from an env var:

// config/phone-number.php
'default_region' => env('PHONE_DEFAULT_REGION', 'IT'),

This is the single most important setup step. Most "the package is broken" reports on libphonenumber wrappers trace back to a missing or wrong default region.

3. First use case: normalize before storage

The canonical day-one workflow is: accept a messy user string, parse it once, store E.164, render it formatted later.

use GepurIt\PhoneNumber\Facades\PhoneNumber; // verify facade name in src/

$phone = PhoneNumber::parse($request->input('phone'), $request->input('country', 'IT'));

$phone->getE164();          // +393331234567  -> store this
$phone->getInternational(); // +39 333 123 4567 -> show this
$phone->isValid();          // bool

The package is a thin Laravel wrapper over giggsey/libphonenumber-for-php, so the object semantics you get back follow libphonenumber's PhoneNumber/PhoneNumberUtil API. If the facade methods differ, src/ is ~a handful of files — read them end to end in ten minutes.

4. Where to look, in order

  1. README.md — likely sparse; skim for the facade name and config key.
  2. src/ — the service provider tells you what is bound in the container; the facade tells you the surface area.
  3. config/*.php — default region, maybe metadata path.
  4. composer.json — confirm PHP 8 is now allowed (v5.3.0) and check whether Laravel 9/10/11 are within the constraint. Also pin down which libphonenumber-for-php version is pulled in; the PHP 8 bump does not by itself guarantee the metadata dependency was refreshed.

Implementation Patterns

Validate in a Form Request, store canonical form

Use the package for parsing/validation, but keep a single canonical representation in the database.

class StoreContactRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'phone'   => ['required', 'string', 'max:32'],
            'country' => ['required', 'string', 'size:2'],
        ];
    }
}

// Controller
$phone = PhoneNumber::parse($request->phone, $request->country);

abort_unless($phone->isValid(), 422, 'Invalid phone number');

$contact->update(['phone_e164' => $phone->getE164()]);

If the package ships a validation rule (check src/Rules/), prefer it over hand-rolling; otherwise wrap the parse in a custom Rule.

class PhoneNumberRule implements ValidationRule
{
    public function __construct(private ?string $region = null) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        try {
            $parsed = PhoneNumber::parse((string) $value, $this->region ?? config('phone-number.default_region'));
        } catch (\Throwable) {
            $fail('The :attribute is not a valid phone number.');
            return;
        }

        if (! $parsed->isValid()) {
            $fail('The :attribute is not a valid phone number.');
        }
    }
}

Model accessors: E.164 in, display format out

Store E.164 (or E.164 + region), never store the formatted string as the source of truth.

public function setPhoneAttribute(string $value): void
{
    $this->attributes['phone_e164'] = PhoneNumber::parse($value, $this->country)->getE164();
}

public function getPhoneDisplayAttribute(): string
{
    return PhoneNumber::parse($this->phone_e164)->getNational();
}

This means a later switch to propaganistas/laravel-phone or raw libphonenumber only touches the mutator/accessor — not every view and controller.

Split country + national input in the UI

Best practice for international forms: a country <select> plus a plain national-format input. Pass the selected ISO code as the second arg to parse(). This avoids asking users to type + and avoids region ambiguity entirely.

Display formatting per context

  • Emails / SMS / PDFs → getE164() or getInternational() (unambiguous).
  • Local UI in a single-country app → getNational().
  • tel: links → always E.164.

Keep one helper (PhonePresenter) so the format decision lives in one place rather than being re-litigated in every Blade file.

Bulk import / CSV normalization

For imports, always pair the number column with a country column; fall back to the default region only when the country is blank. Collect failures into a rejects file rather than aborting the whole import:

$parsed = rescue(fn () => PhoneNumber::parse($row['phone'], $row['country'] ?? null), null, false);
if (! $parsed?->isValid()) { $rejects[] = $row; continue; }

SMS vs voice routing

If you route SMS, check the number type before sending; landlines parse and validate fine but cannot receive SMS.

if ($phone->getType() !== 'mobile') { /* fall back to voice or skip */ }

(Exact type constant/method name depends on the wrapper — verify in src/.)

Testing

Phone formatting is deterministic, which makes it perfect for data providers. Test the parse → store → display round trip with a fixture of numbers from each market you serve (IT, US, GB, DE at minimum), plus known-invalid strings.

Gotchas and Tips

The default region is not optional. Parsing "020 7946 0018" without GB in context will either throw or silently produce a wrong result. Always pass the region explicitly at the call site, even if a default is configured — it makes the code self-documenting and survives config changes.

Catch parse exceptions. libphonenumber throws NumberParseException on unparseable input. Any controller path that calls parse() without a try/catch is a 500 waiting to happen. Wrap it once in a service and return null on failure.

isValid() vs "possible". A number can be possible (right length/shape) but not valid (not in an assigned range). For signup forms prefer isValid(). Reach for the looser check only when you intentionally want to accept numbers from regions where metadata lags.

Metadata staleness. libphonenumber's data changes as countries reallocate ranges. The v5.3.0 release notes mention only a PHP 8 update, so there is no explicit evidence the libphonenumber metadata was refreshed. Check the resolved libphonenumber-for-php version in composer.lock; if accurate validation in fast-changing markets matters, you may need to bump the underlying library independently of this package (composer replace/alias, or fork).

Maintenance risk is still the headline, but PHP 8 is no longer the blocker. v5.3.0 (upd php 8) removes the most common installation failure on modern stacks, but the package remains low-activity (1 star, zero dependents) and the release notes are a single terse line. Before adopting:

  • Pin ^5.3 and verify the composer.json constraints actually allow your Laravel version — a PHP 8 bump does not guarantee Laravel 10/11 are tested or allowed.
  • Smoke-test the service provider and facade on PHP 8.2/8.3; an old provider using removed or deprecated Laravel APIs can still fail at boot.
  • Skim the diff between the previous tag and v5.3.0 — it may be nothing but the composer.json PHP constraint, which tells you exactly how much to trust the "PHP 8 support" claim.
  • Isolate it behind one small adapter class (App\Support\Phone) from day one. That adapter is your escape hatch to propaganistas/laravel-phone (actively maintained, same underlying lib) with a one-file change.

Don't store formatted strings as lookup keys. Store E.164, index it, and format on read. Searching by "333 123 4567" will miss "+393331234567".

Extensions and short codes. parse() handles extensions (ext. 12) but your DB column and validation rule need to decide whether to keep them. Service numbers and short codes (e.g. 112, 190) frequently fail isValid() — whitelist them explicitly if your product needs them.

Config caching. If you rely on the package's config, remember php artisan config:cache in production; a runtime config() read of a non-cached key will behave differently between local and prod.

Debugging. When a number misbehaves, dump the parsed object's getCountryCode(), getRegionCode(), and the raw national number rather than guessing. Nine times out of ten the region was inferred wrong, not the number.

Extension points. If the facade is Macroable (common for Laravel packages), add project-specific macros (PhoneNumber::macro('forDisplay', ...)) instead of forking. If not, a thin App\Support\Phone wrapper gives you the same benefit and keeps you portable.

Vendor-lock escape plan. The PHP 8 release improves the odds you can install it, but does not change the package's long-term trajectory. Treat it as a convenience wrapper, not a foundation. Keep PhoneNumber:: calls confined to one or two classes so a swap to direct libphonenumber-for-php usage is a refactor of an afternoon, not a sprint.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource