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.

  1. public function __construct()

  2. {

  3. parent::__construct($this->_readCSV('PL_Locator_Website_Confirmations.csv'));

  4. }

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.

  1. private function _readCSV($url)

  2. {

  3. $handle = fopen($url,'r');

  4. while(($data = fgetcsv($handle,1000,","))!=false)

  5. if($data[1]!='Street Address') $array[] = $data[1].', '.$data[2].', '.$data[3].' '.$data[4];

  6. fclose($handle);

  7. return $array;

  8. }

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.

  1. public function _process($data)

  2. {

  3. foreach($data as $key => $row)

  4. {

  5. if($key=='zip_code')

  6. {

  7. if(is_numeric($row))

  8. $this->post_array['zip_code'] = $row;

  9. else

  10. $error[] = array('error'=>'The zip code you entered is not valid. Please use a 5-digit code.');

  11. }

  12. if($key=='radius')

  13. {

  14. if(is_numeric($row))

  15. $this->post_array['radius'] = $row;

  16. else

  17. $error[] = array('error'=>'The radius you entered is not valid. Please click one of the values.');

  18. }

  19. }

  20. if($error)

  21. return $error;

  22. else

  23. return true;

  24. }

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.

  1. public function _returnAddresses()

  2. {

  3. $array = parent::_filterAddresses($this->post_array['zip_code'],$this->post_array['radius']);

  4. if(in_array(array('error','there was no addresses that are within the radius'),$array))

  5. $array = array_merge(array('error'=>'best match'),array(parent::_bestAddress($this->post_array['zip_code'])));

  6. else if(in_array(array('error','google can not understand the address'),$array))

  7. $array = array('error'=>'bad zip code');

  8. return $array;

  9. }

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.

  1. require_once('gDistanceCompare.php');

  2. class zipcodeDistanceCompare extends gDistanceCompare

  3. {

  4. public $error = array();

  5. private $post_array = array();

  6. public function __construct()

  7. {

  8. parent::__construct($this->_readCSV('PL_Locator_Website_Confirmations.csv'));

  9. }

  10. public function _process($data)

  11. {

  12. /*

  13. checks the two types of post data available

  14. zip code and radius

  15. if numeric -> good, else error

  16. */

  17. foreach($data as $key => $row)

  18. {

  19. if($key=='zip_code')

  20. {

  21. if(is_numeric($row))

  22. $this->post_array['zip_code'] = $row;

  23. else

  24. $error[] = array('error'=>'The zip code you entered is not valid. Please use a 5-digit code.');

  25. }

  26. if($key=='radius')

  27. {

  28. if(is_numeric($row))

  29. $this->post_array['radius'] = $row;

  30. else

  31. $error[] = array('error'=>'The radius you entered is not valid. Please click one of the values.');

  32. }

  33. }

  34. if($error)

  35. return $error;

  36. else

  37. return true;

  38. }

  39. public function _returnAddresses()

  40. {

  41. /*

  42. returns addresses that match

  43. if no addresses, then returns error and closest match

  44. */

  45. $array = parent::_filterAddresses($this->post_array['zip_code'],$this->post_array['radius']);

  46. if(in_array(array('error','there was no addresses that are within the radius'),$array))

  47. $array = array_merge(array('error'=>'best match'),array(parent::_bestAddress($this->post_array['zip_code'])));

  48. else if(in_array(array('error','google can not understand the address'),$array))

  49. $array = array('error'=>'bad zip code');

  50. return $array;

  51. }

  52. private function _readCSV($url)

  53. {

  54. /*

  55. reads the csv, turns into array

  56. */

  57. $handle = fopen($url,'r');

  58. while(($data = fgetcsv($handle,1000,","))!==false)

  59. if($data[1]!='Street Address') $array[] = $data[1].', '.$data[2].', '.$data[3].' '.$data[4];

  60. fclose($handle);

  61. return $array;

  62. }

  63. }

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!