CakePHP - Проблема с виртуальным полем с помощью Помощника по разбивке на страницы
У меня есть таблица списков с полями lat/long. Я использую формулу Хаверсина для вычисления расстояния (в качестве псевдонима/виртуального поля) между исходной точкой (33,987339, -81,036819) и широтой/длиной каждого списка и возвращаю списки с расстоянием в пределах 10 миль от исходной точки.
Следующий SQL-запрос в phpMyAdmin возвращает именно то, что я ожидаю:
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, я использовал этот инструмент для генерации следующего кода контроллера CakePHP (он предоставлял как параметры модели, так и контроллера) из SQL:
$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->Пагинатор->Виртуальные поля на $this->Список ->Виртуальные поля (так как я не смог найти никакой документации по пагинатору, фактически использующему метод виртуальных полей), я не получу никаких ошибок и разбивка на страницы работает нормально, но возвращаемые результаты не ограничены расстоянием (возвращаются все записи списка). Вот фрагмент сгенерированного 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`
У кого-нибудь есть какие-либо предложения о том, как заставить это работать правильно? ЛЮБАЯ помощь будет очень признательна.
2 answers
Я думаю, что причина вашей проблемы в том, что CakePHP не распознает "Обладание", я полагаю. Поскольку у вас, похоже, нет группы по, вы можете просто использовать обычную WHERE и получить те же результаты, в данном случае array('conditions' => array('distance <' => 10))
Если у вас есть Группа По, см. Ниже:
Пирожное: Как я могу использовать операцию "НАЛИЧИЕ" при построении запросов с помощью метода find?
Существует довольно много открытых билетов, касающихся виртуальных полей. Это вполне может быть одним из них.
Даже несмотря на то, что ваша первоначальная привязка к пагинатору выглядит не так. Вы должны добавить виртуальные поля в текущую модель, поэтому перечислите.
$this->Listing->virtualFields['distance'] = ...
Для меня в тех сценариях, когда я не мог легко использовать виртуальное поле, помогало вручную использовать поле с псевдонимами, поэтому Listing__distance ASC
в вашем заказе или, что более важно, в вашем предложении having.
Вместо этого он также будет повторно использовать уже вычисленное поле сделать это снова (хотя я не знаю, есть ли здесь улучшение скорости таким образом). Смотрите это.
Также обратите внимание, что было бы чище использовать поведение , чтобы избежать повторения этого для других запросов (и сохранить его СУХИМ):
$this->Listing->setDistanceAsVirtualField($lag, $lng);
И я обычно использую условия для ограничения расстояния (в этом нет необходимости, не так ли?).