Since the introduction of Google Maps and other location services, a broad set of possibilities are open to web applications, allowing geographical information to be used for building services.
This recipe shows how to use the Geocode plugin to add location information to our own Address
model, allowing us to search address records by proximity.
The Geocode
plugin is another open source project I released. More information about it can be obtained at http://github.com/mariano/geocode.
To go through this recipe we need a sample table to work with. Create a table named addresses
, using the following SQL statement:
CREATE TABLE `addresses`( `id` INT UNSIGNED AUTO_INCREMENT NOT NULL, `address_1` VARCHAR(255) NOT NULL, `city` VARCHAR(255) default NULL, `state` VARCHAR(255) NOT NULL, `zip` VARCHAR(10) default NULL, `latitude` FLOAT(10,7) NOT NULL, `longitude` FLOAT(10,7) NOT NULL, PRIMARY KEY(`id`) );
We proceed now to create the required model. Create the model Address
in a file named address.php
and place it in your app/models
folder with the following contents (we are only specifying a few states for readability):
<?php class Address extends AppModel { public $validate = array( 'address_1' => array('rule' => 'notEmpty'), 'state' => array('rule' => 'notEmpty') ); public static $states = array( 'CA' => 'California', 'FL' => 'Florida', 'NY' => 'New York' ); } ?>
Create its appropriate controller AddressesController
in a file named addresses_controller.php
and place it in your app/controllers
folder. With the following contents:
<?php class AddressesController extends AppController { public function add() { if (!empty($this->data)) { $this->Address->create(); if ($this->Address->save($this->data)) { $this->Session->setFlash('Address created'), $this->redirect('/'), } else { $this->Session->setFlash('Please correct the errors'), } } $states = $this->Address->states; $this->set(compact('states')); } } ?>
Create a folder named addresses
in your app/views
folder, then create the view to hold the form in a file named add.ctp
and place it in your app/views/addresses
folder, with the following contents:
<?php echo $this->Form->create(); echo $this->Form->inputs(array( 'address_1' => array('label' => 'Address'), 'city', 'state' => array('options'=>$states), 'zip' )); echo $this->Form->end('Create'), ?>
We need to download the Geocode plugin for CakePHP. Go to http://github.com/mariano/geocode/downloads and download the latest release. Uncompress the downloaded file into your app/plugins
folder. You should now have a directory named geocode
inside app/plugins
.
Finally, we need to sign up for a Google Maps API key. To do so, go to http://code.google.com/apis/maps/signup.html and follow the instructions given.
app/config/bootstrap.php
file and place the following statement right before the closing PHP statement, replacing the string APIKEY
with your own Google Maps API key:Configure::write('Geocode.key', 'APIKEY'),
Address
model extend the skeleton model provided by the plugin. Edit your app/models/address.php
file and make the following changes:<?php
App::import('Model', 'Geocode.GeoAddress'),
class Address extends GeoAddress {
public $validate = array(
'address_1' => array('rule' => 'notEmpty'),
'state' => array('rule' => 'notEmpty')
);
public static $states = array(
'CA' => 'California',
'FL' => 'Florida',
'NY' => 'New York'
);
}
?>
GeoAddress
, the Geocodable
behavior is automatically attached to our model. We can now use the form at http://localhost/addresses/add
to add new addresses. After adding quite a few, we are ready to implement a paginated listing with support to finding addresses that are near a certain location. AddressesController
class:public function index() { $address = '1211 La Brad Lane, Tampa, FL'; $this->paginate = array( 'near', 'address' => $address ); $addresses = $this->paginate(); $this->set(compact('address', 'addresses')); }
app/views/addresses/index.ctp
, with the following contents:<h1>Addresses near <strong><?php echo $address; ?></strong></h1> <div class="paging"> <?php echo $this->Paginator->prev(); ?> <?php echo $this->Paginator->numbers(); ?> <?php echo $this->Paginator->next(); ?> </div> <br /> <ul> <?php foreach($addresses as $currentAddress) { ?> <li> <?php echo $currentAddress['Address']['address_1']; ?> at <strong><?php echo number_format($currentAddress['Address']['distance'], 2) . ' km.'; ?></strong> </li> <?php } ?> </ul>
If you inserted sample addresses that are near the specified address, the output could be similar to that shown in the following screenshot:
We started by downloading the plugin and configuring it by setting our own Google Maps API key in the bootstrap.php
configuration file. We then made our Address
model inherit from the GeoAddress
model provided by the plugin, which makes our model use the Geocodable
behavior, and implements the near
custom find type.
Since our Address
model is now attached to the Geocodable
behavior, every time we create new address records the plugin will use the Google Maps API to save the appropriate location in the latitude
and longitude
fields.
Using the near
custom find type, we can easily find addresses that are near a certain address, and we can also see what distance separates each of those addresses from the point of origin.
The Geocode plugin is quite flexible, and even includes a helper to show addresses in a visual map. To find out all it has to offer, go to its website at http://github.com/mariano/geocode.
18.189.178.237