Quantcast
Channel: HTML Campus » PHP
Viewing all articles
Browse latest Browse all 6

Build a zip code store locator application in PHP

$
0
0

store

There are times when you’ll want to purchase an item at a store but you’re not sure of the store’s exact location. Typically you’ll get online go to their website and visit the ‘Store Locations’ section. There they prompt for your zip code and they’re able to triangulate your coordinates and list all their closest locations within a given range of miles. This is real convenience if you run a store because you’re customers can save having to call and ask for locations nearby.

Services like Google Maps API, and Yahoo API, and others can provide you with this type of service for your site but they come with restrictions like limits on the number of daily queries, or even require purchase in the case of some pre-built applications. Here I’m going to teach you how to build your own version that is free and will allow your users to find nearby stores given their zip code and distance limit.

Here is an overview of how the application will work. We’ll create a form with two fields, zip code and radius. Look up the latitude & longitude coordinates for the given zip code and each store in the database. Then we’ll use a supplied algorithm to figure out the geological distance between the given zip code and each store’s coordinates. If the distance is within the given radius, set the store aside and continue with the remaining stores. In the end we’re left with a list of nearby stores which we’ll display to the customer in HTML. It may sound overwhelming, but it’s really easier than it sounds. Let’s get started!

Step 1: Create and populate the stores & zip code MySQL tables

Create stores tables

CREATE TABLE `stores` (
  `store_id` INT(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  `address` VARCHAR(50) NOT NULL,
  `town` VARCHAR(25) NOT NULL,
  `state` VARCHAR(50) NOT NULL,
  `postal` VARCHAR(5) NOT NULL,
  `phone` VARCHAR(20) DEFAULT NULL,
  `hours` VARCHAR(100) DEFAULT NULL,
  PRIMARY KEY  (`store_id`),
  KEY `postal` (`postal`)
);

Create zip code table

CREATE TABLE `zip_codes` (
  `zip_id` INT(11) NOT NULL AUTO_INCREMENT,
  `zip` VARCHAR(5) NOT NULL DEFAULT '',
  `abbr_state` char(2) NOT NULL DEFAULT '',
  `lat` VARCHAR(10) NOT NULL DEFAULT '',
  `lon` VARCHAR(10) NOT NULL DEFAULT '',
  `city` VARCHAR(50) DEFAULT NULL,
  `full_state` VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY  (`zip_id`),
  UNIQUE KEY `zip` (`zip`)
);

Now we populate the two tables. I have provided SQL files with sample store data and the list of zip codes. Of course you can use your own store data, however you will need the zip codes. Just load and run the SQL files and they’ll create and save the needed tables and their data. The data for the US zip codes is from the 2000 US Census, and is provided free of charge by The Zip Code Database Project Team.

***Please be advised that this data is almost 10 years old. It is probably missing new zip codes, some of the cities and towns may have moved around a bit, and other changes may have taken place since then. You can shop around for an updated zip code list if you like, but I have been using this one for a while and it works fine for my uses.

Step 2: Create the HTML form

The HTML form basically consists of the zip code text field and the radius drop down. The radius is the limit of how many miles from the zip code to search.

<form action="" method="post">
  <p>Enter your zip code below to find locations near you.</p>
  <div>
    <label>Zip:</label>
    <input name="zip" type="text" size="10" maxlength="5" />
  </div>
  <div>
    <label>Search Area:</label>
    <select name="radius" id="radius">
      <option value="5">5 mi.</option>
      <option value="10">10 mi.</option>
      <option value="15">15 mi.</option>
      <option value="20">20 mi.</option>
    </select>
  </div>
  <div>
    <input type="hidden" name="submitted" value="submitted" />
    <input type="submit" value="Submit" />
  </div>
</form>

Step 3: Validate the submitted infomation

We check if the form is submitted, if it is we validate the submitted values. If the values are valid we continue and retrieve the coordinates for the stores.

if (isset ($_POST['submitted'])) {

  if (!empty ($_POST['zip']) && is_numeric ($_POST['zip'])) {

    $z = (int)$_POST['zip'];

    $query = "SELECT lat, lon FROM zip_codes WHERE zip = '$z'";
    $result = mysql_query ($query);

    if (mysql_num_rows ($result) == 1) {
      $zip = mysql_fetch_assoc ($result);
    } else {
      $Errors = '<p>The zip code you entered was not found!</p>';
      $z = NULL;
    }

  }

  if (isset ($_POST['radius']) && is_numeric ($_POST['radius'])) {
    $r = (int)$_POST['radius'];
  }

  if ($r && $z) {
    // Retrieve coordinates of the stores
  } else {
    $Errors = ($Errors) ? $Errors : '<p>Errors were found please try again!</p>';
  }

}

Step 4: Retrieve coordinates of the stores

Here we run a query to pull all the stores in the database and their coordinates. We then loop through the results in order to separate the ones within the range.

$query = "SELECT name, address, town, state, postal, phone, hours, lat, lon
FROM stores
INNER JOIN zip_codes
ON stores.postal = zip_codes.zip";

$result = mysql_query ($query);
while ($row = mysql_fetch_assoc ($result)) {
  // Separate closest stores
}

Step 5: Separate closest stores

Now we feed the coordinates of the given zip code and store into the distance function. The output is the distance between the two. If the the distance is less than or equal to the given radius we then save the store’s information in an array for later use.

$distance = Dist ($row['lat'], $row['lon'], $zip['lat'], $zip['lon']);

if ($distance <= $r) {

  $stores[] = array (
    'name'      => $row['name'],
    'address'   => $row['address'],
    'state'     => $row['state'],
    'town'      => $row['town'],
    'postal'	=> $row['postal'],
    'phone'     => $row['phone'],
    'hours'     => $row['hours']
  );

}

This is the function used above to calculate the distance between two sets of latitude and longitude coordinates. This function is also provided by The Zip Code Database Project Team.

function Dist($lat_A, $long_A, $lat_B, $long_B) {

  $distance = sin(deg2rad($lat_A))
      * sin(deg2rad($lat_B))
      + cos(deg2rad($lat_A))
      * cos(deg2rad($lat_B))
      * cos(deg2rad($long_A - $long_B));

  $distance = (rad2deg(acos($distance))) * 69.09;
  return $distance;

}

Step 6: Display the closest stores information in HTML

Underneath the HTML form we place this block of code. This checks the array of saved stores and sees if there were any stores saved. If there were, we list their information along with a link to its location using Google Maps, otherwise if there weren’t we display a ‘No results found’ message.

<?php

if (isset ($stores)) {

  if (!empty ($stores)) {

    echo '<p><strong>' . count ($stores) . ' results were found.</strong></p>';
    foreach ($stores as $value) {

      echo '<p><strong>' . $value['name'] . '</strong><br />';
      echo $value['address'] . '<br />';
      echo $value['town'] . ', ' . $value['state'] . ' ' . $value['postal'];
      echo '&nbsp;<a target="_blank" href="http://maps.google.com/maps?q=',
      $value['address'], ' ',
      $value['postal'], ', ',
      $value['state'], ' ',
      $value['postal'],
      '">Map this location</a><br />';
      echo 'Phone: ' . $value['phone'] . '<br />';
      echo 'Hours: ' . $value['hours'];
      echo '</p>';

    }

  } else {
    echo '<p><strong>No results found</strong></p>';
  }

}

?>

Final result

Here is the final product. I have added comments, database connection, basic HTML, and declared at the top the variables that are used in the script.

<?php

// Create page variables
$r = NULL;
$z = NULL;
$stores = NULL;
$Errors = NULL;

// Establish DB connection
$dbc = mysql_connect ('localhost', 'username', 'password');
mysql_select_db ('store_locator', $dbc);

// Declare page functions
function Dist ($lat_A, $long_A, $lat_B, $long_B) {

  $distance = sin(deg2rad($lat_A))
      * sin(deg2rad($lat_B))
      + cos(deg2rad($lat_A))
      * cos(deg2rad($lat_B))
      * cos(deg2rad($long_A - $long_B));

  $distance = (rad2deg(acos($distance))) * 69.09;
  return $distance;

}

### Handle form if submitted
if (isset ($_POST['submitted'])) {

  // Validate Zip code field
  if (!empty ($_POST['zip']) && is_numeric ($_POST['zip'])) {

    $z = (int)$_POST['zip'];

    // Verify zip code exists
    $query = "SELECT lat, lon FROM zip_codes WHERE zip = '$z'";
    $result = mysql_query ($query);

    if (mysql_num_rows ($result) == 1) {
      $zip = mysql_fetch_assoc ($result);
    } else {
      $Errors = '<p>The zip code you entered was not found!</p>';
      $z = NULL;
    }

  }

  // Validate radius field
  if (isset ($_POST['radius']) && is_numeric ($_POST['radius'])) {
    $r = (int)$_POST['radius'];
  }

  // Proceed if no errors were found
  if ($r && $z) {

    // Retrieve coordinates of the stores
    $stores = array();
    $query = "SELECT name, address, town, state, postal, phone, hours, lat, lon
    FROM stores
    INNER JOIN zip_codes
    ON stores.postal = zip_codes.zip";
    $result = mysql_query ($query);

    // Go through and check all stores
    while ($row = mysql_fetch_assoc ($result)) {

      // Separate closest stores
      $distance = Dist ($row['lat'], $row['lon'], $zip['lat'], $zip['lon']);

      // Check if store is in radius
      if ($distance <= $r) {

        $stores[] = array (
          'name'      => $row['name'],
          'address'   => $row['address'],
          'state'     => $row['state'],
          'town'      => $row['town'],
          'postal'    => $row['postal'],
          'phone'     => $row['phone'],
          'hours'     => $row['hours']
        );

      }

    }

  } else {
    $Errors = ($Errors) ? $Errors : '<p>Errors were found please try again!</p>';
  }

}

?><html>
<head>
<title>Store Locator</title>
</head>
<body>
<form action="" method="post">
  <p>Enter your zip code below to find locations near you.</p>
  <?php echo ($Errors) ? $Errors : ''; ?>
  <div>
    <label>Zip:</label>
    <input name="zip" type="text" size="10" maxlength="5" />
  </div>
  <div>
    <label>Search Area:</label>
    <select name="radius" id="radius">
      <option value="5">5 mi.</option>
      <option value="10">10 mi.</option>
      <option value="15">15 mi.</option>
      <option value="20">20 mi.</option>
    </select>
  </div>
  <div>
    <input type="hidden" name="submitted" value="submitted" />
    <input type="submit" value="Submit" />
  </div>
</form>

<?php

if (isset ($stores)) {

  if (!empty ($stores)) {

    echo '<p><strong>' . count ($stores) . ' results were found.</strong></p>';
    foreach ($stores as $value) {

      echo '<p><strong>' . $value['name'] . '</strong><br />';
      echo $value['address'] . '<br />';
      echo $value['town'] . ', ' . $value['state'] . ' ' . $value['postal'];
      echo '&nbsp;<a target="_blank" href="http://maps.google.com/maps?q=',
      $value['address'], ' ',
      $value['town'], ', ',
      $value['state'], ' ',
      $value['postal'],
      '">Map this location</a><br />';
      echo 'Phone: ' . $value['phone'] . '<br />';
      echo 'Hours: ' . $value['hours'];
      echo '</p>';

    }

  } else {
    echo '<p><strong>No results found</strong></p>';
  }

}

?>
</body>
</html>

You may see the working demo here. I have provided the source code and all the SQL files, you may download them here. You’ll need to replace the database connection values with your own.


Viewing all articles
Browse latest Browse all 6

Trending Articles