如何指定条件以按半径(纬度、经度)检索数据?
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!
另见
在 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!
另见