Distance Compare Class Part B
This post assumes advanced understanding of PHP and some object orientated knowledge.
In my last post, I outlined a basic class that compares addresses by distance. The class returns a sorted, filtered, or single best match from an array of addresses based on a single comparison address. However, this didn't fulfill my client's needs completely. I needed to extend this class to fill his needs, so I made a new class that utilized my basic class.
The first step was making a construct method that extended to gDistanceCompare's construct method. To do this, all I needed to do is put parent:: in front of the class method from gDistanceCompare.
public function __construct()
{
parent::__construct($this->_readCSV('PL_Locator_Website_Confirmations.csv'));
}
The client's information was in a csv file, so I made a method for turning the CSV information into an array that gDistanceCompare needs.
private function _readCSV($url)
{
$handle = fopen($url,'r');
while(($data = fgetcsv($handle,1000,","))!=false)
if($data[1]!='Street Address') $array[] = $data[1].', '.$data[2].', '.$data[3].' '.$data[4];
fclose($handle);
return $array;
}
To read the CSV, you need to loop through the file with a file handler. The columns are returned as an associative array - the address I wanted was spread through several columns, so I concatenated them into a single address that gDistanceCompare could use.
Now I had the address array in the parent class… But I still needed to pass the comparison address. My client wanted the user to enter their zip code and radius in miles in a form. They would then get back a list of stores within that radius, sorted by proximity. If no stores were found within the radius, then the closest one would be returned. I decided to process the form first, validating the user's input, before starting up gDistanceCompare.
public function _process($data)
{
foreach($data as $key => $row)
{
if($key=='zip_code')
{
if(is_numeric($row))
$this->post_array['zip_code'] = $row;
else
$error[] = array('error'=>'The zip code you entered is not valid. Please use a 5-digit code.');
}
if($key=='radius')
{
if(is_numeric($row))
$this->post_array['radius'] = $row;
else
$error[] = array('error'=>'The radius you entered is not valid. Please click one of the values.');
}
}
if($error)
return $error;
else
return true;
}
This is the bare minimum of error checking - but, as no information will be stored on the server, I wasn't too worried about hacking. After checking and saving the form data, I could go ahead and get the addresses.
public function _returnAddresses()
{
$array = parent::_filterAddresses($this->post_array['zip_code'],$this->post_array['radius']);
if(in_array(array('error','there was no addresses that are within the radius'),$array))
$array = array_merge(array('error'=>'best match'),array(parent::_bestAddress($this->post_array['zip_code'])));
else if(in_array(array('error','google can not understand the address'),$array))
$array = array('error'=>'bad zip code');
return $array;
}
This class is fairly straightforward. It filters the array of store addresses by the user's zip code and radius. If none are returned, it looks for the best match, or closest store, and returns a flag for my code to pick up.
I wanted the error messages to be on the front end, which makes both classes I made to be as extension-able as possible. Each class is saved in a separate file, which is included before the class is called. Below is the entire zip_code class.
require_once('gDistanceCompare.php');
class zipcodeDistanceCompare extends gDistanceCompare
{
public $error = array();
private $post_array = array();
public function __construct()
{
parent::__construct($this->_readCSV('PL_Locator_Website_Confirmations.csv'));
}
public function _process($data)
{
/*
checks the two types of post data available
zip code and radius
if numeric -> good, else error
*/
foreach($data as $key => $row)
{
if($key=='zip_code')
{
if(is_numeric($row))
$this->post_array['zip_code'] = $row;
else
$error[] = array('error'=>'The zip code you entered is not valid. Please use a 5-digit code.');
}
if($key=='radius')
{
if(is_numeric($row))
$this->post_array['radius'] = $row;
else
$error[] = array('error'=>'The radius you entered is not valid. Please click one of the values.');
}
}
if($error)
return $error;
else
return true;
}
public function _returnAddresses()
{
/*
returns addresses that match
if no addresses, then returns error and closest match
*/
$array = parent::_filterAddresses($this->post_array['zip_code'],$this->post_array['radius']);
if(in_array(array('error','there was no addresses that are within the radius'),$array))
$array = array_merge(array('error'=>'best match'),array(parent::_bestAddress($this->post_array['zip_code'])));
else if(in_array(array('error','google can not understand the address'),$array))
$array = array('error'=>'bad zip code');
return $array;
}
private function _readCSV($url)
{
/*
reads the csv, turns into array
*/
$handle = fopen($url,'r');
while(($data = fgetcsv($handle,1000,","))!==false)
if($data[1]!='Street Address') $array[] = $data[1].', '.$data[2].', '.$data[3].' '.$data[4];
fclose($handle);
return $array;
}
}
These classes are not incredible speedy - I am making a separate request from Google with each store address. There may be ways of speeding it up (caching, etc), but I wanted to avoid client-server write interactions with this application. Overall, it was a simple and easy introduction to PHP classes, and I look forward to creating more classes in the future!
Comments (0)