PHP Street Address Validation

GeoPostcodes-PHP address validation
Table of Contents

Table of Contents

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:

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.

php street address validation

On the Download Center page, under Samples, select Street database.

php street address validation

On the Street database page, under Packages, choose North America, sample countries.

php street address validation

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.

php street address validation

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:

php street address validation

Now, provide the correct zip code (“60633”). The form will display a success message:

php street address validation

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.

Related posts