CakePHP query closest latitude longitude from database

后端 未结 5 509
轻奢々
轻奢々 2021-01-02 02:04

In a CakePHP (v3) application, how can I retrieve the closest results based on passed lat lng values?

I\'d like to have them back as native CakePHP entities, so some

相关标签:
5条回答
  • 2021-01-02 02:31

    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.

    0 讨论(0)
  • 2021-01-02 02:35

    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']);
    
    0 讨论(0)
  • 2021-01-02 02:35

    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.

    0 讨论(0)
  • 2021-01-02 02:42

    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"
       }
    
    0 讨论(0)
  • 2021-01-02 02:54

    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

    0 讨论(0)
提交回复
热议问题