Reputation: 714
I'm building some search capabilities into a Laravel 5.2 application. In my model, an Event has a Location, which is comprised of a latitude and longitude value. The Location class has a method for calculating distance $this->distanceTo($that)
.
I want to be able to search my events by a variety of criteria (keywords, category, etc.), but then order the results by distance to a specified point. So I have a route that looks something like this:
$query = App\Event::query();
if (Input::has('keyword')) {
$query = $query->where('name', 'like', '%'.Input::get('keyword').'%');
}
if (Input::has('category')) {
$query = $query->where('category_id', Input::get('category'));
}
$events = $query->get();
It seems to me like I need to run this query and then perform sorting on the collection it returns. But it would probably perform better if there was a way to incorporate the sorting into the query itself. Is there a way to do that?
If not, then I still need to figure out how to perform the sorting on the collection that gets returned. I know there's a sortBy() method available for a collection, but I'm not sure how to put this together when I need to sort by $eventFromCollection->location()->distanceTo($searchLocation)
. Any guidance on this?
Upvotes: 0
Views: 1293
Reputation: 5452
I built a similar feature and will share my experience.
There are a couple of different ways you can do this:
I will ignore #1 and #3 as you can easily find "How to's" for those.
2. Using PHP:
You will have to sort the results after the sql operation. You can use something like this:
public function distance($lat1, $lon1, $lat2, $lon2, $unit = 'K') {
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
$unit = strtoupper($unit);
if ($unit == "K") {
return round(($miles * 1.609344), 2);
} else if ($unit == "N") {
return round(($miles * 0.8684), 2);
} else {
return round($miles, 2);
}
}
This will give you the distance between two sets of coordinates. Iterate through your results from DB and sort them using this.
2. Using Hosted search service:
Algolia is a very good solution for "Search" in general. There is a Laravel package available for it that makes it easy to implement, and they have built-in Geo search feature.
I use a combination of 2, 3 and 4 based on the requirement.
Upvotes: 1