cakephp1.3でMySQLのgeometry型を使いたとおもったのですが、普通にModel::saveだとうまくいきません。
afterSaveとかでsaveすると2回クエリなげることになって微妙な感じだったんで、なにか方法はないかと探していました。
まる2日間ぐらい悩んだんですが、DboSource::expression()使えばいいんじゃね?ってことで、試してみました。
とりあえずスキーマはこんな感じ
1
2
3
4
5
6
7
8
|
CREATE TABLE `test`.`areas` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` VARCHAR( 255 ) NOT NULL ,
`latlng` GEOMETRY NOT NULL ,
`created` DATETIME NOT NULL ,
`modified` DATETIME NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
んで、適当にbakeとかしてmodelとかcontrollerとかつくっておきます。
controllerのsave前にexpressionを使えば良いわけです。
ちなみに前提としているPOSTされるデータは
$this->data[‘Area’][’name’],
$this->data[‘Area’][’lat’],
$this->data[‘Area’][’lng’],
といった感じになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?php
class AreasController extends AppController {
var $name = 'Areas';
function add() {
if (!empty($this->data)) {
$this->Area->create();
if ( !is_null($this->data['Area']['lat']) && !is_null($this->data['Area']['lng']) ) {
$this->data['Area']['latlng'] = $db->expression(
"GeomFromText('POINT({$this->data['Area']['lat']} {$this->data['Area']['lng']})')"
);
}
if ($this->Area->save($this->data)) {
$this->Session->setFlash(__('The item has been saved', true));
$this->redirect(array('action' => 'index'));
}
}
}
}
|
これでエラーも出ずにsaveが出来てしまいます。
次はfind周りの対策をします。
findした際に戻り値が以下のような形になってしまいます。
1
|
string '����������l�xa@:@0G��A@' (length=25)
|
これではさすがにカオスなので、modelに以下のように「$virtualFields」を指定しておきます。
1
2
3
4
5
6
7
8
9
10
11
|
<?php
class Area extends AppModel {
var $name = 'Area';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $virtualFields = array(
'lng' => 'X(latlng)',
'lat' => 'Y(latlng)'
);
}
|
そすると、普通にlatとlngに別れて値が戻ってきます。
[参考]
CakePHPでGeometry型を使ってみたよ。
Handling spatial data in CakePHP