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

Doctrine Postgis Laravel Package

jsor/doctrine-postgis

Adds PostGIS spatial support to Doctrine DBAL/ORM: geometry and geography column types, schema helpers, SQL and DQL functions, and spatial query integration for PostgreSQL/PostGIS.

View on GitHub
Deep Wiki
Context7

Getting Started

jsor/doctrine-postgis is a Doctrine DBAL/ORM extension — it does not integrate with Eloquent. In a Laravel app you almost always wire it in through laravel-doctrine/orm (or use the DBAL only, via DB::connection()->getDoctrineConnection()).

Minimal steps:

  1. Enable PostGIS on the database: CREATE EXTENSION IF NOT EXISTS postgis;

  2. composer require jsor/doctrine-postgis

  3. Register the DBAL types in config/doctrine.php under dbal.types:

    'dbal' => [
        'types' => [
            'geometry'            => Jsor\Doctrine\PostGIS\Types\GeometryType::class,
            'geography'           => Jsor\Doctrine\PostGIS\Types\GeographyType::class,
            'geometry_collection' => Jsor\Doctrine\PostGIS\Types\GeometryCollectionType::class,
            'point'               => Jsor\Doctrine\PostGIS\Types\PointType::class,
            'linestring'          => Jsor\Doctrine\PostGIS\Types\LineStringType::class,
            'polygon'             => Jsor\Doctrine\PostGIS\Types\PolygonType::class,
            // ...multipoint, multilinestring, multipolygon
        ],
    ],
    
  4. Register the PostGIS DQL functions and platform (see below) on the connection config, or you'll get "unknown function ST_*" errors.

  5. First use case: a places table with a location GEOMETRY(Point, 4326) column, queried with ST_Distance / ordered by proximity.

If you only need a couple of columns, you can also skip the full ORM and define a Doctrine type directly via Doctrine\DBAL\Types\Type::addType(...) inside a service provider — useful when you're migrating an Eloquent app one table at a time.

Implementation Patterns

Entity mapping. Use options to declare the PostGIS type and SRID, because the generic geometry type needs a concrete geometry subtype in the column definition:

#[ORM\Column(type: 'geometry', options: ['geometry_type' => 'Point', 'srid' => 4326])]
private $location;

#[ORM\Column(type: 'geography', options: ['geometry_type' => 'Point', 'srid' => 4326])]
private $area;

geography gives you meters/degrees-aware distance out of the box; geometry gives you a flat Cartesian plane. Pick per column, not per app.

Queries with DQL functions. Register the function set on the connection (either a subset or the whole ST_* group) and use them in createQueryBuilder():

$qb->select('p')
   ->where('ST_DWithin(p.location, ST_GeomFromText(:wkt, 4326), :meters) = true')
   ->setParameter('wkt', 'POINT(13.4 52.5)')
   ->setParameter('meters', 500);

Prefer ST_DWithin(...) over ST_Distance(...) < x — the former uses the GiST index; the latter does not.

Write path. Doctrine will serialize the value you assign through PointType/GeometryType; typically you build WKT strings (ST_GeomFromText) in DQL, or use ST_SetSRID in raw executeStatement() calls for bulk imports.

Platform extension. To make the schema tool emit GEOMETRY(Point, 4326) columns and USING GIST indexes instead of generic varchar/text:

'platform' => Jsor\Doctrine\PostGIS\PostGISPlatform::class,
// or wrap it: new PostGISPlatform(new PostgreSqlPlatform()) in older versions

This is what lets doctrine:migrations:diff produce a correct migration instead of VARCHAR(255) you then have to hand-edit.

Migrations. Register Jsor\Doctrine\PostGIS\Schema\PostGISSchemaManager so SchemaTool knows about spatial indexes and geometry subtypes. Common pattern: generate the migration, then hand-edit the spatial index to CREATE INDEX ... ON places USING GIST (location).

Bulk / analytical queries. For map endpoints, select ST_AsGeoJSON(location) directly and hydrate into arrays — round-tripping geometries through entities is lossy and slower than the DB-side serializer.

Gotchas and Tips

  • SRID consistency. 4326 vs 3857 mismatches are the #1 source of mixed SRID errors. Set SRID explicitly everywhere (ST_GeomFromText(:wkt, 4326), column options['srid']) and never rely on the database default.
  • Laravel Eloquent does not exist here. You cannot mix GeoJSON helpers from this package with Model::where(). Either use Laravel Doctrine (laravel-doctrine/orm), the raw DBAL connection, or keep PostGIS tables in a Doctrine-managed schema and join through a read-only view.
  • Register DQL functions or you'll get cryptic "Unknown function" errors the first time you call ST_AsText. Registering the entire ST_* set is fine; just pin the package version so function signatures don't drift.
  • DBAL version sensitivity. DBAL 3 → 4 changed type registration and platform signatures. If you're on an older Laravel Doctrine stack, check compatibility with your DBAL major before upgrading; the 2025 release targets modern DBAL.
  • Reading geometries back. GeometryType returns WKB by default (a string). If you expected a WKT string or an object, use ST_AsText(location) (or ST_AsEWKT) in your select, or ST_AsGeoJSON. There is no automatic __toString.
  • Indexes are manual. Doctrine's schema tool will not create a GiST index unless you use the schema manager/platform extension. Forgetting this means ST_DWithin queries fall back to sequential scans silently and get slow at a million rows.
  • No precision/scale. precision/scale options on geometry columns are ignored; use the geometry_type and srid options instead.
  • Migrations diff noise. If you don't register the platform (only the types), every doctrine:migrations:diff will try to convert your geometry columns to varchar. Register both or neither.
  • Debugging. Log SQL with DB::listen / Doctrine's SQLLogger, then run the query in psql with EXPLAIN ANALYZE to confirm the GiST index is being used. SELECT postgis_full_version() and SELECT ST_IsValid(...) are your first stops for bad geometries; a 4326 polygon with self-intersections breaks ST_Contains without an obvious error.
  • Extension points. Custom DQL functions subclass Jsor\Doctrine\PostGIS\Functions\FunctionNode — useful when you need a PostGIS 3.x function the package hasn't shipped yet.
  • License / maintenance. MIT, actively released (Oct 2025). It's a well-scoped package; no runtime dependencies beyond Doctrine, so it's safe to vendor in.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin