PHPackages                             mohamedhabibwork/laravel-spatial - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. mohamedhabibwork/laravel-spatial

ActiveLibrary[Database &amp; ORM](/categories/database)

mohamedhabibwork/laravel-spatial
================================

Multi-database spatial data types extension for Laravel supporting MySQL 8+ and PostgreSQL 16+ (with PostGIS).

01PHP

Since Dec 6Pushed 5mo agoCompare

[ Source](https://github.com/mohamedhabibwork/laravel-spatial)[ Packagist](https://packagist.org/packages/mohamedhabibwork/laravel-spatial)[ RSS](/packages/mohamedhabibwork-laravel-spatial/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Laravel Spatial Extension
=========================

[](#laravel-spatial-extension)

[![Build Status](https://camo.githubusercontent.com/e38b7d0ea59392d8798d3af9f42ae0610e89d2864cdf0a9d6581f7b34539e6cc/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f6d6f68616d65646861626962776f726b2f6c61726176656c2d7370617469616c2e7376673f7374796c653d666c61742d737175617265)](https://travis-ci.org/mohamedhabibwork/laravel-spatial)[![Code Climate](https://camo.githubusercontent.com/40032854cf59885998d683be9e57471e3f0cf84ca6beda17c752f50779eca069/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636c696d6174652f6d61696e7461696e6162696c6974792f6d6f68616d65646861626962776f726b2f6c61726176656c2d7370617469616c2e7376673f7374796c653d666c61742d737175617265)](https://codeclimate.com/github/mohamedhabibwork/laravel-spatial/maintainability)[![Code Climate](https://camo.githubusercontent.com/c22503c123cb6dcbd9acdc7c4a8f7d55ebe5fcaaeffd4345da0221a714abc0b7/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636c696d6174652f632f6d6f68616d65646861626962776f726b2f6c61726176656c2d7370617469616c2e7376673f7374796c653d666c61742d73717561726526636f6c6f72423d344243413241)](https://codeclimate.com/github/mohamedhabibwork/laravel-spatial/test_coverage)[![Packagist](https://camo.githubusercontent.com/4350c4d07aae9fde4c4cb2465ed4e8231a00568c75a5725a125a639071e78207/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6f68616d65646861626962776f726b2f6c61726176656c2d7370617469616c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mohamedhabibwork/laravel-spatial)[![Packagist](https://camo.githubusercontent.com/dba80689ddf1ef1ed86daf60223e682c47c44b402821562a3ad0a13428039c76/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6f68616d65646861626962776f726b2f6c61726176656c2d7370617469616c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mohamedhabibwork/laravel-spatial)[![StyleCI](https://camo.githubusercontent.com/f614637c76386fdb94b4d036bb55d3fdde54cb9e5a4b0c903692764525a3eb70/68747470733a2f2f6769746875622e7374796c6563692e696f2f7265706f732f38333736363134312f736869656c643f6272616e63683d6d6173746572)](https://github.styleci.io/repos/83766141)[![license](https://camo.githubusercontent.com/7123c32787e013be5a8a13598ad01f562754637ed6141e89b02e85bf16d3e63e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6d6173686170652f6170697374617475732e7376673f7374796c653d666c61742d737175617265)](LICENSE)

Modern Laravel package for working with spatial data types and functions. Supports both **MySQL 8+** and **PostgreSQL 16+** (with PostGIS) using a transparent, unified API.

Features
--------

[](#features)

- 🌐 **Multi-Database Support**: Works seamlessly with MySQL 8+ and PostgreSQL 16+ (PostGIS)
- 🔄 **Transparent API**: Same code works for both databases
- 🚀 **Modern PHP**: Built with PHP 8.2+ features (typed properties, match expressions, readonly, etc.)
- 📦 **Laravel 11+**: Full integration with Laravel's query builder and Eloquent ORM
- 🎯 **Type Safety**: Comprehensive type hints and strict types throughout
- 🗺️ **SRID Support**: Full support for Spatial Reference System Identifiers
- 🔍 **Spatial Functions**: Distance calculations, geometric comparisons, and more

Database Support
----------------

[](#database-support)

### MySQL 8+

[](#mysql-8)

- Uses MySQL's native [Spatial Data Types](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html)
- Leverages [MySQL Spatial Functions](https://dev.mysql.com/doc/refman/8.0/en/spatial-function-reference.html)
- SRID support with `POINT SRID 4326` syntax
- Spatial indexes with `SPATIAL INDEX`

### PostgreSQL 16+ (PostGIS)

[](#postgresql-16-postgis)

- Requires [PostGIS extension](https://postgis.net/)
- Uses `geometry(Point, 4326)` type syntax
- Spatial indexes with `GIST INDEX`
- Full PostGIS function support

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- Laravel 11.0 or higher
- MySQL 8.0+ **OR** PostgreSQL 16+ with PostGIS extension

Installation
------------

[](#installation)

Install via Composer:

```
composer require mohamedhabibwork/laravel-spatial
```

### PostgreSQL Setup

[](#postgresql-setup)

For PostgreSQL, you must enable the PostGIS extension:

```
CREATE EXTENSION IF NOT EXISTS postgis;
```

The package service provider is auto-discovered by Laravel. For manual registration, add to `config/app.php`:

```
'providers' => [
    Habib\LaravelSpatial\SpatialServiceProvider::class,
],
```

Version History
---------------

[](#version-history)

- **`6.x.x`**: MySQL 8+ and PostgreSQL 16+ support with PHP 8.2+ (Current)
- `5.x.x`: MySQL 5.7 and 8.0 (Laravel 8+)
- `4.x.x`: MySQL 8.0 with SRID support (Laravel 8+)
- `3.x.x`: MySQL 8.0 with SRID support (Laravel &lt; 8.0)
- `2.x.x`: MySQL 5.7 and 8.0 (Laravel &lt; 8.0)
- `1.x.x`: MySQL 5.6 and 5.5

Quickstart
----------

[](#quickstart)

### Create a Migration

[](#create-a-migration)

```
php artisan make:migration create_places_table
```

Define your spatial columns:

```
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('places', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            // Add spatial data fields
            $table->point('location')->nullable();
            $table->polygon('area')->nullable();
            $table->timestamps();
        });

        // With SRID (WGS84 spheroid)
        // Schema::create('places', function (Blueprint $table) {
        //     $table->id();
        //     $table->string('name')->unique();
        //     $table->point('location', 4326)->nullable();
        //     $table->polygon('area', 4326)->nullable();
        //     $table->timestamps();
        // });
    }

    public function down(): void
    {
        Schema::dropIfExists('places');
    }
};
```

Run the migration:

```
php artisan migrate
```

### Create a Model

[](#create-a-model)

```
php artisan make:model Place
```

Use the `SpatialTrait` and define spatial fields:

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Habib\LaravelSpatial\Eloquent\SpatialTrait;
use Habib\LaravelSpatial\Types\Point;
use Habib\LaravelSpatial\Types\Polygon;

class Place extends Model
{
    use SpatialTrait;

    protected $fillable = ['name'];

    protected $spatialFields = [
        'location',
        'area',
    ];
}
```

### Save a Model

[](#save-a-model)

```
use App\Models\Place;
use Habib\LaravelSpatial\Types\Point;
use Habib\LaravelSpatial\Types\Polygon;
use Habib\LaravelSpatial\Types\LineString;

// Create a place with a point
$place = new Place();
$place->name = 'Empire State Building';
$place->location = new Point(40.7484404, -73.9878441);  // lat, lng
$place->save();

// With SRID
$place->location = new Point(40.7484404, -73.9878441, 4326);
$place->save();

// Create a polygon
$place->area = new Polygon([new LineString([
    new Point(40.74894149554006, -73.98615270853043),
    new Point(40.74848633046773, -73.98648262023926),
    new Point(40.747925497790725, -73.9851602911949),
    new Point(40.74837050671544, -73.98482501506805),
    new Point(40.74894149554006, -73.98615270853043)
])], 4326);
$place->save();
```

### Retrieve a Model

[](#retrieve-a-model)

```
$place = Place::first();
$lat = $place->location->getLat();  // 40.7484404
$lng = $place->location->getLng();  // -73.9878441
```

Geometry Classes
----------------

[](#geometry-classes)

All geometry types implement a common interface and work identically on both MySQL and PostgreSQL.

ClassOpenGIS Type`Point($lat, $lng, $srid = 0)`[Point](https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html)`MultiPoint(Point[], $srid = 0)`[MultiPoint](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipoint.html)`LineString(Point[], $srid = 0)`[LineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html)`MultiLineString(LineString[], $srid = 0)`[MultiLineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multilinestring.html)`Polygon(LineString[], $srid = 0)`[Polygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html)`MultiPolygon(Polygon[], $srid = 0)`[MultiPolygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipolygon.html)`GeometryCollection(Geometry[], $srid = 0)`[GeometryCollection](https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometrycollection.html)### Working with Geometries

[](#working-with-geometries)

#### From/To WKT (Well Known Text)

[](#fromto-wkt-well-known-text)

```
use Habib\LaravelSpatial\Types\Point;
use Habib\LaravelSpatial\Types\Polygon;

$point = Point::fromWKT('POINT(2 1)');
echo $point->toWKT();  // POINT(2 1)

$polygon = Polygon::fromWKT('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))');
echo $polygon->toWKT();
```

#### From/To GeoJSON

[](#fromto-geojson)

```
use Habib\LaravelSpatial\Types\Point;
use Habib\LaravelSpatial\Types\Geometry;

$point = new Point(40.7484404, -73.9878441);
$json = json_encode($point);
// {
//   "type": "Feature",
//   "properties": {},
//   "geometry": {
//     "type": "Point",
//     "coordinates": [-73.9878441, 40.7484404]
//   }
// }

// Deserialize
$location = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}');
```

Spatial Scopes
--------------

[](#spatial-scopes)

Query with spatial functions using Eloquent scopes. The same API works for both MySQL and PostgreSQL:

```
use App\Models\Place;
use Habib\LaravelSpatial\Types\Point;
use Habib\LaravelSpatial\Types\Polygon;

// Distance queries
$center = new Point(40.7484, -73.9878);
$nearby = Place::distance('location', $center, 1000)->get();

// Distance excluding the point itself
$others = Place::distanceExcludingSelf('location', $center, 5000)->get();

// Spherical distance (uses Earth's curvature)
$nearby = Place::distanceSphere('location', $center, 5000)->get();

// Geometric comparisons
$polygon = Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))');

Place::within('location', $polygon)->get();
Place::contains('area', $point)->get();
Place::intersects('area', $line)->get();
Place::crosses('line', $geometry)->get();
Place::disjoint('area', $geometry)->get();
Place::overlaps('area', $polygon)->get();
Place::equals('location', $point)->get();
Place::touches('area', $polygon)->get();

// Ordering by distance
$ordered = Place::orderByDistance('location', $center)->get();
$ordered = Place::orderByDistanceSphere('location', $center, 'desc')->get();
```

### Available Scopes

[](#available-scopes)

- `distance($column, $geometry, $distance)`
- `distanceExcludingSelf($column, $geometry, $distance)`
- `distanceSphere($column, $geometry, $distance)`
- `distanceSphereExcludingSelf($column, $geometry, $distance)`
- `within($column, $polygon)`
- `contains($column, $geometry)`
- `crosses($column, $geometry)`
- `disjoint($column, $geometry)`
- `equals($column, $geometry)`
- `intersects($column, $geometry)`
- `overlaps($column, $geometry)`
- `doesTouch($column, $geometry)`
- `orderByDistance($column, $geometry, $direction = 'asc')`
- `orderByDistanceSphere($column, $geometry, $direction = 'asc')`

Migrations
----------

[](#migrations)

### Available Column Types

[](#available-column-types)

```
$table->geometry('column_name', $srid = 0);
$table->point('column_name', $srid = 0);
$table->lineString('column_name', $srid = 0);
$table->polygon('column_name', $srid = 0);
$table->multiPoint('column_name', $srid = 0);
$table->multiLineString('column_name', $srid = 0);
$table->multiPolygon('column_name', $srid = 0);
$table->geometryCollection('column_name', $srid = 0);
```

### Spatial Indexes

[](#spatial-indexes)

```
Schema::table('places', function (Blueprint $table) {
    // Make column NOT NULL (required for spatial indexes)
    $table->point('location')->nullable(false)->change();

    // Add spatial index
    $table->spatialIndex('location');
});

// Drop spatial index
Schema::table('places', function (Blueprint $table) {
    $table->dropSpatialIndex(['location']);
    // or by index name
    $table->dropSpatialIndex('places_location_spatial');
});
```

**Note**: Spatial indexes require columns to be `NOT NULL`. For MySQL, InnoDB tables support spatial indexes (MySQL 5.7.5+). For PostgreSQL, GIST indexes are used automatically.

Database-Specific Considerations
--------------------------------

[](#database-specific-considerations)

While the API is identical, there are some internal differences:

### MySQL 8+

[](#mysql-8-1)

- Column definition: `POINT SRID 4326`
- Function calls: `ST_GeomFromText(?, ?, 'axis-order=long-lat')`
- Index type: `SPATIAL INDEX`

### PostgreSQL + PostGIS

[](#postgresql--postgis)

- Column definition: `geometry(Point, 4326)`
- Function calls: `ST_GeomFromText(?, ?)`
- Index type: `GIST INDEX`
- Requires PostGIS extension

The package handles these differences automatically, providing a unified API.

Testing
-------

[](#testing)

```
# Run all tests
composer test

# Unit tests only
composer test:unit

# Integration tests (requires database)
composer test:integration
```

### Integration Test Setup

[](#integration-test-setup)

For MySQL:

```
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=spatial_test mysql:8.0
```

For PostgreSQL:

```
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=spatial_test postgis/postgis:16-3.4
```

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Credits
-------

[](#credits)

- Originally inspired by [njbarrett's Laravel PostGIS package](https://github.com/njbarrett/laravel-postgis)
- Original MySQL implementation by Joseph Estefane
- Multi-database support and PHP 8.2+ modernization

License
-------

[](#license)

This package is open-sourced software licensed under the [MIT license](LICENSE).

###  Health Score

17

—

LowBetter than 6% of packages

Maintenance49

Moderate activity, may be stable

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/465ce474e9c105751d2df13e0aa0f243cdd37909529b7f957cac01251644b1b1?d=identicon)[mohamedhabibwork](/maintainers/mohamedhabibwork)

---

Top Contributors

[![mohamedhabibwork](https://avatars.githubusercontent.com/u/64292519?v=4)](https://github.com/mohamedhabibwork "mohamedhabibwork (3 commits)")

### Embed Badge

![Health badge](/badges/mohamedhabibwork-laravel-spatial/health.svg)

```
[![Health](https://phpackages.com/badges/mohamedhabibwork-laravel-spatial/health.svg)](https://phpackages.com/packages/mohamedhabibwork-laravel-spatial)
```

###  Alternatives

[doctrine/orm

Object-Relational-Mapper for PHP

10.2k285.3M6.2k](/packages/doctrine-orm)[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k115.1M102](/packages/jdorn-sql-formatter)[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[mongodb/mongodb

MongoDB driver library

1.6k64.0M546](/packages/mongodb-mongodb)[ramsey/uuid-doctrine

Use ramsey/uuid as a Doctrine field type.

90340.3M211](/packages/ramsey-uuid-doctrine)[reliese/laravel

Reliese Components for Laravel Framework code generation.

1.7k3.4M16](/packages/reliese-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
