Laravelでgeometry型を扱う場合のマイグレーション、ミューテータ、アクセサのサンプルコードを紹介したいと思います。
参考にしたのは以下のページです。
GEO Spatial MySQL in Laravel 5
上記ページでは、位置情報をpoint型で登録する場合です。
point型ではなく、geometry型で運用したい場合に、上記ページのサンプルコードをどう変更すればよいかを紹介したいと思います。
##マイグレーション
以下のサンプルコードのSchma::create()の後に実行しているDB::statement()がポイントです。
public function up()
{
Schema::create('items', function($table)
{
$table->increments('id');
$table->string('title');
$table->timestamps();
});
DB::statement('alter table items add location geometry' );
}
Laravel標準のSchema Builderには、point型やgeometry型のカラムを定義する術がないので、上記のように、一度テーブルをcreateした後にalter table文でgeometry型のカラムを追加します。geometry型に関わらず、Schema Builderに用意されていない型のカラムを用意したい場合はこの方法で追加できます。
参考までにLaravelのSchema Builderのページを。
Schema Builder - Laravel - The PHP Framework For Web Artisans
##アクセサとミューテータ
説明は後述しますがまずはサンプルコードです。
class Item extends Model
{
protected $geofields = array('location');
public function setLocationAttribute(array $value)
{
$this->attributes['location'] = DB::raw("(GeomFromText('POINT(" . $value['lat'] . " " . $value['lng'] . ")'))");
}
public function getLocationAttribute(string $value)
{
$value = substr($value, strlen('POINT('), strlen($value) - (strlen('POINT(') + 1));
$value = explode(" ", $value);
$ret = [];
$ret['lat'] = $value[0];
$ret['lng'] = $value[1];
return $ret;
}
public function newQuery($excludeDeleted = true)
{
$raw='';
foreach($this->geofields as $column){
$raw .= ' astext('.$column.') as '.$column.' ';
}
return parent::newQuery($excludeDeleted)->addSelect('*',DB::raw($raw));
}
}
上記はあくまで実装例ですが、上記のようなミューテータ(setLocationAttribute())を実装すると、利用側からは、緯度と経度の配列を渡す事でgeometry型のカラムに正しい値を登録出来ます。
上記コードの場合だと、以下のような感じで設定すればOKです。
item->location = ['lat' => 34.6835522,'lng' => 135.4976581];
ミューテータで、上記で設定された文字列を、MySQLのPOINT関数とGeomFromText関数でgeometry型のカラムに登録します。
取得の際は、もう一手間必要でして、まずはnewQuery()というメソッドで、位置情報を登録しているカラム名と同名で、MySQLのastext関数を使って取得できるように仕込んでおきます。
このようにすると、ミューテータに渡した文字列が取得できるようになります。いや、正確には、GeomFromText関数に設定した引数なので、"POINT(緯度 経度)"の形ですね。
ですので、アクセサ(getLocationAttribute())では、この文字列から「POINT(」と「)」をsubstrで取り除いた文字列を元に、配列を生成して返却しています。
というわけで今回は以上です。