Build a Phone Number Validator with Laravel
Time to read: 7 minutes
Data validation is essential to any application, regardless of its size and purpose. Without it, you risk using data that's forged by malicious actors, contains only junk—or is a combination of both. And it doesn't matter where the data comes from either; whether from a web form, retrieved from a database, or read from an environment setting.
Gladly, validation is extremely common in modern software development, so much so that there are numerous third-party packages available on Packagist, and PHP's major frameworks all have a validation component. With them, you can validate email addresses, passwords, dates, IP addresses, numbers and so much more!
However, what happens when the package or framework doesn't support your use case, such as validating a phone number? In that case, you roll your own!
In this tutorial, we're going to build a custom Laravel validation rule object that validates a phone number from any country in the world, and then show you several ways of using it.
Prerequisites
To complete the tutorial, you will need the following things:
- PHP 7.4 or newer (ideally version 8).
- Experience developing with Laravel.
- A free or paid Twilio account. If you are new to Twilio click here to create a free account now and receive a $10 credit when you upgrade to a paid account.
- Composer installed globally (and Git so that Composer works fully)
- cURL
Create the base Laravel application
The first thing to do is to bootstrap a Laravel application. There are several ways to do that, such as the Laravel installer and Laravel Sail. However, one of the simplest is with Composer, by running the commands below.
The commands will create the application in a new directory named twilio-phone-number-validator. To save a bit of time, the commands also change to the new directory and use the Artisan console to launch the application listening on localhost on port 8000.
If you open http://localhost:8000 in your browser of choice, it should look similar to the screenshot below.
Retrieve and register environment variables
With the base application ready to go, you next need to retrieve your Twilio credentials and store them in Laravel's configuration. We need the credentials as the Lookup API requires authentication.
First, add the code below to the end of your .env file, located in the application's root directory.
Then, from the Twilio Console's Dashboard, which you can see below, copy your "Account SID" and "Auth Token" and replace the respective placeholders in .env with them.
Add the required dependencies
Before we can write some custom code, we need to install the required dependencies. Gladly, there's only one: Twilio's PHP Helper Library. To install it, run the command below.
Create a lookup service
Now, let's write some code!
We'll start by creating a small service class to encapsulate the logic for interacting with Twilio's Lookup API. Yes, we could write the code directly in a controller, but doing so isn't the most inspiring. It's not the most flexible either.
By using a lookup service, we can validate the number in a variety of different ways and contexts, should the need arise. To do that, from the command line, create a new nested directory structure, app/Service/Twilio, by running the commands below.
If you're using Microsoft Windows, use the following commands instead.
Next, create a new file, app/Service/Twilio/PhoneNumberLookupService.php, in your editor or IDE and paste the following code into it. Then, let's step through what it's doing together.
PhoneNumberLookupService
has one purpose: to validate a phone number, which it does in the validate
method.
It starts by initializing a class-member variable, $client
, which is a Twilio Client
object, using the Twilio account SID and auth token which were stored in .env earlier. This is how the application will make authenticated calls to Twilio's Lookup API.
The validate
method contains the core validation logic, taking the phone number to check as its sole parameter. If the phone number is empty, it immediately returns false
. This is because an empty string would never validate successfully. So we save a request—and respect the user's time—by exiting early.
If the string isn't empty, a request is made to Twilio's Lookup API using the initialized Twilio Client
object. The call is wrapped in a try/catch block because a TwilioException
will be thrown if the phone number isn't valid. If an exception is thrown, we catch it and return false
. If an exception isn't thrown, we know that the phone number is valid, so true
is returned.
As the request returns information in the response, it might seem wasteful to throw that information away. However, using the Lookup API can be far less work than creating a regular expression capable of validating every possible phone number yourself. Plus, the Lookup API doesn't provide a pure validation endpoint.
Create a custom validation rule
With the lookup service finished, we're now going to create a custom validation rule that will use it to validate a phone number.
First, in the terminal, run the command below to have Artisan create the core of the class in app/Rules/PhoneNumberLookup.php.
Then, replace the body of the class with the code below (formatted for readability).
The constructor takes a PhoneNumberLookupService
object, $phoneNumberLookupService
, to initialize a class member variable of the same name.
The passes
method determines if the rule passes or not; an apt method name, no? It takes two arguments:
$attribute
: The name of the attribute (which we won't use).$value
: The attribute's value.
The method passes $value
to $phoneNumberLookupService
's validate
method and returns the result, which determines if the phone number is valid or not.
The message
method provides a suitable validation error message which is displayed to the user, should the phone number fail validation. It's quite a generic message, but it's suitable for this tutorial.
NOTE: Check out how to customize and localize error messages, if you want to display the message in multiple languages.
Before we move on, don't forget to add the required use statement below, if your text editor or IDE doesn't auto-complete them for you.
Create a new controller
Now that the plumbing code, if you will, has been written, we'll create a new controller class, named PhoneNumberController, that will use the new Rule to validate a request which contains a phone number.
Use Artisan to generate the core of the controller in app/Http/Controllers by running the command below.
Then, use the code below as the body of the new class.
Then, add the use
statement below.
The show
method first instantiates a new PhoneNumberLookupService
, using the Twilio account SID and auth token retrieved using the env helper. It then attempts to validate the phone_number
attribute in the request's body using two existing Laravel validation rules (required, string) and our custom rule.
If you prefer, you could use a closure instead, which you can see in the example below.
The closure receives three arguments:
$attribute
: The attribute's name$value
: The attribute's value$fail
: A callback function to execute if validation fails
For the closure to pass, PhoneNumberLookupService
's validate
method must successfully validate the phone number. If it doesn't, then the $fail
callback is run.
Register a route for the controller
To use the new controller we have to register a route for it. To do that, add the code below to the end of routes/api.php.
The route allows POST requests to /api/validate-phone-number
, which will be handled by PhoneNumberController
's show
method.
Test the code
There are a host of ways to test the code, such as using Postman or PhpStorm's Terminal. However, let's use cURL—my favorite command-line tool! Run the command below, replacing <your phone number>
with your phone number.
If everything works as expected, you will see output in your terminal similar to the example below; the important part is the HTTP/1.1 200 OK status code.
Next, let's see an example of an unsuccessful request, by running the command below, which sends "+1" as the phone number.
This will never validate successfully, so you will see output in your terminal similar to the example below; the important part being the HTTP/1.1 302 Found status code.
Simplify instantiation by using a Service Provider
There's nothing necessarily wrong with instantiating the PhoneNumberLookupService
object directly in the controller—if we're only going to use it there.
But, often, a better approach is to use a Service Provider along with constructor injection. So let's create a Service Provider and make the lookup service available as a service in Laravel's Service Container.
First, run the command below to have Artisan create the core of a new Service Provider for us, in app/Providers/PhoneNumberLookupServiceProvider.php.
Then, open the new file and replace the register
method with the following code.
When run, the closure in the second argument will instantiate a new PhoneNumberLookupService
object. Its constructor receives the Twilio Auth SID and Token, registered in .env, which it will use to initialize the class' Client
object.
The new PhoneNumberLookupService
object will then be registered as a service named PhoneNumberLookupService
with Laravel's service container.
NOTE: Don't forget to add the use
statement below, if your text editor or IDE doesn't auto-complete them for you.
Register the Service Provider
With the Service Provider created, we next need to register it so that Laravel will use it when the application is launched. To do that, in config/app.php add the following at the end of the "Application Service Providers" section.
Refactor PhoneNumberController
With the Service Provider in place and integrated into our application, let's refactor PhoneNumberController
to use it, by adding the following code to the top of the class.
This change makes use of Laravel's Zero Configuration Resolution to retrieve a PhoneNumberLookupService
object from the Service Container and pass it to the controller's constructor when it's instantiated. This, in turn, uses it to initialize a private member variable, named $service
.
Next, replace the show
method's body with the code below.
It's not too different from the first iteration. This time, PhoneNumber
is initialized with the class-member variable initialized in the constructor, not one that we directly instantiated in the method itself.
Test the code
With the changes made, use cURL, as before, to test that a valid phone number still validates successfully. I've included the cURL example below to save you time retrieving it.
That's how to build a phone number validator with Laravel
With a custom validation rule and some glue code in a controller, you can check if any phone in the world is real, pretty darn quickly. What's more, by adding in a custom Service Provider, you can use the validation logic anywhere in your Laravel application, as and when the need arises. Powerful and flexible!
I hope that this has inspired you to create your own custom validation rule and am excited to hear about what you build!
Matthew Setter is a PHP Editor in the Twilio Voices team and a polyglot developer. He’s also the author of Mezzio Essentials and Docker Essentials. When he’s not writing PHP code, he’s editing great PHP articles here at Twilio. You can find him at msetter@twilio.com; he's also settermjd on Twitter and GitHub.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.