How to handle mysql spatial datatypes in eloquent ORM?, This include how to create migration, insert spatial data and performing spatial query's. If there is not actual solutions exists, is there any workarounds?
Asked
Active
Viewed 1.3k times
21
-
8Maybe this could help you out: http://www.codetutorial.io/geo-spatial-mysql-laravel-5/ – haakym Jun 06 '15 at 11:59
-
The link provided by @haakym is so helpful. Laravel does not support these types natively (look here https://github.com/laravel/framework/blob/5.1/src/Illuminate/Database/Schema/Blueprint.php) but you always can run a DB:statement() in your migrations or even, extended the Blueprint class and add your own methods to support those datatypes. Personally I use the DB statement approach. – jhmilan Jun 27 '15 at 13:23
2 Answers
13
A workaround I have implemented a while ago is to have a latitude and longitude fields on the model with the following validations (see Validator class):
$rules = array('latitude' => 'required|numeric|between:-90,90',
'longitude'=>'required|numeric|between:-180,180',)
The magic comes on the boot method of the model, which sets the correct value of the spatial point field:
/**
* Boot method
* @return void
*/
public static function boot(){
parent::boot();
static::creating(function($eloquentModel){
if(isset($eloquentModel->latitude, $eloquentModel->longitude)){
$point = $eloquentModel->geoToPoint($eloquentModel->latitude, $eloquentModel->longitude);
$eloquentModel->setAttribute('location', DB::raw("GeomFromText('POINT(" . $point . ")')") );
}
});
static::updated(function($eloquentModel){
if(isset($eloquentModel->latitude, $eloquentModel->longitude)){
$point = $eloquentModel->geoToPoint($eloquentModel->latitude, $eloquentModel->longitude);
DB::statement("UPDATE " . $eloquentModel->getTable() . " SET location = GeomFromText('POINT(" . $point . ")') WHERE id = ". $eloquentModel->id);
}
});
}
About migrations, like @jhmilan says you can always use the Schema::create and DB::statement methods to customize the migration.
Schema::create('locations', function($table){
$table->engine = "MYISAM";
$table->increments('id')->unsigned();
$table->decimal('latitude', 10, 8);
$table->decimal('longitude', 11, 8);
$table->timestamps();
});
/*Espatial Column*/
DB::statement('ALTER TABLE locations ADD location POINT NOT NULL' );
/*Espatial index (MYISAM only)*/
DB::statement( 'ALTER TABLE locations ADD SPATIAL INDEX index_point(location)' );

Emilio Borraz
- 516
- 3
- 17
-
you can now create index on Spatial datatype on Innodb engine. (from Mysql 5.7) – Mehrdad Shokri Sep 28 '16 at 15:22
-
-
1geoToPoint is only a helper function to concatenate the latitude and longitude, that returns the following: $latitude . " " . $longitude; – Emilio Borraz Jan 22 '17 at 02:59
6
It is available to use https://github.com/grimzy/laravel-mysql-spatial
you may use:
namespace App;
use Illuminate\Database\Eloquent\Model;
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait;
/**
* @property \Grimzy\LaravelMysqlSpatial\Types\Point $location
*/
class Place extends Model
{
use SpatialTrait;
protected $fillable = [
'name',
];
protected $spatialFields = [
'location',
];
}
then you are able to run queries on the 'location' field.
to store model you may use:
$place1 = new Place();
$place1->name = 'Empire State Building';
$place1->location = new Point(40.7484404, -73.9878441);
$place1->save();
to retrieving a model you should use:
$place2 = Place::first();
$lat = $place2->location->getLat(); // 40.7484404
$lng = $place2->location->getLng(); // -73.9878441

Mohammad Nourinik
- 189
- 2
- 5