---
title: "Eloquent models — Geolocation for Laravel | Roundly"
description: "Store a Coordinates value object on any model with the HasLocation trait or CoordinatesCast, and query nearby rows with withinRadius()."
url: https://roundly-consulting.com/open-source/docs/geolocation-for-laravel/eloquent-models
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Geolocation for Laravel](https://roundly-consulting.com/open-source/docs/geolocation-for-laravel.md)

# Eloquent models

The HasLocation trait casts a coordinates attribute over latitude and longitude columns and adds a withinRadius() query scope:

```php
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Geolocation\Concerns\HasLocation;
use RoundlyConsulting\Geolocation\DataTransferObjects\Coordinates;

final class Store extends Model
{
    use HasLocation; // casts a `coordinates` attribute over `latitude`/`longitude` columns
}

$store = new Store;
$store->coordinates = new Coordinates(48.1486, 17.1077);
$store->save();

Store::query()->withinRadius(new Coordinates(48.15, 17.11), radiusKm: 5)->get();
```

The package ships no migration — add the two columns to your own table:

```php
Schema::create('stores', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->float('latitude')->nullable();
    $table->float('longitude')->nullable();
});
```

- Reading coordinates returns null when either column is null.
- Assigning null clears both columns.
- Assigning anything other than a Coordinates instance throws InvalidCoordinatesException.
- coordinates is rebuilt from the columns on every read, so updating latitude / longitude after reading coordinates is never undone on save.

## Exact radius queries

withinRadius() is a bounding-box filter, so rows in the box’s corners can sit slightly outside the true circle. Near the antimeridian it matches longitudes on both sides of ±180°, and near a pole it matches every longitude. Combine it with an exact Haversine check for precise results:

```php
$center = new Coordinates(48.15, 17.11);

$nearby = Store::query()
    ->withinRadius($center, radiusKm: 5)   // cheap bounding-box pre-filter in SQL
    ->get()
    ->filter(fn (Store $store): bool => $store->coordinates?->near($center, radiusKm: 5) ?? false);
```

## Using the cast directly

Apply CoordinatesCast without the trait, or map it onto a different column pair:

```php
use RoundlyConsulting\Geolocation\Casts\CoordinatesCast;

// Without the trait — latitude / longitude columns:
protected $casts = ['coordinates' => CoordinatesCast::class];

// A custom column pair (cast arguments map to latitudeColumn, longitudeColumn):
protected $casts = ['position' => CoordinatesCast::class.':lat,lng'];
```

The withinRadius() scope always filters on latitude and longitude, so custom column names work with the cast but not with the scope.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/geolocation-for-laravel.md)
