CakePHP从数据库

时间:2016-07-19 15:26:42

标签: php mysql cakephp location cakephp-3.0

在CakePHP(v3)应用程序中,如何根据传递的lat lng值检索最接近的结果?

我希望将它们作为本机CakePHP实体返回,如下所示:

public function closest($lat, $lng) {

    $sightings = //records within given lat lng 

    $this->set(compact('sightings'));
    $this->set('_serialize', ['sightings']);
}

我知道这个SQL有效:

SELECT *,
       ( 3959 * acos( cos( radians(50.7) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(-1.8) ) + sin( radians(50.7) ) * sin( radians( latitude ) ) ) ) AS distance
  FROM sightings
HAVING distance < 10
 ORDER BY distance
 LIMIT 0 , 20

努力将两者结合起来。

更新

我已添加

class Sighting extends Entity {
    public $_virtual = ['distance'];
    //rest of class...
}

所以现在distance出现在我的json输出中(正如预期的那样,值为null,但我觉得这是一个步骤)。

我在这里看了一下:http://www.mrthun.com/2014/11/26/search-distance-cakephp/这似乎是我想要实现的,所以假设这样:

$latitude = 51.145;
$longitude = -1.45;
$distance = 100;

$this->Sightings->virtualFields
    = array('Sightings.distance'
     => '(3959 * acos (cos ( radians('.$latitude.') )
    * cos( radians( Sightings.latitude ) )
    * cos( radians( Sightings.longitude )
    - radians('.$longitude.') )
    + sin ( radians('.$latitude.') )
    * sin( radians( Sightings.latitude ) )))');

$sightings = $this->Sightings->find('all', [
    'conditions' => ['Sightings.distance <' => $distance]
]);

$this->set(compact('sightings'));
$this->set('_serialize', ['sightings']);

结果:Column not found: 1054 Unknown column 'Sightings.distance' in 'where clause'

不确定它是否可能是CakePHP v2而不是v3?

5 个答案:

答案 0 :(得分:10)

蛋糕3中没有更多虚拟字段,但您仍可以为计算字段创建别名

根据@ndm的建议,您最好绑定$latitude$longitude以防止SQL注入

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

使用

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

使用

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

答案 1 :(得分:0)

您需要在此处使用ConnectionManager。

例如:

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');

您可以尝试以这种方式设置查询。它肯定会奏效。

使用此作为参考:

SELECT Statements with CakePHP 3

答案 2 :(得分:0)

有几种方法可以实现您的结果。

1。原始查询

您可以执行raw query

,而不是与ORM斗争
$conn = ConnectionManager::get('my_connection');

然后您可以运行自定义查询,如:

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

2。原始查询w / Eager loading

如果要运行查询eager load the relations,则需要使用包含方法,如:

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

3。使用虚拟字段

如果您想使用上述示例中的虚拟字段,则需要将其添加到实体模型类中,如:

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

这应该确保你的json中的距离不再为空。

您可以使用CakephpGeo library中的距离方法来构建查询

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。原始PHP

最后,不推荐,您可以检索所需的所有信息,并使用PHP缩小搜索范围。痛苦,缓慢,不推荐。

答案 3 :(得分:0)

最后我得到了解决方案。下面是工作示例

$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]);

输出

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

答案 4 :(得分:-1)

人们可能想查看CakePHP GeoDistance Plugin

  

CakePHP-GeoDistance是一种CakePHP 3行为,用于使用球面余弦定律根据制图距离查询地理编码数据。它非常适合“找到我最近的X”或“找到我附近的Y”类型查询。

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

做完了。