如何指定条件以按半径(纬度、经度)检索数据?

How to specify a condition to retrieve data by radius (lat, lon)?

在 cakephp 2 中,我可以为此使用 virtualField,但在 3 中似乎不可能。我已经为此苦苦挣扎了两天,没有从互联网或 cakephp 手册中得到运气。我没有收到任何错误,但我收到了空白 return.

我的控制器中的代码如下所示:

if (isset($this->request->data['location']) && (isset($this->request->data['radius']))){
        $radius = $this->request->data['radius'];
        $location = $this->request->data['location'];
        $address = $location; // Google HQ
        $HttpSocket = new Client();
        $geocode = $HttpSocket->get('http://maps.google.com/maps/api/geocode/json?address='.$address.'&sensor=false');
        $geocode = $geocode->json;
        if ($geocode['status'] == "OK"){ 
        $lat = $geocode['results'][0]['geometry']['location']['lat'];
        $lon = $geocode['results'][0]['geometry']['location']['lng']; 

$R = 6371;  // earth's mean radius, km  

// first-cut bounding box (in degrees)
$maxLat = $lat + rad2deg($radius/$R);
$minLat = $lat - rad2deg($radius/$R);

// compensate for degrees longitude getting smaller with increasing latitude
$maxLon = $lon + rad2deg($radius/$R/cos(deg2rad($lat)));
$minLon = $lon - rad2deg($radius/$R/cos(deg2rad($lat)));

        $conditions[] = ['Venues.lat' => "'BETWEEN '".$minLat."' AND '".$maxLat."'"];
        $conditions[] = ['Venues.lon' => "'BETWEEN :'".$minLon."' AND :'".$maxLon."'"];
       }

 $this->paginate =[ 
        'limit' => 10,
        'order' => ['Quads.date' => 'asc'],
        'conditions' => $conditions,
        'contain' => [
            'Performers' => ['Genres'],
            'Users' => ['Profiles'],
            'Venues' => ['fields' => [
                    'name',
                    'id',
                    'verified',
                    'address1',
                    'city',
                    'zip_code'], 'States'=>['Countries']],
            'Categories',
            'Likes' => ['Users' => ['Profiles']]]];

    $quads = $this->paginate();

不可能(几乎)没有。旧的虚拟字段概念已经消失了,对吧,新的 ORM 足够灵活,因此不再需要。

你的问题是你以错误的方式定义了条件,你在那里通过指定 key => value 集所做的是创建普通的运算符条件,其中的值将是 escaped/casted根据列类型。如果你真的没有收到任何错误,我会假设 lat/lan 列是数字类型,所以你的 BETWEEN ... 字符串最终会变成数字,条件看起来像

Venus.lat = 0 AND Venus.lon = 0

另请注意,您正在创建一个嵌套数组,即

[
    ['keyA' => 'value'],
    ['keyB' => 'value']
]

虽然这行得通,但如果您没有意识到,您可能 运行 会遇到更多问题,所以您最好坚持使用

[
    'keyA' => 'value',
    'keyB' => 'value'
]

除非确实有使用嵌套条件的技术原因。

tl;dr 使用表达式

也就是说,您可以使用表达式来构建适当的条件,例如

$conditions[] = $this->Quads->Venues
    ->query()->newExpr()->between('Venues.lat', $minLat, $maxLat);

$conditions[] = $this->Quads->Venues
    ->query()->newExpr()->between('Venues.lon', $minLon, $maxLon);

这将安全地创造适当的条件,例如

Venus.lat BETWEEN a AND b AND Venus.lon BETWEEN x AND Y

请注意,建议通过包含列的 table 创建表达式(在本例中为 VenuesTable),否则您必须手动指定列类型(请参阅QueryExpression::between()) 的第四个参数,以便应用正确的 casting/escaping!

另见