fundistadi / fundi-postgis
PostGIS spatial types, automatic GiST indexing, and ST_* DQL functions for Doctrine ORM 3 / DBAL 4 on Symfony.
Package info
github.com/fundistadi/fundi-postgis
Type:symfony-bundle
pkg:composer/fundistadi/fundi-postgis
Requires
- php: >=8.4
- doctrine/dbal: ^4.0
- doctrine/doctrine-bundle: ^3.0
- doctrine/orm: ^3.0
- symfony/framework-bundle: ^7.3 || ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.2
This package is auto-updated.
Last update: 2026-08-07 18:38:40 UTC
README
First-class PostGIS support for Doctrine ORM 3 / DBAL 4 on Symfony — spatial
types exchanged as GeoJSON, ST_* DQL functions, automatic GiST indexing, and
typed geometry columns with churn-free migrations. Enable the bundle and go.
Part of the Fundistadi toolset.
Why
Doctrine ships no spatial types, and PostGIS stores every shape as the base geometry
type — so the usual approaches either hand-write DDL or fight migrations:diff churn.
fundi-postgis handles all of it: declare a column, get a GiST-indexed, GeoJSON-friendly,
optionally shape-constrained column with clean diffs.
Requirements
- PHP 8.4+
doctrine/dbal^4,doctrine/orm^3,doctrine/doctrine-bundle^3- Symfony 7.3+ / 8
- PostgreSQL with the PostGIS extension
Install
composer require fundistadi/fundi-postgis
Enable the bundle:
// config/bundles.php return [ // ... FundiStadi\PostGIS\FundiPostGISBundle::class => ['all' => true], ];
That one line registers the spatial types, the DB-type mappings, the ST_* DQL functions,
the USING gist platform middleware, the typmod-aware schema manager, and the auto-GiST
schema listener. Nothing else to configure.
Usage
Columns
use Doctrine\ORM\Mapping as ORM; class Area { // Generic geometry — accepts any shape, SRID 4326, diff-clean. #[ORM\Column(type: 'geometry')] public ?string $footprint = null; // GeoJSON string in, GeoJSON string out // Typed — PostGIS enforces the shape via the typmod, still diff-clean. #[ORM\Column(type: 'multipolygon')] public ?string $boundary = null; // geometry(MultiPolygon,4326) }
Values are plain GeoJSON strings both ways:
$area->boundary = '{"type":"MultiPolygon","coordinates":[[[[36,-4],[37,-4],[37,-3],[36,-3],[36,-4]]]]}'; $em->persist($area); $em->flush(); // $reloaded->boundary === '{"type":"MultiPolygon", ... }'
Available column types: geometry, geography, point, polygon, multipolygon
(more shape sub-types are trivial to add — see src/Types).
Spatial queries (DQL)
$em->createQuery( 'SELECT COUNT(a.id) FROM App\Entity\Area a WHERE ST_Intersects(a.boundary, ST_GeomFromGeoJSON(:poly)) = true' )->setParameter('poly', $geoJsonPolygon)->getSingleScalarResult();
Registered functions: ST_AsGeoJSON, ST_GeomFromGeoJSON, ST_Intersects.
Automatic GiST indexes
Every geometry/geography column gets a USING gist index automatically in the generated
schema and migrations — no hand-written DDL.
Generic vs. typed columns
geometry |
multipolygon (typed) |
|
|---|---|---|
| SRID enforced | ✅ | ✅ |
| Shape enforced by the DB | ❌ (any shape) | ✅ geometry(MultiPolygon,4326) |
migrations:diff clean |
✅ | ✅ (typmod-aware schema manager) |
See docs/geometry-columns.md for the details.
Contributing
See CONTRIBUTING.md. CI enforces the standard (php-cs-fixer, PHPStan max, PHPUnit against real PostGIS) on every pull request.
License
MIT.