Introduction
We have all created forms for our web site visitors to fill out. Some are very simple and others are very complex. There are some that requires our web site visitors to fill in their address, keeping not only their street name and number but their city and state as well. But that is where the problem comes in. Generally, when we are asking for their address, we want to be able to do something with it. To make our lives easier, the better organized and standardize the data are, the easier it is to use them. Although we can control what the user inputs for the state, we can't for the city. People can enter many different things for the city name and some may even enter their town name instead of the city name. Also, some people will spell the city name one way and someone else will spell it another way.
A better solution is to just ask them for the zipcode. You set how the city name is spelled so it becomes standardize. A drawback of this of course is that you need to have a record of every city with their corresponding zipcode. Such databases are generally not free and quite large. However, that shouldn't stop you from trying and fortunately for you, I happen to have one that is free and mostly updated.
Obviously it would be stupid to create such a large database and only use it for getting the corresponding city name. You should be able to use it to do many different things. One thing that comes to mine is using it to retrieve a list of all the cities within a given radius.
So, to begin, we will start by giving this class a name. I'll call it DLZipcode. Now, note that for the sake to simplifying things, I will be detailing the instruction of writing this class by making it just a regular class. My original DLZipcode is actually a static classs but that require some extra stuff which isn't needed for this article. I will also be using another class that I wrote (it is also static). That class is called DLCommon and it basically just contains some functions that I used commonly. I'll explain more about them when we get to it.
Setup
The first thing we need to do is to create a MySQL table with the list of zipcodes. I have attached a CSV file for such list below. It is a very simple list containing the following columns: zipcode, latitude, longitude, city, and state. In total, there are 32,102 zipcodes in this CSV file. I do want to note though, when you are creating the fields for the table, set the zipcode to INT, give it a length of 5 with auto fill zeros check, then import the list, and change the type for the zipcode field to VARCHAR with a length of 5 as well. It is some extra work but it will make our lives easier when we are writing the methods that will be using this data. Anyway, you can download the CSV file by clicking here (you might need to right click and choose Save As).
Once the data has been imported to the database, check the data to make sure there are no defects. If everything is good, then we can begin to implement the class.
Class Construction
We will begin by looking at any data members that should come with this class. Right away, we know that we will need to have a variable that handles our database connection and query. I will be using the more secured mysqli object instead of the old mysql functions. With that, I'll just call that variable $mysqli.
I have the tendency to change up the names of the tables in the database from time to time if I feel it deserves a better name. For that reason, I am going to add in another variable called $table so that my SQL queries will use that instead of hard-coding. It is also good practice to pull out data that will be used in many different places and put them in a variable instead of hard-coding it. Remember, the whole point of object oriented programming is to abstract the code so that you can change it in only one place and it will update it through the code.
In order to create a mysqli object, I need to have the host, username, password, and database for which the data is stored. So those four things will be needed as the parameters for the construction of the object. However, they do not need to be stored within the class since they will be stored my the mysqli object. So lets begin with the start of our code:
<?php
// Load the necessary classes
loadClass('DLCommon');
class DLZipcode {
/**
* The mysqli object used for interaction with the database.
*
* @var mysqli
*/
private $mysqli;
/**
* The table in the database where the zipcodes are stroed.
*
* @var string
*/
private $table = 'zipcode';
/**
* Create a new DLZipcode object.
*
* @param string $host The host to connect to the database.
* @param string $user The username to connect to the database.
* @param string $pass The password to connect to the database
* @param string $db The database.
* @return DLZipcode
*/
public function __construct($host, $user, $pass, $db) {
// Create the mysqli object
DLCommon::mysqli($this->mysqli, $host, $user, $pass, $db);
}
}
?>
The function loadClass is another function that I wrote. It basically loads the given class name along with any other files needed by that class. The method DLCommon::mysqli simply takes an object along with the MySQL information and create a new mysqli object. Since it is set up to return by reference, I can just send it $this->mysqli instead of setting it manually. Now we have the skeleton for our class. It is time to write some methods for it.
The Methods
We will start with some easy ones. Usually, these easy ones end up being used on the more complex methods so it is a good idea to get it done first. The first method that comes to my mind is that I need to have a method that validate the zipcode to make sure it is of the correct format: numeric and is 5-digit long.
<?php
...
/**
* Determine whether or not the given input is a valid zipcode.
*
* @param string $zipcode The string to validate.
* @return boolean
*/
public function validZipcode($zipcode) {
$result = is_numeric($zipcode);
$result &= isset($zipcode[4]);
return $result;
}
...
?>
This method accepts a string and use the is_numeric() function to make sure that it is numeric. Then it checks to make sure it has 5 digit in it. Note that I could have used the function strlen() to get the length but it is faster to use the code above (see my article on how to optimize your PHP code). Now that we can find out whether a given input is a valid zipcode, we need to be able to retrieve the zipcode from the database.
<?php
...
/**
* Retrieve the given zipcode information. The retrned value is an
* associative array with the following keys: zipcode, latitude, longitude,
* city, and state.
*
* @param string $zipcode The zipcode to retrieve the information for.
* @return array|NULL
*/
public function getZipcode($zipcode) {
// First, make sure the zipcode is valid
if(!$this->validZipcode($zipcode)) {
// Try to fetch zipcode from database
$sql = 'SELECT * FROM %s WHERE `zipcode`="%s" LIMIT 1';
$result = DLCommon::query($this->mysqli, $sql, $this->table, $zipcode);
$result = $result->fetch_assoc();
if(is_array($result)) { return $result; }
}
// If we gotten here, then the zipcode was not found on the database
return NULL;
}
...
?>
Note that the method DLCommon::query is just a wrapper for the real mysqli_query() function. It just replaces all the %s with their correct value as well as adding slashes in the right place so that the SQL is safe and is not prone to SQL injections. Having the limit there is optional. Anyway, after the query is made, we retrieve the first row of result by calling the function mysqli_fetch_assoc() (note that I am calling these methods in the object oriented style rather than the procedural style). If it found the zipcode information, it will return an associative array with the column names as the key with their respective values. If it did not find anything, it will just return NULL and NULL is considered FALSE. Next, we want to have a method that checks to see whether or not a zipcode actually exist. This is easily coded since we have the method getZipcode.
<?php
...
/**
* Determine whether or not the zipcode exist in the database.
* @param string $zipcode The zipcode to validate.
* @return boolean
*/
public function zipcodeExist($zipcode) {
// Fetch the zipcode from the database, if it exist, then we should get
// an array back, otherwise, it is false
if($this->getZipcode($zipcode)) { return TRUE; }
else { return FALSE; }
}
...
?>
All that we needed to do was call the getZipcode method. If it returns NULL, the if statement will evaluate to FALSE so we just return FALSE, otherwise, we return TRUE. In most cases, this is all that you need. It is a very simple application that allows you to check and retrieve information about any zipcode that it has in its database.
Conclusion
Our class consist of only three methods and is a very simple class that is powered by a huge repository of information from our database. But it should not stop here. It can be further upgraded to allow more functionailty such as being able to find a list of all the zipcodes within a given radius. Having some kind of user interface for administrative purposes would also be nice. However, most importantly, it can be rewritten to better efficiency. Part 2 of this article will cover these issues and more. You can read part 2 by clicking here.
Source Code
Below is the complete source code for DLZipcode.
<?php
// Load the necessary classes
loadClass('DLCommon');
class DLZipcode {
/**
* The mysqli object used for interaction with the database.
*
* @var mysqli
*/
private $mysqli;
/**
* The table in the database where the zipcodes are stroed.
*
* @var string
*/
private $table = 'zipcode';
/**
* Create a new DLZipcode object.
*
* @param string $host The host to connect to the database.
* @param string $user The username to connect to the database.
* @param string $pass The password to connect to the database
* @param string $db The database.
* @return DLZipcode
*/
public function __construct($host, $user, $pass, $db) {
// Create the mysqli object
DLCommon::mysqli($this->mysqli, $host, $user, $pass, $db);
}
/**
* Retrieve the given zipcode information. The retrned value is an
* associative array with the following keys: zipcode, latitude, longitude,
* city, and state.
*
* @param string $zipcode The zipcode to retrieve the information for.
* @return array|NULL
*/
public function getZipcode($zipcode) {
// First, make sure the zipcode is valid
if(!$this->validZipcode($zipcode)) {
// Try to fetch zipcode from database
$sql = 'SELECT * FROM %s WHERE `zipcode`="%s" LIMIT 1';
$result = DLCommon::query($this->mysqli, $sql, $this->table, $zipcode);
$result = $result->fetch_assoc();
if(is_array($result)) { return $result; }
}
// If we gotten here, then the zipcode was not found on the database
return NULL;
}
/**
* Determine whether or not the given input is a valid zipcode.
*
* @param string $zipcode The string to validate.
* @return boolean
*/
public function validZipcode($zipcode) {
$result = is_numeric($zipcode);
$result &= isset($zipcode[4]);
return $result;
}
/**
* Determine whether or not the zipcode exist in the database.
* @param string $zipcode The zipcode to validate.
* @return boolean
*/
public function zipcodeExist($zipcode) {
// Fetch the zipcode from the database, if it exist, then we should get
// an array back, otherwise, it is false
if($this->getZipcode($zipcode)) { return TRUE; }
else { return FALSE; }
}
}
?>