Street address validation involves verifying the accuracy and completeness of addresses provided by users to ensure the addresses exist and are correctly formatted according to postal standards. This process helps businesses reduce errors related to incomplete or incorrect addresses, such as late or unsuccessful deliveries or sending out irrelevant information to clients based on their location, which can incur costs or damage a business’s reputation.
Address validation improves data accuracy, enhances user experience, and ensures timely deliveries and communications. For instance, CRM systems help ensure that the customer information provided is accurate, which helps facilitate better communication. In e-commerce, it helps minimize shipping errors and enhances overall customer satisfaction by ensuring that products ordered reach the correct destination. Logistics companies rely on accurate address validation to optimize route planning and reduce operational costs.
In this article, you’ll learn how to use the street data provided by GeoPostcodes to create a robust address validation system in Laravel, a PHP web framework.
Prerequisites
To follow along with this tutorial, make sure you have the following:
- Node.js and npm installed on your local machine
- A code editor and a web browser. This guide uses VS Code and Firefox Browser.
Obtaining Street Data from GeoPostcodes
First, you need to obtain street data from GeoPostcodes to use in your PHP application.
Log in to the GeoPostcodes portal and select Download Center.
On the Download Center page, under Samples, select Street database.
On the Street database page, under Packages, choose North America, sample countries.
GeoPostcodes offers comprehensive international address databases, but this tutorial only uses US data for the sake of simplicity.
On the Download page, you’ll find the description of the data you have selected and the file format. Click the Download button, read the license restriction information displayed, and download the data.
The street data will be downloaded to your local machine as a zipped file. Unzip the file and take note of the file named GPC-STRT-GEO-SAMPLE-A1-SELECTED.csv under the path <EXTRACTED-FOLDER-NAME>/CSV
. This file contains all the data you need to create the street address validation system. You will use this file later.
Creating a Laravel Application
Next, you’ll create a Laravel application that will make use of the data you just obtained. The Laravel application will have a user interface that captures the relevant data from the user—street, city, state, and postal code—and a controller that validates whether the information provided is a valid address by checking it against data stored in the database. If the address provided is valid, the user interface will show a success message. If not, it will display an error message.
Create a Laravel application by executing the command below in your terminal:
composer create-project laravel/laravel street-address-validation
Note: This tutorial’s workflow is based on Laravel 11.
You will first implement the backend logic and then implement the user interface later.
Implementing the Backend Logic
You now need to create a database table and populate it with the CSV data you obtained in the previous step, which involves a couple of steps.
First, copy the CSV file you obtained in the previous step in the project root folder and open the file in the IDE. You’ll notice that the CSV file has several columns, but this tutorial only uses a few: street
, locality
(will represent city
), region1
(will represent state
), and postcode
.
Next, cd
into the project root directory and create a database table for storing the street data by executing the command below in your terminal.
cd street-address-validation php artisan make:migration create_street_address_table
This command creates a new migration in the database/migrations
folder. Open the file and replace the up
method with the code below:
public function up(): void { Schema::create('street_address', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('street'); $table->string('city'); $table->string('state'); $table->string('zip_code'); }); }
The up
method uses the Schema::create
function to specify the structure of the table. It includes an auto-incrementing primary key (id
), timestamp columns (created_at
and updated_at
), and string columns for street
, city
, state
, and zip_code
. This sets up the necessary schema to store street address information in the database.
Next, run the migration with this command:
php artisan migrate
Then, create a model to interact with the database by executing the command below.
php artisan make:model StreetAddress
The command creates a model in the app/Models
folder. Open the model and add the following code below the use HasFactory;
statement to specify the table name and the mass-assignable fields:
// Specify the table name protected $table = 'street_address'; // Specify the mass-assignable fields protected $fillable = [ 'street', 'city', 'state', 'zip_code', ];
With the database table and the model ready, you can now use the CSV file you copied to the project root folder to seed the database. Execute the command below to create a database seeder:
php artisan make:seeder StreetAddressSeeder
The command creates the seeder in the database/seeders
directory.
Open the seeder you created and add the following code inside the run
method:
// Truncate the table to remove existing data StreetAddress::truncate(); // CSV file path $csvFile = fopen(base_path('GPC-STRT-GEO-SAMPLE-A1-SELECTED.csv'), 'r'); // Check if the file is opened successfully if ($csvFile === false) { die('Error opening the CSV file.'); } // Skip the first row (the header) fgetcsv($csvFile, 1000, ','); // Define the columns to extract and their positions $columnsToExtract = [ 'street' => 11, 'locality' => 8, 'region1' => 4, 'postcode' => 9 ]; // Loop through all the rows and insert the data into the DB while (($row = fgetcsv($csvFile, 1000, ';')) !== false) { // Extract the specified columns $extractedData = []; foreach ($columnsToExtract as $column => $index) { if (isset($row[$index])) { $extractedData[$column] = $row[$index]; } else { $extractedData[$column] = ''; // Handle missing data } } StreetAddress::create([ 'street' => $extractedData['street'], 'city' => $extractedData['locality'], 'state' => $extractedData['region1'], 'zip_code' => $extractedData['postcode'], ]); } // Close the file fclose($csvFile);
This code first truncates the database table to remove any existing data. Then, it opens the specified CSV file for reading and skips the header row. It defines specific columns to extract from the CSV with their respective positions. As it loops through each row of the CSV, it extracts the necessary columns, handling any missing data by assigning an empty string. It then uses the StreetAddress
model to insert the extracted data into the appropriate database table. Finally, the CSV file is closed.
Make sure you import the StreetAddress
model into the current namespace:
use App\Models\StreetAddress;
Next, run the seeder using the command below:
php artisan db:seed --class=StreetAddressSeeder
This command executes the StreetAddressSeeder
class and populates the appropriate database table with data extracted from the CSV file.
Next, you’ll create a controller to handle user requests. The controller will check whether the provided user address exists in the database and return the appropriate response.
To create the controller, execute the command below in your terminal:
php artisan make:controller StreetAddressController
The command creates the controller in the app/Http/Controllers
directory. Open the file and add the following code to the StreetAddressController
Class:
public function create() { return view('street_address.create'); } public function validate(Request $request) { // Validate the incoming data $request->validate([ 'street' => 'required|string', 'city' => 'required|string', 'state' => 'required|string', 'zip_code' => 'required|string', ], [ 'street.required' => 'Street field is required.', 'city.required' => 'City field is required.', 'state.required' => 'State field is required.', 'zip_code.required' => 'Zip Code field is required.', ]); // Retrieve inputs $street = $request->input('street'); $city = $request->input('city'); $state = $request->input('state'); $zipCode = $request->input('zip_code'); // Check if the provided address exists in DB $addressExists = StreetAddress::whereRaw('LOWER(street) = ?', [strtolower($street)]) ->whereRaw('LOWER(city) = ?', [strtolower($city)]) ->whereRaw('LOWER(state) = ?', [strtolower($state)]) ->whereRaw('LOWER(zip_code) = ?', [strtolower($zipCode)]) ->exists(); // Return the appropriate response if ($addressExists) { return back()->with('success', 'Address is valid.'); } else { return back()->withErrors(['message' => 'Address does not exist. Please check your input.'])->withInput(); } }
The create
method returns a view that displays a form for entering a new address. You will create this form later. The validate
method checks if the address data the user provided is correct. It enforces that street
, city
, state
, and zip_code
fields are required and must be strings. If validation fails, custom error messages are shown. If the validation succeeds, the code retrieves the input data and checks if an address with the same details exists in the database. If the address exists, it returns a success message. Otherwise, it returns an error message and the input data to the form.
Make sure you import the StreetAddress
model into the current namespace:
use App\Models\StreetAddress;
Lastly, set up routing for the application by replacing the code in the routes/web.php file with the following:
<?php use App\Http\Controllers\StreetAddressController; use Illuminate\Support\Facades\Route; Route::get('/', [StreetAddressController::class, 'create']); Route::post('/', [StreetAddressController::class, 'validate'])->name('validate.address');
Your backend logic is now complete.
Implementing the User Interface
Your user interface will be an HTML form where the end user can input an address and submit it to the backend for validation.
Create a new file named street_address/create.blade.php in the resources/views
folder and add the code below:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Street Address Validation</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous" /> </head> <body> <div class="container"> <form method="POST" action="{{ route('validate.address') }}" class="mt-5 p-4 border rounded" > @csrf <h2 class="text-center mb-4">Street Address Validation</h2> <!-- Success Message --> @if (session('success')) <div class="alert alert-success">{{ session('success') }}</div> @endif <!-- Validation Errors --> @if ($errors->any()) <div class="alert alert-danger"> @foreach ($errors->all() as $error) <p>{{ $error }}</p> @endforeach </div> @endif <!-- Street --> <div class="mb-3"> <label for="street" class="form-label">Street</label> <input type="text" class="form-control" id="street" name="street" value="{{ old('street') }}" required /> </div> <!-- City --> <div class="mb-3"> <label for="city" class="form-label">City</label> <input type="text" class="form-control" id="city" name="city" value="{{ old('city') }}" required /> </div> <!-- State --> <div class="mb-3"> <label for="state" class="form-label">State</label> <input type="text" class="form-control" id="state" name="state" value="{{ old('state') }}" required /> </div> <!-- Zip Code --> <div class="mb-3"> <label for="zip_code" class="form-label">Zip Code</label> <input type="text" class="form-control" id="zip_code" name="zip_code" value="{{ old('zip_code') }}" required /> </div> <!-- Submit Button --> <button type="submit" class="btn btn-primary"> Validate Address </button> </form> </div> </body> </html>
The form includes fields for entering the street, city, state, and zip code, each with validation to ensure they are completed. The form displays success or error messages received from the backend. It uses Bootstrap for styling and includes a CSRF token for security.
Testing the Application
To test if everything is working as expected, run the Laravel server by executing the command below in the terminal:
php artisan serve
Navigate to http://localhost:8000
in your web browser.
In the displayed HTML form, let’s test what will happen if you provide an address with an incorrect zip code. Use “East 136th Street” as the street, “Chicago” as the city, “Illinois” as the state, and “60634” as the zip code.
Note: GeoPostcodes’ sample data only includes a few addresses for testing purposes. If you try to input a valid address in the North American region that is not listed in the sample dataset, the application will return an error indicating that the address provided is invalid.
The form will display an error indicating that the address you provided is invalid:
Now, provide the correct zip code (“60633”). The form will display a success message:
This confirms that everything is working as expected.
You can access the full project code for this tutorial on GitHub.
The Value of a Comprehensive International Address Database
Even though this article only showed how to validate US addresses, using a comprehensive database is even more important if you need to validate addresses on a global scale.
An address format can consist of a number, complex or building name, street, zip code, locality, and administrative division. How these are presented can differ considerably between regions. For instance, the number may be written before or after the street name, or, in some cases, it might even be omitted because the building is prominent enough. If the building is an apartment complex, there could be an additional number associated with the building to indicate the occupant’s unit.
Different countries also use different principles for abbreviations. For instance, in France, “ST” is short for the municipality name “Saint,” while other countries use “ST” or “St” as an abbreviation for “street.” Even if you could use capitalization to differentiate between the uses in this case, remember that people from different countries will not have this context. They could also make mistakes when typing.
Using a normalized global address list like GeoPostcodes caters to all these needs, so you do not have to clean data and write ever more complex regular expressions to validate addresses.
GeoPostcodes offers data at three region levels—for example, in the US, you have national, state, and county data. We also offer data in multiple languages for countries with multiple official ones, like Switzerland.
The data is available in normalized and denormalized form, making it easy to import into existing systems. It is consistently updated, cleaned, and standardized to ensure it is ready for use.
Conclusion
Congratulations! You now know how to build a robust address validation in PHP using GeoPostcodes’ accurate street address data, whether it is to reduce the risk of undeliverable or late shipments, improve customer satisfaction, and optimize delivery routes.
GeoPostcodes offers comprehensive and accurate postal and zip code data to ensure the reliability of address validation systems. Its databases provide extensive coverage and up-to-date postal information, making it easier for businesses to maintain accurate customer records and ensure efficient operations.
Use GeoPostcodes to streamline your data validation processes and deliver better service to your customers.
FAQ
What constitutes a complete address for validation purposes?
A complete address includes several key components: a street name, street number, city, state, and zip code.
For effective validation, each of these elements must be present and correctly formatted.
Ensuring a complete address helps prevent errors in deliveries and ensures that communications reach the correct destination without issues.
What is PHP street address validation?
PHP street address validation is a process that verifies the accuracy and completeness of addresses submitted by users.
This ensures that the addresses are real and correctly formatted according to postal standards, helping businesses prevent issues like failed deliveries or sending information to incorrect locations.
How do you validate street addresses using a regular expression in PHP?
To validate street addresses using a regular expression in PHP, you can create patterns that check if the street name and street number follow the expected format.
Regular expressions help ensure that the input matches predefined criteria, such as specific character sequences, before proceeding with further validation steps.
What does “a za z” mean in regular expressions?
In regular expressions, “a za z” refers to a character set that matches any single lowercase or uppercase letter.
The pattern [A-Za-z] specifies that any letter between A-Z, either in uppercase or lowercase, is acceptable. The hyphen (-) in the pattern indicates a range of characters to include.
What role does the USPS API play in street address validation?
The USPS API is an essential tool for validating street addresses in the United States.
It ensures that addresses, including street names and street numbers, conform to USPS formatting guidelines, which helps improve the accuracy of deliveries and reduces the risk of address-related errors.