Nullyのぶろぐ

仙台から東京へ転勤したエンジニアのブログ

GoogleMapsViewHelperっぽいのを書いてみた

前日のポストで造ってみよう的なやつをさくっと書いてみました。

バグあったらすみません。。。

以下ソース

ViewHelperっぽいの

[CC lang="php"]

<?php

require_once("Zend/Exception.php") ;

require_once("Zend/Db.php") ;

require_once("Zend/Registry.php") ;

require_once("Zend/Http/Client.php");

require_once("Zend/View/Helper/Abstract.php") ;

require_once("Nully/View/Helper/Google/Maps/MapModel.php") ;

class Nully_View_Helper_Google_Maps_Map extends Zend_View_Helper_Abstract {

/**

* google api url for map.

**/

const API_URL = "http://www.google.com/jsapi" ;

/**

* geo coding url

* @var    string

**/

const GEOCODE_URI = "http://maps.google.com/maps/geo" ;

/**

* google map API KEY

* @var    string

*/

protected $_api_key ;

/**

* display target block elements id

* @var    string

**/

protected $_id = "gmap" ;

/**

* google maps cache dir

* @var    string

**/

protected $_cache_dir ;

/**

* cache name

**/

protected $_cache_name = "cache.dat" ;

/**

* cache life time

* @var    int

**/

protected $_cache_life_time = 21600 ; // 60 * 60 * 6 ( 1/4 hour )

/**

* constructor

* set the Zend_Config registerd google maps API KEY.

* @access public

**/

public function __construct() {

if(Zend_Registry::isRegistered("google_config")) {

$gConfig = Zend_Registry::get("google_config") ;

if(isset($gConfig->map->apikey)) {

$this->_api_key = $gConfig->map->apikey ;

}

}

$database = array() ;

if(Zend_Registry::isRegistered("database")) {

$database = Zend_Registry::get("database");

}

$this->_cache_dir = dirname(__FILE__). "/cache/" ;

try {

$db = Zend_Db::factory($database) ;

$db->getConnection()->exec("SET NAMES utf8;") ;

$this->_geo = new Nully_View_Helper_Google_Maps_MapModel($db) ;

}

catch(Exception $e) {

throw new Exception("Database connection faild.") ;

}

}

/**

* display google maps script tag.

* @access  public

* @param   str|array  registerd address data.

* @param   array      parameter is so many...

* @return  str|bool   generated google maps script tag, or false.

**/

public function map($addresses, $params = array()) {

if(empty($addresses)) {

return false ;

}

$this->_setParams($params) ;

if($this->_hasCache()) {

$data = $this->_loadCache() ;

}

else {

$data = $this->_getGeos($addresses) ;

$this->_saveCache($data) ;

}

$param = $this->_getScriptParams() ;

echo $this->_displayScript($data, $param) ;

}

/**

* set parametars

* @access priavate

**/

private function _setParams($param) {

foreach($param as $func => $val) {

$func = "_set".$func ;

if(method_exists(&$this, $func)) {

$this->{$func}($val) ;

}

}

}

/**

* display javascript tag.

* @access  private

* @param   array   data array.

* @param   array   data parameter array.

**/

private function _displayScript($data, $param) {

$json = $this->_toJson($data) ;

$jsapi = $this->_getApiUrl() ;

$script = <<< SCRIPT_END

<script type="text/javascript" src="{$jsapi}"></script>

<script type="text/javascript">

google.load("maps", 2, 'initialize') ;

var addr = {$json} ;

function initialize() {

var map = new GMap2(document.getElementById("{$param['id']}")) ;

map.setCenter(new GLatLng(addr[0]["lat"], addr[0]["lon"]), 10) ;

for(var i = 0; i < addr.length; i ++) {

var point = new GLatLng(addr[i]["lat"], addr[i]["lon"]);

map.addOverlay(new GMarker(point));

}

}

window.onload = function() { initialize() ; } ;

window.onunload = function() { GUnload() ; } ;

</script>

SCRIPT_END;

return $script ;

}

/**

* thrown data is json?

* @acess   private

* @param   string|array

* @return  boolean   is json not encoded return false.

**/

private function _isJson($addr) {

if(is_array($addr) || !is_string($addr)) {

return false ;

}

if(preg_match("/促{.*促}/", $addr)) {

return true ;

}

return false ;

}

/**

* has cache?

* @access  private

* @return  bool

**/

private function _hasCache() {

$file = $this->_cache_dir. $this->_cache_name ;

if(!file_exists($file)) {

unset($file) ;

return false ;

}

// checking for life time.

if($this->_compareLifeTime($file)) {

unset($file);

return true ;

}

unset($file) ;

return false ;

}

/**

* compare life time.

* @access  private

* @return  boolean

**/

private function _compareLifeTime($file) {

$modified = filemtime($file) ;

$now = time() ;

if*1 {

return false ;

}

$put_data = $this->_toJson($data) ;

$file = $this->_cache_dir. $this->_cache_name ;

$fp = fopen($file, "w") ;

flock($fp, LOCK_SH) ;

fputs($fp, $put_data. PHP_EOL) ;

flock($fp, LOCK_UN) ;

fclose($fp) ;

return true ;

}

/**

* load cache data

* if not has cache data, return empty array.

* @access  private

* @return  array   caceh data.

**/

private function _loadCache() {

$file = $this->_cache_dir. $this->_cache_name ;

$data = file($file) ;

if(empty($data)) {

return array() ;

}

return json_decode($data[0]) ;

}

/**

* get geometries.

* @access  private

* @param   array   address data array

**/

private function _getGeos($addr) {

if(empty($addr)) {

return false ;

}

$geos = array() ;

foreach($addr as $key => $address) {

$geos[$key] = $this->_findGeos($address) ;

}

return $geos ;

}

/**

* find for database geometry data.

* @access  private

* @param   string   find address name.

* @return  array    cache data or request google geo API data.

**/

private function _findGeos($address) {

$select = $this->_geo

->select()

->where("address = ?", $address) ;

$stmt = $select->query() ;

$row = $stmt->fetchAll() ;

if(!empty($row)) {

return $row[0] ;

}

else {

return $this->_requestGeocode($address) ;

}

}

/**

* request geo code.

* result is csv data.

* @access  priavte

* @param   array

*          response data is address, lat, lon.

**/

private function _requestGeocode($addr) {

if(empty($addr)) {

return array() ;

}

$cli = new Zend_Http_Client() ;

$cli->setUri(self::GEOCODE_URI) ;

$cli->setParameterGet(array(

"q"      => $address,

"key"    => $this->_api_key,

"output" => "csv"

)) ;

$response = $cli->request() ;

if($response->getStatus() === 200) {

$csv = $response->getBody();

if($csv) {

list($sts, $accure, $lat, $lon) = split(",", $csv)  ;

if*2 {

return json_encode(array());

}

return json_encode($data) ;

}

/**

* get google jsapi url

* @access private

* @return string    jsapi url

**/

private function _getApiUrl() {

if(!isset($this->_api_key)) {

return false ;

}

return self::API_URL. "?key=".$this->_api_key ;

}

/**

* set map box id

* @access  private

* @param   string

* @return  this

**/

private function _setId($id) {

if(!is_string($id)) {

$id = (string)$id ;

}

$this->_id = $id ;

return $this ;

}

/**

* set cache path

* @access   private

* @param    string

* @return   this

**/

private function _setCachePath($path) {

if(!is_string($path)) {

$path = (string)$path ;

}

$this->_cache_path = $path ;

return $this ;

}

/**

* set cache name

* @access  private

* @param   string

* @return this

**/

private function _setCacheName($name) {

if(!is_string($name)) {

$name = (string)$name ;

}

$this->_cache_name = $name ;

return $this ;

}

/**

* set cache life time.

* @access  private

* @param   string

* @return  this

**/

private function _setCacheLifeTime($time) {

if(!is_numeric($time)) {

$time = (int)$time ;

}

$this->_cache_life_time = $time ;

return $this ;

}

/**

* get script paramaters

* @access  private

* @return  array

**/

private function _getScriptParams() {

return array(

"id" => $this->_id

) ;

}

}

?>

[/CC]

付随するModel

[CC lang="php"]

<?php

require_once("Zend/Db/Table/Abstract.php") ;

class Nully_View_Helper_Google_Maps_MapModel extends Zend_Db_Table_Abstract {

/**

* geocode table name

*/

protected $_name = "geocodes" ;

/**

* primary key

**/

protected $_primary = "address" ;

}

?>

[/CC]

Modelは変にいろいろmethodたてるのがめんどくさかったのでDbTableを継承させて造りました。

テーブルは以下のクエリを発行で。

[CC lang="sql"]

CREATE TABLE geocodes (

`address` VARCHAR(255) NOT NULL DEFAULT '',

`lat` FLOAR DEFAULT NULL,

`lon` FLOAR DEFAULT NULL,

PRIMARY KEY  (address)

);

[/CC]

後はControllerでaddPrefixPathでパスとクラスPrefixを指定して、Viewで$this->map($this->addresses, $this->map_param) で呼ぶとGoogleMapsが表示されます。

$map_paramで有効なパラメタはid、cachepath、cachename、cachelifetimeです。

それぞれkey => valの形で渡します。

まだまだ足りない機能(nocssとか)いろいろ有るけど、まぁまだ実装しなくていいかなぁと。

あとそろそろGit使いたい。

*1:$now - $modified) < $this->_cache_life_time) {

return true ;

}

return false ;

}

/**

* save cache data.

* @access  private

* @param   array    geo data array.

**/

private function _saveCache($data) {

if(empty($data

*2:int)$sts == 200) {

$this->_addDbGeo($addr, $lat, $lon) ;

return array(

"address" => $addr,

"lat"     => $lat,

"lon"     => $lon

) ;

}

else {

$this->_addDbGeo($addr, 0, 0) ;

return array(

"address" => $addr,

"lat"     => 0,

"lon"     => 0

) ;

}

}

else {

// not have a csv data...

return false ;

}

}

else {

// http status error.

return false ;

}

}

/**

* add geometry to database.

* @access  private

* @param   string

* @param   floor

* @param   floor

**/

private function _addDbGeo($address, $lat, $lon) {

$geos = array(

"address" => $address,

"lat"     => $lat,

"lon"     => $lon

) ;

$res = $this->_geo->insert($geos) ;

}

/**

* to json encode.

* @access  private

* @param   array   to json data array.

* @return  string  to json data.

**/

private function _toJson($data) {

if(!is_array($data