Installation
Add the package to composer.json with the provided repository config:
composer require bnza/spgsp --dev
(Note: The README references pbald/spgsp—verify the correct repo URL in your project.)
Doctrine Configuration
Register the SPGSP\Doctrine\DBAL\Types\PostgisType in your config/database.php under the doctrine/dbal connection:
'types' => [
'spatial' => SPGSP\Doctrine\DBAL\Types\PostgisType::class,
],
First Use Case: Define a Spatial Field
In an Entity, map a PostGIS-compatible column (e.g., POINT, POLYGON):
use Doctrine\ORM\Mapping as ORM;
use SPGSP\Doctrine\DBAL\Types\PostgisType;
#[ORM\Entity]
class Location
{
#[ORM\Column(type: 'spatial')]
private $coordinates;
}
Database Schema Ensure your PostgreSQL database has PostGIS enabled:
CREATE EXTENSION postgis;
Use DQL with PostGIS functions (e.g., ST_Distance, ST_Intersects):
$locations = $entityManager->createQuery(
'SELECT l FROM App\Entity\Location l
WHERE ST_DWithin(l.coordinates, :point, 1000)'
)->setParameter('point', $searchPoint)->getResult();
Leverage PostGIS functions directly in queries:
// Buffer a point
$buffered = $entityManager->createQuery(
'SELECT ST_AsText(ST_Buffer(l.coordinates, 500)) FROM App\Entity\Location l'
)->getSingleScalarResult();
Convert between WKT (Well-Known Text) and objects:
// WKT to object
$point = PostgisType::convertToPHP('POINT(1 2)', $platform);
// Object to WKT
$wkt = PostgisType::convertToDatabaseValue($point, $platform);
Add custom spatial queries to repositories:
public function findNearby($latitude, $longitude, $radius)
{
$point = "SRID=4326;POINT($longitude $latitude)";
return $this->createQueryBuilder('l')
->where("ST_DWithin(l.coordinates, :point, :radius)")
->setParameter('point', $point)
->setParameter('radius', $radius)
->getQuery()
->getResult();
}
Service Provider Binding
Bind the PostgisType in a service provider for global use:
$this->app->bind('spatial_type', function () {
return new PostgisType();
});
Query Builder Extensions Extend Laravel’s query builder for spatial methods:
use Illuminate\Database\Query\Builder;
Builder::macro('near', function ($field, $latitude, $longitude, $radius) {
$point = "SRID=4326;POINT($longitude $latitude)";
return $this->whereRaw("ST_DWithin($field, ST_GeomFromText(:point), :radius)", [
'point' => $point,
'radius' => $radius,
]);
});
CREATE INDEX idx_location_coordinates ON locations USING GIST(coordinates);
SRID=4326 for WGS84) to avoid implicit conversions.doctrine/dbal).beberlei/doctrineextensions (active PostGIS support).spatial type may clash with Laravel’s default json/array types.
#[ORM\Column(type: 'spatial', options: ['spatial_type' => true])]
POINT(1) vs. POINT(1 2)) cause exceptions.
if (!PostgisType::checkType($wktString)) {
throw new \InvalidArgumentException("Invalid WKT format");
}
SELECT PostGIS_version();
Add to config/logging.php:
'channels' => [
'doctrine' => [
'driver' => 'single',
'path' => storage_path('logs/doctrine.log'),
'level' => 'debug',
],
],
Then log queries in a service provider:
$eventManager->addEventListener(
ORM\Events::onFlush,
function (ORM\Event\OnFlushEventArgs $args) {
$em = $args->getEntityManager();
$em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
}
);
Start with basic ST_AsText() to verify connectivity:
$wkt = $entityManager->createQuery(
'SELECT ST_AsText(l.coordinates) FROM App\Entity\Location l'
)->getSingleScalarResult();
Extend the type to support additional functions:
class CustomPostgisType extends PostgisType
{
public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{
return 'GEOMETRY'; // Override for custom types
}
public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform)
{
return "ST_AsText($sqlExpr)"; // Custom conversion
}
}
Create a trait for Eloquent models:
trait SpatialTrait
{
public function scopeNear($query, $latitude, $longitude, $radius)
{
$point = "SRID=4326;POINT($longitude $latitude)";
return $query->whereRaw("ST_DWithin({$this->getTable()}.coordinates, ST_GeomFromText(:point), :radius)", [
'point' => $point,
'radius' => $radius,
]);
}
}
Combine with a geocoding service (e.g., Google Maps API) to convert addresses to WKT:
public function geocodeAndStore($address)
{
$coordinates = $this->geocodeService->getCoordinates($address);
$this->coordinates = "SRID=4326;POINT($coordinates['lon'] $coordinates['lat'])";
$this->save();
}
How can I help you explore Laravel packages today?