CakePHP query closest latitude longitude from database

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-30 12:21:34

there are no more virtualFields in cake 3 but you still can create an alias for your calculated field

As suggested by @ndm you'd better bind $latitude and $longitude to prevent SQL injections

$distanceField = '(3959 * acos (cos ( radians(:latitude) )
    * cos( radians( Sightings.latitude ) )
    * cos( radians( Sightings.longitude )
    - radians(:longitude) )
    + sin ( radians(:latitude) )
    * sin( radians( Sightings.latitude ) )))';

using where

$sightings = $this->Sightings->find()
    ->select([
        'distance' => $distanceField
    ])
    ->where(["$distanceField < " => $distance])
    ->bind(':latitude', $latitude, 'float')
    ->bind(':longitude', $longitude, 'float')
    ->contain(['Photos', 'Tags']);

using having

$sightings = $this->Sightings->find()
    ->select([
        'distance' => $distanceField
    ])
    ->having(['distance < ' => $distance])
    ->bind(':latitude', $latitude, 'float')
    ->bind(':longitude', $longitude, 'float')
    ->contain(['Photos', 'Tags']);

You need to use ConnectionManager here.

For example:

use Cake\Datasource\ConnectionManager;  // Mention this first, preferably on top of the page.

$connection = ConnectionManager::get('default');

$results = $connection
  ->execute('SELECT * FROM articles WHERE id = :id', ['id' => 1])
  ->fetchAll('assoc');

You could try and set up your query in this fashion. It will definitely work.

Use this as a reference:

SELECT Statements with CakePHP 3

There are several approaches to achieve your result.

1. Raw Query

Instead of struggling with the ORM, you could perform a raw query?

$conn = ConnectionManager::get('my_connection');

And then you can run a custom query like:

$results = $conn->execute('MY RAW SQL HERE');

2. Raw Query w/Eager loading

If you want to run a query and eager load the relations, you will need to use the contains method like:

$query = $articles->find('all', ['contain' => ['Authors', 'Comments']]);

3. Using Virtual Fields

If you want to use virtual fields like in your example above, you need to add it in your entity model class like:

protected function _getDistance()
{
    //access model members using $this->_properties e.g.
    //return $this->_properties['latitude'] . '  ' .
    //    $this->_properties['longitude'];
}

This should ensure your distance is no longer null in your json.

And you could use with the distance method from CakephpGeo library to construct a query

public function distance(array $pointX, array $pointY) {
    $res = $this->calculateDistance($pointX, $pointY);
    return ceil($res);
}

public static function calculateDistance($pointX, $pointY) {
    $res = 69.09 * rad2deg(acos(sin(deg2rad($pointX['lat'])) * sin(deg2rad($pointY['lat']))
        + cos(deg2rad($pointX['lat'])) * cos(deg2rad($pointY['lat'])) * cos(deg2rad($pointX['lng']
        - $pointY['lng']))));
    return $res;
}

4. Raw PHP

Lastly, and not recommended, you could retrieve all the info you need and use PHP to narrow down your results. Painful, slow and not recommended.

finally i got the solution for this. below is the working example

$distance = 25;
$start_latitude = 12.920479;
$start_longitude = 77.670547;

$contents = $this->Sightings->find()->select(['latitude', 'longitude', 'distance' => 'SQRT(
    POW(69.1 * (latitude - '.$start_latitude.'), 2) +
    POW(69.1 * ('.$start_longitude.' - longitude) * COS(latitude / 57.3), 2))'])->having(["distance < " => $distance]);

output

   {"lat": xx.920479,
    "lng": xx.670547,
    "distance": "0"
   }

People might want to look at the CakePHP GeoDistance Plugin:

CakePHP-GeoDistance is a CakePHP 3 behavior for querying geocoded data based on cartographic distance using the spherical cosine law. It is great for 'find my nearest X' or 'find Y near me' type queries.

https://github.com/chris48s/cakephp-geodistance

Did the job.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!