Skip to content

Instantly share code, notes, and snippets.

@dljoseph
Created November 22, 2014 11:52
Show Gist options
  • Save dljoseph/ee3712eda3fb5c4b6820 to your computer and use it in GitHub Desktop.
Save dljoseph/ee3712eda3fb5c4b6820 to your computer and use it in GitHub Desktop.
SilverStripe 3.1.x Store locator snippet - grabs lat/lng from Google
<?php
class Store extends DataObject {
static $db = array(
'Title' => "Varchar(128)",
'Name' => "Varchar(128)",
'Address1' => "Varchar(128)",
'Address2' => "Varchar(128)",
'Address3' => "Varchar(128)",
'PostCode' => "Varchar(10)",
"Phone" => "Varchar(16)",
"Lat" => "Float(10,9)",
"Lng" => "Float(10,9)"
);
private static $summary_fields = array(
'Title',
'Name',
'Address1',
'Address2',
'Address3',
'PostCode',
'Lat',
'Lng'
);
public function getCMSFields()
{
$fields = parent::getCMSFields();
return $fields;
}
public function onBeforeWrite() {
if (!$this->ID || $this->isChanged()) {
//get the lat and long values for the address
//$address = $this->Title ? $this->Title .',' : '';
$address = $this->Address1 ? $this->Address1 .',' : '';
$address .= $this->Address2 ? $this->Address2 .',' : '';
$address .= $this->Address3 ? $this->Address3 .',' : '';
$address .= $this->PostCode;
$address = urlencode($address);
$region = 'GB';
$json = file_get_contents("http://maps.google.com/maps/api/geocode/json?address=$address&sensor=false&region=$region");
$lat = $json->{'results'}[0]->{'geometry'}->{'location'}->{'lat'};
$lng = $json->{'results'}[0]->{'geometry'}->{'location'}->{'lng'};
if ($lat) {
$this->Lat = $lat;
}
if ($lng) {
$this->Lng = $lng;
}
}
parent::onBeforeWrite();
}
}
class StoresService implements WebServiceable {
public function __construct() {
}
public function webEnabledMethods() {
return array(
'getStores' => 'GET',
);
}
public function getStores($lat = null, $lng = null) {
/*
* To find locations in your markers table that are within a certain radius distance of a given latitude/longitude,
* you can use a SELECT statement based on the Haversine formula. The Haversine formula is used generally for
* computing great-circle distances between two pairs of coordinates on a sphere.
*
* Here's the SQL statement that will find the closest 20 locations that are within a radius of 25 miles to
* the 37, -122 coordinate. It calculates the distance based on the latitude/longitude of that row and the target
* latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query
* by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.
*
* SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance
* FROM tablename
* HAVING distance < 25
* ORDER BY distance
* LIMIT 0 , 20;
* */
if (is_numeric($lat) && is_numeric($lng)) {
$sqlQuery = new SQLQuery();
$sqlQuery->setFrom('Store');
$sqlQuery->selectField('( 3959 * acos( cos( radians('.$lat.') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('.$lng.') ) + sin( radians('.$lat.') ) * sin( radians( lat ) ) ) )','distance');
//$sqlQuery->selectField('concat(Title, " ", Name)','storename'); //valid concat statement, but SS doesn't like it!
$sqlQuery->addHaving('distance < 25');
$sqlQuery->setOrderBy('distance');
$sqlQuery->setLimit(20);
$result = $sqlQuery->execute();
$returnedRecords = new ArrayList();
foreach($result as $row) {
$row['Name'] = $row['Title']. ' ' . $row['Name'];
unset($row['Title']);
$Address = '';
$Address.= $row['Address1'] ? $row['Address1'] .', ' : '';
$Address.= $row['Address2'] ? $row['Address2'] .', ' : '';
$Address.= $row['Address3'] ? $row['Address3'].', ' : '';
$Address.= $row['PostCode'] ? $row['PostCode'] : '';
unset($row['Address1']);
unset($row['Address2']);
unset($row['Address3']);
//insert address line at offset 5 in associative array
$offset = 5;
$newArray = array_slice($row, 0, $offset, true) +
array('Address' => "$Address") +
array_slice($row, $offset, NULL, true);
$returnedRecords->push(new ArrayData($newArray));
}
return array(
'Stores' => $returnedRecords,
);
}
return null;
}
}
class StoreAdmin extends ModelAdmin {
public static $managed_models = array(
'Store'
);
static $url_segment = 'store'; // will be linked as /admin/store
static $menu_title = 'Store Locations';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment