Laravel 12 – Country-Based Phone Validation
Laravel 12 – Country-Based Phone Validation
In this tutorial, we will learn how to integrate country-based phone number validation in Laravel 12 using the propaganistas/laravel-phone package to ensure users register with valid, correctly formatted phone numbers.
If you're a video person, feel free to skip the post and check out the video instead!
Quick Overview
This guide walks you through integrating country-based phone number validation into your Laravel 12 application. You’ll start with a fresh Laravel project, install the propaganistas/laravel-phone package, and publish the required language files. The users table and User model are updated to handle phone and country fields, and the registration form is modified to collect both values. The CreateNewUser action validates the phone number according to the selected country, ensuring only correctly formatted numbers are accepted. Finally, you can test the application by registering users with valid phone numbers for Germany, US, or UK, while mismatched numbers will fail validation.
Step # 1 : Create a Fresh Laravel 12 Project with Livewire Starter Kit.
Before we dive in, you’ll need a fresh Laravel installation. For this guide, we’ll start from scratch and name our project phone. If you have the Laravel Installer installed globally, you can run
laravel new phone
No installer? No problem! Composer works just as well.
composer create-project laravel/laravel --prefer-dist phone
When using Composer, you won’t see the interactive setup prompts. Those prompts appear only when using the Laravel Installer. When using the Laravel Installer, you’ll be guided through a few setup prompts.
- Would you like to install the starter kit? — Choose Livewire.
- Which authentication provider do you prefer? [Laravel's built-in authentication]? — Choose Laravel.
- Would you like to use Laravel Volt? (yes/no)? — Choose No.
- After selecting None, you'll be asked about testing framework. — Choose Pest.
- Finally, type yes if prompted, or run npm install and npm run build to install and compile your frontend dependencies.
As a result we have Laravel 12 project named phone with the Livewire starter scaffolding (auth routes, register/login pages).
Step # 2 : Navigate to Your Laravel Project Directory
Open your terminal (for example, Git Bash, Terminal, or Command Prompt) and navigate to the root folder of your Laravel project by running.
cd c:xampp/htdocs/phone
Make sure you’re inside the project directory before running any Artisan or npm commands.
Step # 3 : Install Laravel Phone Package.
We’ll use the Propaganistas/Laravel-Phone package for phone number validation and formatting.
composer require propaganistas/laravel-phone
This package integrates Google’s libphonenumber library with Laravel’s validation system, allowing you to validate and format phone numbers for multiple countries.
Step # 4 : Publish Language files.
By default, Laravel 12 doesn’t include the resources/lang folder. Publish it by running
php artisan lang:publish
This command creates the file resources/lang/en/validation.php. Next, open that file and add a custom message to the returned array.
/* Custom Validation Start */
'phone' => 'The :attribute field must be a valid number.',
/* Custom Validation End */
Adding this message ensures users see a clear, friendly error message when an invalid phone number is entered.
Step # 5 : Add Phone and Country columns to users table.
Run the following command to add Phone and Country columns to the users table. It will generate a migration file named with a timestamp, for example: 2025_10_22_000000_add_phone_and_country_to_users_table.php.
php artisan make:migration add_phone_and_country_to_users_table --table=users
Open the generated migration file (database/migrations/xx_xx_xx_xxxx_add_phone_and_country_to_users_table.php) and update it with the following code.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Adds 'phone' and 'country' columns to the existing 'users' table.
* The 'phone' column stores the user's phone number.
* The 'country' column stores the user's country in ISO 3166-1 alpha-2 format (e.g. DE, US, GB).
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->nullable()->after('email');
$table->string('country', 2)->nullable()->after('phone'); // ISO 3166-1 alpha-2
});
}
/**
* Reverse the migrations.
*
* Removes the 'phone' and 'country' columns from the 'users' table.
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['phone', 'country']);
});
}
};
Once you’ve updated the file, run the migration to apply these changes to your database.
php artisan migrate
This will add the phone and country columns to your existing users table, preparing it for phone number and country data during registration.
Step # 6 : Update the User model (E.164 casting + fillable)
Open app/Models/User.php and add the phone cast and fillable attributes (or ensure phone and country are mass assignable).
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Propaganistas\LaravelPhone\Casts\E164PhoneNumberCast;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable, TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*
* Added 'phone' and 'country' to allow mass assignment during
* user registration and updates (these map directly to the
* new database columns added in the migration).
*/
protected $fillable = [
'name',
'email',
'password',
'phone',
'country',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'two_factor_secret',
'two_factor_recovery_codes',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @return array<string, string>
*
* This ensures the 'phone' field is automatically converted
* to and from the standardized E.164 format (e.g. +491512345678)
* when stored or retrieved from the database.
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'phone' => E164PhoneNumberCast::class, // stores phone in E.164 format
];
}
/**
* Get the user's initials (used in UI display or avatars)
*/
public function initials(): string
{
return Str::of($this->name)
->explode(' ')
->take(2)
->map(fn ($word) => Str::substr($word, 0, 1))
->implode('');
}
}
We added phone and country to $fillable so Laravel can safely assign these attributes in bulk when creating or updating a user. This aligns with the database schema we modified earlier and ensures smooth data handling during registration.
Step # 7 : Update the Registration Form to Include Country and Phone Fields.
Now that our database and model are ready to handle phone numbers and countries, we need to update the registration form so new users can provide this information when signing up. Open the file: resources/views/auth/register.blade.php and replace its contents with the following updated form.
<x-layouts.auth>
<div class="flex flex-col gap-6">
<x-auth-header
:title="__('Create an account')"
:description="__('Enter your details below to create your account')"
/>
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form method="POST" action="{{ route('register.store') }}" class="flex flex-col gap-6">
@csrf
<!-- Name -->
<flux:input
name="name"
:label="__('Name')"
type="text"
required
autofocus
autocomplete="name"
:placeholder="__('Full name')"
/>
<!-- Email Address -->
<flux:input
name="email"
:label="__('Email address')"
type="email"
required
autocomplete="email"
placeholder="email@example.com"
/>
<!-- Password -->
<flux:input
name="password"
:label="__('Password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Password')"
viewable
/>
<!-- Confirm Password -->
<flux:input
name="password_confirmation"
:label="__('Confirm password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Confirm password')"
viewable
/>
<!-- Country -->
<!-- User's country. Used for phone number validation and stored in users table. -->
<flux:select
name="country"
:label="__('Country')"
required
>
<option value="DE">{{ __('Germany') }}</option>
<option value="US">{{ __('United States') }}</option>
<option value="GB">{{ __('United Kingdom') }}</option>
</flux:select>
<!-- Phone Number -->
<!-- User's phone number. Validated according to selected country and stored in users table. -->
<flux:input
name="phone"
:label="__('Phone number')"
type="text"
required
autocomplete="tel"
:placeholder="__('e.g. +491512345678')"
/>
<div class="flex items-center justify-end">
<flux:button type="submit" variant="primary" class="w-full">
{{ __('Create account') }}
</flux:button>
</div>
</form>
<div class="space-x-1 rtl:space-x-reverse text-center text-sm text-zinc-600 dark:text-zinc-400">
<span>{{ __('Already have an account?') }}</span>
<flux:link :href="route('login')" wire:navigate>{{ __('Log in') }}</flux:link>
</div>
</div>
</x-layouts.auth>
The registration form now includes fields for country and phone number, allowing users to enter their country and mobile number during registration. These values are stored in the users table and validated according to the selected country.
Step # 8 : Update the CreateNewUser Action for Phone and Country Validation.
To ensure the registration form properly validates the new phone and country fields, we need to update the CreateNewUser action. Open: app/Actions/Fortify/CreateNewUser.php and update it as follows.
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Propaganistas\LaravelPhone\Rules\Phone;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*/
public function create(array $input): User
{
// Define phone validation rule based on the selected country and mobile type
$phoneRule = (new Phone())->countryField('country')->type('mobile');
// Validate user input
Validator::make($input, [
'name' => ['required', 'string', 'max:255'], // Name is required
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class), // Ensure email is unique
],
'password' => $this->passwordRules(), // Apply password rules
'country' => ['required', 'in:DE,US,GB'], // Country must be one of the allowed ISO codes
'phone' => ['required', $phoneRule], // Phone must match the selected country's format
])->validate();
// Create the user in the database with validated data
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => $input['password'],
'phone' => $input['phone'], // Store phone in E.164 format
'country' => $input['country'], // Store selected country
]);
}
}
This update ensures that the phone number entered by the user matches the selected country and that the country value is one of the allowed ISO codes (DE, US, GB), storing both fields correctly in the database upon registration.
Step # 9 : Testing the Phone.
Start the Laravel development server by running.
php artisan serve
Once the server is running, open the registration page, select a country (Germany, US, or UK), and enter the corresponding phone number to create a new user.
Now, if you select one country but enter a phone number from a different country, the validation should fail and prevent registration.
If you want strict validation of phone numbers (e.g., to fail when a number has fewer or more digits than expected), you need to combine the phone rule with a regex rule. The propaganistas/laravel-phone package alone only checks if a number is possible for the selected country, not the exact length.
Conclusion
By following this guide, you’ve successfully added country-based phone number validation to your Laravel application. Users can now enter their phone number along with their country during registration, and the system ensures that only correctly formatted numbers are accepted. With the propaganistas/laravel-phone package handling the phone validation and formatting, your application provides a reliable and efficient way to collect accurate contact information. You can also customize this setup further to match your specific requirements.
For more information, refer to the Propaganistas/Laravel-Phone documentation.
Share this with friends!
To engage in commentary, kindly proceed by logging in or registering
Subscribe to Our Newsletter
Stay ahead of the curve! Join our newsletter to see what everyone’s talking about.
0 Comments