slvler/phone-validation
slvler/phone-validation is a Laravel package for validating phone numbers. It provides a simple validation rule to check number format and country code, helping keep user-submitted contact data clean, consistent, and reliable. Lightweight and easy to integrate into forms and APIs.
composer require slvler/phone-validation
php artisan vendor:publish --provider="Slvler\PhoneValidation\PhoneValidationServiceProvider"
Set the AbstractAPI key in .env (confirm the exact key name in the published config/phone-validation.php; slvler packages typically use a package-prefixed variable):
PHONE_VALIDATION_API_KEY=your_abstractapi_key
Clear cached config after adding the key:
php artisan config:clear
First call:
use Slvler\PhoneValidation\Facades\PhoneValidation;
$result = PhoneValidation::phone('+14152007986')->validate();
dump($result);
// [
// 'number' => '14152007986',
// 'valid' => true,
// 'format' => ['local' => '4152007986', 'international' => '+14152007986'],
// 'country' => ['code' => 'US', 'name' => 'United States', 'prefix' => '+1'],
// 'location' => 'California',
// 'type' => 'mobile',
// 'carrier' => 'AT&T',
// ]
Where to look first:
README.md for the exact fluent method chain (phone()->validate() vs. a single validate() call).config/phone-validation.php for the env key and base URL.src/PhoneValidation.php to see how errors and HTTP responses are handled.src/Facades/PhoneValidation.php to confirm the facade accessor.config/app.php.Custom validation rule. The natural day-to-day integration is a Laravel ValidationRule:
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Slvler\PhoneValidation\Facades\PhoneValidation;
class ValidPhoneNumber implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$result = PhoneValidation::phone($value)->validate();
} catch (\Throwable $e) {
report($e);
$fail('The :attribute could not be validated right now.');
return;
}
if (! ($result['valid'] ?? false)) {
$fail('The :attribute is not a valid phone number.');
}
}
}
Then in a form request:
public function rules(): array
{
return ['phone' => ['required', 'string', new ValidPhoneNumber]];
}
Normalize before sending. Convert user input to E.164 first so the API returns stable results and your cache keys are consistent:
$e164 = (string) \Propaganistas\LaravelPhone\PhoneNumber::make($request->phone, $request->country)->formatE164();
$result = PhoneValidation::phone($e164)->validate();
Cache aggressively. Every call is a paid/limited HTTP request. Phone validity rarely changes, and carrier/line-type data changes slowly:
$result = Cache::remember("phone:".sha1($e164), now()->addDays(7), function () use ($e164) {
return PhoneValidation::phone($e164)->validate();
});
Persist the raw response. Store the JSON in a json column alongside the normalized number so you can re-render carrier/type without re-calling the API:
$profile->update([
'phone' => $e164,
'phone_meta' => $result,
]);
Queue bulk validation. Imports or backfills should never hit the API in a web request. Dispatch a job per number or batched job with rate limiting:
RateLimiter::for('abstractapi', fn () => Limit::perMinute(10));
Wrap it behind your own interface. The package is thin; an adapter keeps you free to swap in Twilio Lookup or libphonenumber locally:
interface PhoneLookup
{
public function lookup(string $e164): array;
}
Bind the package implementation in a service provider; call sites depend on the interface, not the facade.
Fake it in tests. Because it is an HTTP client under the hood, use Laravel's HTTP fake:
Http::fake([
'phonevalidation.abstractapi.com/*' => Http::response([
'number' => '14152007986',
'valid' => true,
'format' => ['international' => '+14152007986'],
'country' => ['code' => 'US'],
'type' => 'mobile',
], 200),
]);
PHONE_VALIDATION_API_KEY to .env has no effect in production until php artisan config:cache is rerun.valid: false is a 200, not an error. Branch on the valid field for business logic, and catch exceptions separately for transport/quota/auth failures.dump() the result once in staging to see what a bad key and an exhausted quota actually look like.propaganistas/laravel-phone (libphonenumber) for basic syntax and E.164 normalization, and reserve the API for carrier/line-type lookup where those fields are actually needed.format.international and country.code, not top-level shortcuts. Field presence varies by country, so use null-safe access (data_get).RateLimiter and cache.ConnectionException and 5xx, but never retry 4xx.Http::timeout() globally or in your adapter; default timeouts can leave requests hanging under network issues.composer.json, read src/ before relying on undocumented behavior, and be prepared to fork — it is a small enough class to vendor if needed.app(\Slvler\PhoneValidation\PhoneValidation::class) or inject the class; both are easier to swap in tests than a static facade.How can I help you explore Laravel packages today?