In many web applications, particularly those that involve mapping or location-based services, it is crucial to retrieve and display locations closest to a user's current position .
When dealing with geographical coordinates, calculating the distance between two points on the Earth's surface can be complex due to the spherical nature of the Earth. The Haversine formula is commonly used for this purpose. It calculates the distance between two points on a sphere given their longitudes and latitudes.
Setting Up the Scope
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Farm extends Model
{
public function scopeClosest($query, $lat, $long)
{
$distanceQuery = "
( 6371 * acos( cos( radians(?) ) *
cos( radians( farms.lat ) )
* cos( radians( farms.long ) - radians(?) )
+ sin( radians(?) ) *
sin( radians( farms.lat ) ) ) )
AS distance
";
return $query->select('farms.*')
->selectRaw($distanceQuery, [$lat, $long, $lat])
->orderBy('distance', 'asc');
}
}
Breaking Down the Scope
SQL Query: The Haversine formula is used within a raw SQL query to calculate the distance.
6371 is the Earth's radius in kilometers. If you need the distance in miles, replace it with 3959.
radians(?) converts degrees to radians for the latitude and longitude.
selectRaw: This method allows us to include raw expressions in our query. The placeholders (?) are replaced with the provided latitude and longitude values.
orderBy: Finally, we sort the results by the calculated distance in ascending order, so the closest farms appear first.
Using the Scope
$lat = 37.7749; // User's latitude or use $request->lat
$long = -122.4194; // User's longitude or use $request->long
$nearestFarm = Farm::closest($lat, $long)->first();
This query will return a collection of Farm models ordered by their proximity to the user's location.
Top comments (2)
You can simplify these calculations with
ST_Distance_Sphere
which returns the distance in km or miles/feet: dev.mysql.com/doc/refman/8.4/en/sp...Make sure you have an spatial index in your table if you want to sort by it.
nice