我有一个带有lat / long字段的Listings表。我正在使用Haversine Formula计算原点(33.987339,-81.036819)与每个列表的纬度/经度之间的距离(作为别名/虚拟字段)并返回在原点10英里范围内距离的商家信息。
phpMyAdmin中的以下SQL查询完全返回我期望的内容:
SELECT *, round(3959 * acos(cos(radians(33.987339)) * cos(radians(Listing.lat)) * cos(radians(Listing.long) - radians(-81.036819)) + sin( radians(33.987339)) * sin(radians(Listing.lat))))
AS distance, `Listing`.`id`
FROM `preview_site`.`listings` AS `Listing`
LEFT JOIN `preview_site`.`users` AS `User` ON (`Listing`.`user_id` = `User`.`id`)
LEFT JOIN `preview_site`.`categories` AS `Category` ON (`Listing`.`category_id` = `Category`.`id`)
LEFT JOIN `preview_site`.`states` AS `State` ON (`Listing`.`state_id` = `State`.`id`)
WHERE `Listing`.`status` = 'Active'
HAVING distance < 10
ORDER BY `distance` ASC LIMIT 20
尝试(以及多种方式失败)获取CakePHP代码以正确生成上述SQL后,我使用this tool从SQL生成以下CakePHP控制器代码(它提供了模型和控制器选项):
$this->Paginator->virtualFields = array(
'distance' => 'round(3959 * acos(cos(radians(33.987339)) * cos(radians(Listing.lat )) * cos(radians(Listing.long) - radians(-81.036819)) + sin(radians(33.987339)) * sin(radians(Listing.lat))))');
$this->Paginator->settings = array(
'fields' => array(
'Listing.*',
'Listing.distance',
'Listing.id',
'Category.*',
'State.*',
'User.*',
),
'joins' => array(
array(
'conditions' => array(
'Listing.user_id = UserJoin.id',
),
'table' => 'users',
'alias' => 'UserJoin',
'type' => 'left',
),
array(
'conditions' => array(
'Listing.category_id = CatJoin.id',
),
'table' => 'categories',
'alias' => 'CatJoin',
'type' => 'left',
),
array(
'conditions' => array(
'Listing.state_id = StateJoin.id',
),
'table' => 'states',
'alias' => 'StateJoin',
'type' => 'left',
),
),
'conditions' => array(
'Listing.status' => 'Active',
),
'order' => array(
'distance' => 'asc',
),
'limit' => '5',
'having' => array(
'distance <' => '10',
),
'contain' => array(
'User',
'Category',
'State',
),
);
$data = $this->Paginator->paginate('Listing');
$this->set('listings', $data);
如果我使用此代码,则会收到以下错误:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Listing.distance' in 'field list'
如果我将$ this-&gt; Paginator - &gt; virtualFields更改为$ this-&gt; 列表 - &gt; virtualFields(因为我找不到任何关于Paginator实际上使用了virtualFields方法),我没有得到任何错误,并且分页工作正常,但返回的结果不受距离限制(返回所有列表记录)。以下是带有距离别名的生成SQL的片段:
SELECT `Listing`.*, `Listing`.`id`, `Category`.*, `State`.*, `User`.*, (round(3959 * acos(cos(radians(33.987339)) * cos(radians(`Listing`.`lat` )) * cos(radians(`Listing`.`long`) - radians(-81.036819)) + sin(radians(33.987339)) * sin(radians(`Listing`.`lat`)))))
AS `Listing__distance`
FROM `preview_site`.`listings` AS `Listing`
有没有人对如何使这项工作正确有任何建议?任何帮助将不胜感激。
答案 0 :(得分:1)
我认为你的问题来自哪里,我相信CakePHP不承认“有”。由于您似乎没有Group By,因此您可以使用常规WHERE并获得相同的结果,在本例中为array('conditions' => array('distance <' => 10))
如果您确实有Group By,请参阅以下内容:
CakePHP: How can I use a "HAVING" operation when building queries with find method?
答案 1 :(得分:0)
关于虚拟领域有很多打开的门票。 这可能就是其中之一。
即使你对paginator的初始绑定看起来很糟糕。 您应该将虚拟字段添加到当前模型,因此列出。
$this->Listing->virtualFields['distance'] = ...
对我而言,在我无法轻松使用虚拟字段的情况下,它有助于手动使用别名字段,因此您的订单中包含Listing__distance ASC
,或者更重要的是在您的having子句中。
它还将重用已经计算的字段而不是再次执行它(即使我不知道这里是否有速度改进)。请参阅this。
另请注意,利用behavior来避免重复其他查询(并保持干燥)可能会更清晰:
$this->Listing->setDistanceAsVirtualField($lag, $lng);
我通常使用条件来限制距离(不需要有,有吗?)。