Yii2-geopoint
ActiveRecord inspired by yii2-spatial but made simpler only to use specific spatial datatype: POINT.
Transform the internal MySQL format to simple coordinate text after finding, and vice versa before storing., (*1)
Yii2-geopoint can also be used to find the model or models which are nearest to a given location., (*2)
Notice that this extension can be used with MySQL >= 5.6.1
, MariaDB >= 5.3.3
, and PostgreSQL >= 9.1
., (*3)
Installation
Install Yii2-geopoint with Composer. Either add the following to the require section of your composer.json
file:, (*4)
"reza-id/yii2-geopoint": "*"
, (*5)
Or run:, (*6)
$ php composer.phar require reza-id/yii2-geopoint "*"
, (*7)
You can manually install Yii2-geopoint by downloading the source in ZIP-format., (*8)
Usage
Create spatial indexed table using migration:, (*9)
$this->createTable('{{%place}}', [
'id' => $this->primaryKey(),
'name' => $this->string(125)->notNull(),
'location' => 'POINT NOT NULL',
], $tableOptions);
if ($this->db->driverName === 'mysql') {
$this->execute('CREATE SPATIAL INDEX `idx-place-location` ON '.'{{%place}}(location);');
} elseif ($this->db->driverName === 'pgsql') {
$this->execute('CREATE INDEX "idx-place-location" ON '.'{{%place}} USING GIST(location);');
}
Use a rezaid\geopoint\ActiveRecord
as base class for your models, like so:, (*10)
<?php
namespace app\models;
use rezaid\geopoint\ActiveRecord;
class MyModel extends ActiveRecord
{
// ...
}
Notice: if you override find()
in a rezaid\geopoint\ActiveRecord
-derived class, be sure to return a rezaid\geopoint\ActiveQuery
and not an 'ordinary' yii\db\ActiveQuery
., (*11)
ActiveQuery method
nearest()
public function nearest($from, $attribute, $radius, $unit)
Change the query so that it finds the model(s) nearest to the point given by $from
., (*12)
-
$from
- string
: location in the form <lng>,<lat>
(two floats
).
-
$attribute
- string
attribute name of Point
in the model.
-
$radius
- number
search radius in kilometers or miles. Default 100
.
-
$unit
- string
unit value km
for kilometers or mil
for miles. Default km
.
Example usages:, (*13)
$here = '4.9,52.3'; // longitude and latitude of my place
$nearestModel = <model>::find()->nearest($here, <attributeName>, 200, 'mil')->one(); // search radius is 200 miles
$fiveNearestModels = <model>::find()->nearest($here, <attributeName>)->limit(5)->all(); // search radius is 100 km (default)
$dataProvider = new ActiveDataProvider([ 'query' => <model>::find()->nearest($here, <attributeName>) ]);
ActiveRecord method
getDistance()
Get the distance from given location in the ActiveQuery method nearest()
, if you want to display the distance in RESTful API, add this as new field in your model:, (*14)
<?php
namespace app\models;
use rezaid\geopoint\ActiveRecord;
class MyModel extends ActiveRecord
{
// ...
public function fields()
{
$fields = parent::fields();
$fields['distance'] = function ($model) {
return $model->getDistance();
};
return $fields;
}
// ...
}
Example rest controller:, (*15)
<?php
namespace app\controllers;
use Yii;
use yii\rest\ActiveController;
class PlaceController extends ActiveController
{
public $modelClass = 'app\models\MyModel';
public function actionSearch()
{
$from = Yii::$app->request->get('from');
$model = new $this->modelClass;
$query = $model->find();
if (!empty($from)) {
$query->nearest($from, 'location', 200);
}
try {
$provider = new \yii\data\ActiveDataProvider([
'query' => $query,
]);
} catch (Exception $ex) {
throw new \yii\web\HttpException(500, 'Internal server error');
}
if ($provider->getCount() <= 0) {
throw new \yii\web\HttpException(404, 'No entries found');
} else {
return $provider;
}
}
}