В приложении 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?
в торте 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']);
Здесь вы должны использовать 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 с CakePHP 3
Существует несколько подходов для достижения вашего результата.
Вместо того, чтобы бороться с ORM, вы можете выполнить необработанный запрос ?
$conn = ConnectionManager::get('my_connection');
И затем вы можете запустить пользовательский запрос, например:
$results = $conn->execute('MY RAW SQL HERE');
Если вы хотите запустить запрос и загружать отношения , вам нужно будет использовать метод contains, например:
$query = $articles->find('all', ['contain' => ['Authors', 'Comments']]);
Если вы хотите использовать виртуальные поля, как в приведенном выше примере, вам нужно добавить его в класс модели сущности, например:
protected function _getDistance() { //access model members using $this->_properties eg //return $this->_properties['latitude'] . ' ' . // $this->_properties['longitude']; }
Это должно обеспечить, что ваше расстояние больше не равно нулю в вашем json.
И вы можете использовать метод расстояния из библиотеки CakephpGeo для построения запроса
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; }
Наконец, и не рекомендуется, вы можете получить всю необходимую информацию и использовать PHP, чтобы сузить результаты. Болезненный, медленный и не рекомендуется.
Люди могут захотеть посмотреть плагин GeoDistance CakePHP :
CakePHP-GeoDistance – это поведение CakePHP 3 для запроса геокодированных данных на основе картографического расстояния с использованием закона сферического косинуса. Это отлично подходит для запросов «найти мои ближайшие X» или «найти Y рядом со мной».
https://github.com/chris48s/cakephp-geodistance
Сделала работу.