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

Laravel Eloquent Spatial Laravel Package

matanyadaev/laravel-eloquent-spatial

Laravel Eloquent Spatial adds spatial data types and query functions to Eloquent models. Easily work with geometry, points, polygons, distances, and spatial indexes across MySQL and PostgreSQL, with expressive casts, scopes, and query builders.

Deep Wiki
Context7

Getting Started

composer require matanyadaev/laravel-eloquent-spatial

Publish the config if you need to change the default SRID or column naming conventions:

php artisan vendor:publish --provider="Matanyadaev\LaravelSpatial\LaravelSpatialServiceProvider"

Define a spatial column and cast it to a geometry type:

// migration
Schema::create('places', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->point('location');          // or ->geometry(), ->polygon(), etc.
    $table->spatialIndex('location');
});
use Matanyadaev\LaravelSpatial\Types\Point;

class Place extends Model
{
    protected $casts = ['location' => Point::class];
}

First use case — "10 nearest places to me":

$me = new Point(51.5074, -0.1278); // confirm lat/lng vs x/y in the README

Place::query()
    ->orderByDistance('location', $me)
    ->limit(10)
    ->get();

Where to look first:

  1. README — the type table (Point, LineString, Polygon, Multi*, GeometryCollection) and the query-method table are the real API surface.
  2. config/laravel-spatial.php — SRID defaults, column type mapping, GeoJSON/WKT serialization options.
  3. src/Types — the geometry value objects; this is what your casts return.
  4. src/Query (or the builder macros) — where whereDistance, whereWithin, whereContains, orderByDistance are registered.
  5. Tests — the fastest way to see per-driver behavior (MySQL vs PostGIS vs SQLite).

Implementation Patterns

Cast the column once, query it everywhere. Don't wrap geometry in accessors that rebuild points on every read; let the cast do the work and keep the raw column as the single source of truth.

Compose with the normal builder. The spatial methods are macros, so they chain with scopes, where, pagination, and eager loads:

Place::query()
    ->where('is_active', true)
    ->whereDistance('location', $me, '<', 5_000)   // check argument signature/units
    ->orderByDistance('location', $me)
    ->paginate(25);

Prefilter, then order. For "nearest N" over a large table, add a cheap bounding-box/radius filter first so the spatial index can be used, then orderByDistance on the smaller set. Ordering alone is usually a full scan.

Containment and overlap for regions. Store polygons on areas or zones and use whereContains('boundary', $point) to answer "which zone am I in?" rather than doing it in PHP.

GeoJSON at the edge. Eloquent casts are convenient but don't assume the JSON shape is what Leaflet/Mapbox wants. Normalize in an API resource:

'geometry' => $place->location->jsonSerialize(), // verify GeoJSON vs WKT in config

Factories and seeders work naturally once the cast is registered:

Place::factory()->create([
    'location' => new Point(fake()->latitude(), fake()->longitude()),
]);

Pick one production driver and match it in CI. MySQL 8 and PostGIS are the well-trodden paths; SQLite is fine for unit tests of value objects but not for query behavior.

Consistent SRID. Use 4326 for WGS84 lat/lng everywhere unless you have a specific reason (web mercator 3857 for tiles). Mixing SRIDs is the #1 source of "why is this distance zero" bugs.

Gotchas and Tips

  • Coordinate order. Lat/lng and x/y (lng/lat) are easy to swap, and different spatial libraries disagree. Decide once, document it in the model, and write a test with an asymmetric point (e.g., lat 10, lng 50) so a swap is obvious.
  • Units differ by driver. MySQL's ST_Distance_Sphere and PostGIS geography return meters; raw ST_Distance on a 4326 geometry returns degrees. Don't copy a radius constant between environments without checking.
  • SRID mismatch fails quietly on some drivers (returns 0 or null) and loudly on others. Assert the SRID in tests.
  • SQLite is not spatial out of the box. Laravel's :memory: database will throw on geometry columns unless spatialite is loaded. Keep spatial query tests on MySQL/PostGIS.
  • MySQL 8 vs 5.7. Spatial function coverage and SRID enforcement differ significantly; if you're on 5.7, test early.
  • Binary bindings. toSql() shows ?, and getBindings() may contain binary blobs — dump with bin2hex() rather than dd(), or you'll get unreadable output.
  • Spatial indexes are picky. They help whereWithin/whereContains-style predicates; they generally do not accelerate orderByDistance.
  • Null geometry columns cast to null, not an empty point — guard in resources and Blade templates.
  • Serialization surprises. Check whether toArray()/toJson() emits WKT, GeoJSON, or a plain string in your configured version; frontend map libraries expect GeoJSON.
  • Extension points: register custom SQL expressions with DB::raw / selectRaw for functions the package doesn't expose, and extend the base geometry class if you need a domain-specific type (e.g., a validated Point with application invariants).
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle