Haversine distance calculation between two points in Laravel

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Mastering Geospatial Queries in Laravel: Calculating Haversine Distance Between Points As senior developers working with location-based services in Laravel, we often encounter the need to calculate the distance between geographical points. The most common method for this is the Haversine formula, which calculates the great-circle distance over a sphere given two points on the surface of that sphere. While the mathematical concept is straightforward, implementing it efficiently within an Eloquent query, especially when dealing with relationships and raw SQL, can prove surprisingly tricky. This post will walk you through exactly how to correctly implement the Haversine calculation in Laravel, resolving the issues you encountered with accessing related model data within `selectRaw`. We will look at the theory, debug your specific query structure, and explore best practices for geospatial data handling in a robust application built on frameworks like Laravel. ## Understanding the Haversine Formula Context The Haversine formula determines the shortest distance between two points on a sphere. The core idea involves converting latitude and longitude into radians, applying trigonometric functions (sine and cosine), and finally calculating the arc length. The general formula for distance $d$ between point 1 $(\phi_1, \lambda_1)$ and point 2 $(\phi_2, \lambda_2)$ is: $$ d = R \cdot \arccos\left( \sin(\phi_2) \cdot \sin(\phi_1) + \cos(\phi_2) \cdot \cos(\phi_1) \cdot \cos(\lambda_2 - \lambda_1) \right) $$ Where $R$ is the radius of the Earth (approximately 6371 km). When translating this into raw SQL, we must ensure that the latitude and longitude values from the related tables are correctly referenced within the calculation. This is where many developers run into trouble when mixing Eloquent relationships with complex mathematical functions in a single query. ## Debugging the Laravel Implementation Your approach using `selectRaw` was correct in principle, but the issue often lies in how Eloquent handles accessing nested attributes from related models inside raw SQL expressions. When you use `with('user')`, Laravel loads the necessary data, but referencing it directly in a deeply nested raw calculation requires explicit database referencing syntax (like table aliases or direct column access). The key to solving this is ensuring that your raw expression correctly references the columns from both the main table (`products`) and the related table (`users`). Since you have defined `longitude` and `latitude` on the `User` model, we must reference them through the relationship context. ### The Corrected Approach using Eloquent Relationships Instead of relying purely on nested dot notation in complex mathematical functions within `selectRaw`, it is often cleaner and more reliable to calculate the distance by explicitly joining the necessary tables first, ensuring the database has all the required coordinates available for calculation. Here is a refined way to structure your query, focusing on the join: ```php $latitude = 51.0258761; $longitude = 4.4775362; $radius = 20000; $products = Product::select('products.*', function ($query) use ($latitude, $longitude, $radius) { $query->selectRaw(" ( 6371 * acos( cos( radians( ? ) * cos( radians(users.latitude) ) * cos( radians(users.longitude) - radians( ? ) ) + sin( radians( ? ) ) * sin( radians(users.latitude) ) ) ) AS distance ", [$latitude, $longitude]) // Pass the boundary coordinates as parameters safely ->whereRaw("distance < ?", [$radius]) ->orderBy('distance'); }); // Note: The calculation above assumes a direct join or subquery context where 'users' is accessible. ``` **Why this works better:** By using `selectRaw` within a closure on the main query, we can leverage Eloquent's ability to structure the underlying SQL correctly. Crucially, by passing the dynamic latitude/longitude values as parameters (`?`), we prevent SQL injection and ensure the database interprets these variables safely, rather than embedding literal strings into the mathematical function. ## Best Practices: Geospatial Data and Laravel While raw SQL is powerful for complex calculations like Haversine, for large-scale geospatial applications, relying solely on manual trigonometric functions in application code or raw queries can become cumbersome and less performant. For truly demanding geospatial tasks—such as finding all points within a radius (a spatial query) rather than calculating distances between two known points—we highly recommend integrating a dedicated spatial database extension like **PostGIS** if you are using PostgreSQL, or utilizing MySQL's built-in spatial functions. These systems are optimized specifically for these types of queries, offering superior indexing and performance compared to custom mathematical calculations. When building robust features in Laravel, always consider leveraging the power of your underlying database engine. For more complex geospatial needs, exploring packages that integrate well with raw SQL, such as those found on platforms like [laravelcompany.com](https://laravelcompany.com), will provide a scalable foundation for future development. ## Conclusion Calculating Haversine distance in Laravel requires careful attention to how Eloquent handles raw SQL expressions. By correctly structuring your query, utilizing proper table references within `selectRaw`, and employing safe parameter binding, you can successfully perform complex mathematical operations directly on your database. For simple point-to-point calculations like this, mastering the syntax is the first step; for high-volume geospatial work, transitioning to native spatial extensions will unlock true performance gains.