Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laraversine Laravel Package

freepeace13/laraversine

A lightweight Laravel package for Haversine distance calculations. Compute great-circle distances between latitude and longitude points, then use them for nearby searches, radius filtering, or distance sorting in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

laraversine is a small Laravel package for haversine (great-circle) distance queries against Eloquent models. v1.1.0 is the first release that makes the computed distance a first-class citizen via a Distance value object, so it's the natural entry point.

  1. Install and (if present) publish config:

    composer require freepeace13/laraversine
    php artisan vendor:publish --tag=laraversine-config
    
  2. Look first at the package README's "Quick start" and the config/laraversine.php defaults — specifically the default unit (km vs. miles) and the earth radius constant. Those two settings are what most "why is my number off" reports come down to.

  3. Add the package trait to a model that has latitude/longitude columns:

    use Freepeace13\Laraversine\Concerns\HasCoordinates;
    
    class Store extends Model
    {
        use HasCoordinates;
    }
    
  4. First real use case — "stores within 10km, closest first":

    $stores = Store::query()
        ->nearby($lat, $lng, radius: 10)
        ->nearestFirst()
        ->take(20)
        ->get();
    
    $stores->first()->distance()->kilometers(); // 1.42
    

If that snippet works, you've exercised the whole package: scope, ordering, and the new value object.

Implementation Patterns

Scope-then-order. nearby() establishes the distance computation and filters; nearestFirst() only ever orders a query that has already had nearby() applied. Keep them adjacent in the chain so the dependency is visually obvious:

Store::nearby($lat, $lng, 25)->nearestFirst()->paginate(15);

Combine with normal query builder constraints. nearby() returns a builder, so category filters, whereNull, scopes, and eager loads all compose normally. Apply your where clauses before nearestFirst() for readability — ordering doesn't care about position in the chain.

Reading the distance. Reach for distance() on a hydrated model rather than re-computing in PHP:

foreach ($stores as $store) {
    printf("%s — %.1f km\n", $store->name, $store->distance()->kilometers());
}

Treat the returned Distance as immutable and format at the edge (Blade, API resource, notification), not in the model. If you need a specific unit, prefer the value object's own conversion methods over multiplying the raw float yourself.

Resource / API layer. Because the accessor returns an object, don't dump it straight into toArray(). Map it explicitly:

return [
    'id'      => $this->id,
    'name'    => $this->name,
    'distance_km' => round($this->distance()->kilometers(), 2),
];

Repository / service wrapper. If several controllers need "nearest X," wrap the pattern once:

class NearbyStores
{
    public function __invoke(float $lat, float $lng, float $km, int $limit = 20)
    {
        return Store::query()
            ->where('is_active', true)
            ->nearby($lat, $lng, $km)
            ->nearestFirst()
            ->limit($limit)
            ->get();
    }
}

Testing. Use SQLite in-memory for fast scope tests, seeding a handful of coordinates with known separations (e.g. points on the same meridian at 0.01° intervals) and asserting rounded distances — this sidesteps floating-point flakiness while still catching unit/config regressions.

Gotchas and Tips

  • nearestFirst() before nearby() now throws. In v1.1.0 this produces an explicit exception instead of a confusing SQL error or silently wrong ordering. If you were relying on the old behaviour, you were already broken; the message now tells you exactly what to fix.
  • The distance accessor needs the computed column. distance() only makes sense on models returned from a nearby() query. On a model fetched via find() there is no distance to expose — expect an error or a null-ish result depending on how the accessor is implemented. Check the source before assuming either; guard with if ($store->hasDistance())-style checks or only call it in the nearby code path.
  • Units are config-driven, not per-call. Mixing kilometres in one controller and miles in another is a recipe for silent bugs. Decide the unit at the config level and convert at the presentation layer via the Distance object.
  • Bounding-box prefilter for large tables. Haversine in SQL cannot use a plain index on latitude/longitude. For anything beyond modest row counts, add a cheap whereBetween box on lat/lng before nearby() — it prunes the scan dramatically and the radius filter then cleans up the corners.
  • Precision and rounding. Compare floats with a tolerance in tests, and round only at output. A Distance value object is a good place to centralise rounding so every endpoint reports consistently.
  • Debugging the generated SQL. ->toSql() / ->dd() on the chain will show the raw haversine expression; if the query is slow, that's usually the first thing to look at alongside EXPLAIN.
  • Extension points. The config file and the Distance object are the two intended seams: unit/radius configuration on one side, formatting/conversion on the other. Prefer these over subclassing the trait — trait method collisions with your own distance() or nearby() methods will fail at class-load time, so check for name clashes when adding the trait to an existing model.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package