Welcome to Doctrine spatial extension’s documentation!

Doctrine spatial extension provides spatial types and spatial functions for doctrine. It allows you to manage spatial entity and to store them into your database server.

Currently, doctrine spatial extension provides two dimension general geometric and geographic spatial types, two-dimension points, linestrings, polygon and two-dimension multi-points, multi-linestrings, multi-polygons. Doctrine spatial is only compatible with MySql and PostgreSql. For better security and better resilience of your spatial data, we recommend that you favor the PostgreSql database server because of the shortcomings and vulnerabilities of MySql.

This project was initially created by Derek J. Lambert in 2015. In March 2020, Alexandre Tranchant forked the originally project because of unactivity for two years. Feel free to contribute. Any help is welcomed:

  • to implement third and fourth dimension in spatial data,

  • to implement new spatial function,

  • to complete documentation and fix typos, (I’m not fluent in english)

  • to implement new abstracted platforms like Microsoft Sql Server.

Contents

Installation

Installation via composer

Add the longitude-one/doctrine-spatial package in your composer.json.

$ composer require longitude-one/doctrine-spatial

Or you can edit directly composer.json file by adding this line on your requirements:

"require": {
    ...
    "longitude-one/doctrine-spatial": "^3.0"

Be careful, the version 3.0.0 will only be available during summer 2021.

Installation without composer

If you’re not using composer, you can download the library. Then copy and paste the lib directory where you store all libraries of your application.

Installation to contribute

If you want to contribute, do not hesitate. Any help is welcomed. The simplest way is too fork the Github project, then to locally clone your forked project. The contribution page explains how to proceed and how to test your updates.

Configuration

Configuration for applications using Symfony framework

To configure Doctrine spatial extension on your Symfony application, you only need to edit your config/doctrine.yaml file. Two steps are sufficient. First step will help you to declare spatial types on DQL. The second step will help you to declare a spatial function.

Declare your geometric types
doctrine:
    dbal:
        types:
            geometry:   LongitudeOne\Spatial\DBAL\Types\GeometryType
            point:      LongitudeOne\Spatial\DBAL\Types\Geometry\PointType
            polygon:    LongitudeOne\Spatial\DBAL\Types\Geometry\PolygonType
            linestring: LongitudeOne\Spatial\DBAL\Types\Geometry\LineStringType

Now, you can create an entity with a geometry, point, polygon and a linestring type.

Here is a complete example of all available types. The names of doctrine types are not hardcoded. So if you only want to use the geometric type, feel free to remove the geometric_ prefixes

doctrine:
    dbal:
        types:
            geography:            LongitudeOne\Spatial\DBAL\Types\GeographyType
            geography_linestring: LongitudeOne\Spatial\DBAL\Types\Geography\LineStringType
            geography_point:      LongitudeOne\Spatial\DBAL\Types\Geography\PointType
            geography_polygon:    LongitudeOne\Spatial\DBAL\Types\Geography\PolygonType

            geometry:            LongitudeOne\Spatial\DBAL\Types\GeometryType
            geometry_linestring: LongitudeOne\Spatial\DBAL\Types\Geometry\LineStringType
            geometry_point:      LongitudeOne\Spatial\DBAL\Types\Geometry\PointType
            geometry_polygon:    LongitudeOne\Spatial\DBAL\Types\Geometry\PolygonType
            geometry_multilinestring: LongitudeOne\Spatial\DBAL\Types\Geometry\MultiLineStringType
            geometry_multipoint:      LongitudeOne\Spatial\DBAL\Types\Geometry\MultiPointType
            geometry_multipolygon:    LongitudeOne\Spatial\DBAL\Types\Geometry\MultiPolygonType

I try to maintain this documentation up-to-date. In any case, the DBAL/Types directory contains all geometric and all geographic available types.

Any help is welcomed to implement the other spatial types declared in the Open Geospatial Consortium standard and in the ISO/IEC 13249-3:2016 like Curve or PolyhedSurface.

Declare a new function
orm:
    dql:
        numeric_functions:
            #Declare functions returning a numeric value
            #A good practice is to prefix functions with ST when they are issue from the Standard directory
            st_area: LongitudeOne\Spatial\ORM\Query\AST\Functions\Standard\StArea
        string_functions:
            #Declare functions returning a string
            st_envelope: LongitudeOne\Spatial\ORM\Query\AST\Functions\Standard\STEnvelope
            #Prefix functions with SP when they are not issue from the Standard directory is a good practice
            sp_asgeojson: LongitudeOne\Spatial\ORM\Query\AST\Functions\Postgresql\SpAsGeoJson
            #You can use the DQL function name you want and then use it in your DQL
            myDQLFunctionAlias: LongitudeOne\Spatial\ORM\Query\AST\Functions\Standard\StCentroid
            #SELECT myDQLFunctionAlias(POLYGON(...

Add only the functions you want to use. The list of available function can be found in these sections:

  1. list of Functions described in OGC Standards or in ISO/IEC 13249-3:2016 declared in the Open Geospatial Consortium standard,

  2. list of Specific functions of the PostgreSql database server which are not already declared in the OGC Standard,

  3. list of Specific functions of the MySql database server which are not already declared in the OGC Standard,

Nota: By default, function declared by the Open Geospatial Consortium in the standards of SQL Options are prefixed by ST_, other functions should not be declared with this prefix. We suggest to use the SP_ prefix (specific).

Configuration for other application

Declare your geometric types

Doctrine allows you to create new mapping types. We used this functionnality to create spatial types in this extension. You only need to let Doctrine know which type you want to use. Two lines are sufficient to do it. The first line calls the Type class. The second line, declare a type. In the below example, we declare a geometric point type.

<?php
// in your bootstrapping code

// ...

use Doctrine\DBAL\Types\Type;

// ...

// Register types provided by the doctrine2 spatial extension
Type::addType('point', 'LongitudeOne\Spatial\DBAL\Types\Geometry\PointType');
Declare a new function

You can register functions of the doctrine spatial extension adding them to the ORM configuration:

<?php

// in your bootstrapping code

// ...

use Doctrine\ORM\Configuration\Doctrine\ORM\Configuration;

// ...

$config = new Configuration();
// This is an example to declare a standard spatial function which is returning a string
$config->addCustomStringFunction('ST_Envelope', 'LongitudeOne\Spatial\ORM\Query\AST\Functions\Standard\StEnvelope');
// This is another example to declare a standard spatial function which is returning a numeric
$config->addCustomNumericFunction('ST_Area', 'LongitudeOne\Spatial\ORM\Query\AST\Functions\Standard\StArea');
// This is another example to declare a Postgresql specific function which is returning a string
$config->addCustomNumericFunction('SP_GeoJson', 'LongitudeOne\Spatial\ORM\Query\AST\Functions\PostgreSql\SpGeoJson');

Create spatial entities

It is a good practice to use the most adapted column to store you geometric or geographic data. If your entity have only to store points, do not use a “geometry” type, but a “point” type. Use a geometry column only if your entity can store different types (points and lines as example)

Example1: Entity with a spatial point

Below, you will find an example to declare an entity with a point. Before you need to declare the point type as described in the configuration section.

<?php

// We declare the class of the type we want to use.
use LongitudeOne\Spatial\PHP\Types\Geometry\Point;
// We declare the Mapping as usual
use Doctrine\ORM\Mapping as ORM;

/**
 * Point entity example.
 *
 * As you can see we do not change anything in Entity and Table annotations. Feel free to use them as usual.
 *
 * @ORM\Entity
 * @ORM\Table
 */
class PointEntity
{
    /**
     * @var int The usual Doctrine Identifier
     *
     *  As you can see we do not change anything in Entity and Table annotations. Feel free to use it as usual.
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @var Point
     *
     * As you can see we declare a point property of type point.
     * point shall be declared in the doctrine.yaml as a custom type.
     * Feel free to use options as usual. As example, I declared that point is not nullable. But you can
     * set it to nullable=true if you want.
     *
     * @ORM\Column(type="point", nullable=false)
     */
    protected $point;

    /**
     * Get id.
     * This is the usual Id getter.
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Get point.
     * This is a standard getter.
     *
     * @return Point
     */
    public function getPoint(): Point
    {
        return $this->point;
    }

    /**
     * Set point.
     * This is a fluent setter.
     *
     * @param Point $point point to set
     *
     * @return self
     */
    public function setPoint(Point $point): self
    {
        $this->point = $point;

        return $this;
    }
}

Seven examples with each geometric spatial types

The Fixtures directory creates some spatial entities for our tests. Inside this directory, you will find a lot of entities which are implementing geometric properties:

Four examples with each geographic spatial types

The Fixtures directory creates some spatial entities for our tests. Inside this directory, you will find a lot of entities which are implementing geographic properties:

Repository

When your spatial entity is created, you can add new methods to your repositories. This section will explain you how to add new methods to a standard repository.

In this example, we assume that a building entity was already created. The building entity owns a spatial property to store polygon. We assume that the entity is named building and that the spatial property is name plan which is a polygon type.

<?php

namespace App\Repository;

use App\Entity\Building; // This is our spatial entity
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * Building repository.
 *
 * These methods inherits from ServiceEntityRepository
 *
 * @method Building|null find($id, $lockMode = null, $lockVersion = null)
 * @method Building|null findOneBy(array $criteria, array $orderBy = null)
 * @method Building[]    findAll()
 * @method Building[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class BuildingRepository extends ServiceEntityRepository
{
    /**
     * BuildingRepository constructor.
     *
     * The repository constructor of a spatial entity is strictly identic to other repositories.
     *
     * @param ManagerRegistry $registry injected by dependency injection
     */
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Building::class);
    }

    // ...

    /**
     * Find building that have an area between min and max .
     *
     * @param float $min the minimum accepted area
     * @param float $max the maximum accepted area
     *
     * @return Building[]
     */
    public function findAreaBetween(float $min, float $max): array
    {
        //The query builder is normally retrieved
        $queryBuilder = $this->createQueryBuilder('b');

        //We assume that the ST_AREA has been declared in configuration
        return $queryBuilder->where('ST_AREA(b.plan) BETWEEN :min AND :max')
            ->setParameter('min', $min, 'float')
            ->setParameter('max', $max, 'float')
            ->getQuery()
            ->getResult()
        ;
    }

Glossary

Types

Types described in OGC Standards or in ISO/IEC 13249-3:2016

The ISO/IEC 13249-3 International Standard defines multimedia and application specific types and their associated routines using the user-defined features in ISO/IEC 9075. The third part of ISO/IEC 13249 defines spatial user-defined types and their associated routines.

In doctrine spatial extensions, some of all normalized spatial user-defined types are implemented.

This section lists them.

Spatial types

COORDINATES

Implemented

MySql

PostgreSql

Geometric

X, Y

YES

YES

YES

Point

X, Y

YES

YES

YES

LineString

X, Y

YES

YES

YES

Polygon

X, Y

YES

YES

YES

MultiPoint

X, Y

YES

YES

YES

MultiLineString

X, Y

YES

YES

YES

MultiPolygon

X, Y

YES

YES

YES

GeomCollection

X, Y

NO

Curve

X, Y

NO

Surface

X, Y

NO

PolyHedralSurface

X, Y

NO

GeometricZ

X, Y, Z

NO

PointZ

X, Y, Z

NO

LineStringZ

X, Y, Z

NO

PolygonZ

X, Y, Z

NO

MultiPointZ

X, Y, Z

NO

MultiLineStringZ

X, Y, Z

NO

MultiPolygonZ

X, Y, Z

NO

GeomCollectionZ

X, Y, Z

NO

CurveZ

X, Y, Z

NO

SurfaceZ

X, Y, Z

NO

PolyHedralSurfaceZ

X, Y, Z

NO

GeometricM

X, Y, M

NO

PointM

X, Y, M

NO

LineStringM

X, Y, M

NO

PolygonM

X, Y, M

NO

MultiPointM

X, Y, M

NO

MultiLineStringM

X, Y, M

NO

MultiPolygonM

X, Y, M

NO

GeomCollectionM

X, Y, M

NO

CurveM

X, Y, M

NO

SurfaceM

X, Y, M

NO

PolyHedralSurfaceM

X, Y, M

NO

GeometricZM

X, Y, Z, M

NO

PointZM

X, Y, Z, M

NO

LineStringZM

X, Y, Z, M

NO

PolygonZM

X, Y, Z, M

NO

MultiPointZM

X, Y, Z, M

NO

MultiLineStringZM

X, Y, Z, M

NO

MultiPolygonZM

X, Y, Z, M

NO

GeomCollectionZM

X, Y, Z, M

NO

CurveZM

X, Y, Z, M

NO

SurfaceZM

X, Y, Z, M

NO

PolyHedralSurfaceZM

X, Y, Z, M

NO

Functions

Functions described in OGC Standards or in ISO/IEC 13249-3:2016

The ISO/IEC 13249-3 International Standard defines multimedia and application specific types and their associated routines using the user-defined features in ISO/IEC 9075. The third part of ISO/IEC 13249 defines spatial user-defined types and their associated routines.

Associated routines of this document are considered as the “Standard functions” for this doctrine spatial extension. I try to maintain this documentation up-to-date. In any case, you will find under the Functions/Standards directory a set of classes. Each class implement the spatial function of the same name.

The below table shows the defined functions:

Spatial functions

Implemented

Type

MySql

PostgreSql

ST_Area

YES

Numeric

YES

YES

ST_AsBinary

YES

String

YES

YES

ST_Boundary

YES

String

YES

YES

ST_Buffer

YES

Numeric

NO*

YES

ST_Centroid

YES

String

YES

YES

ST_Contains

YES

Numeric

YES

YES

ST_ConvexHull

YES

String

NO

YES

ST_Crosses

YES

Numeric

YES

YES

ST_Difference

YES

String

YES

YES

ST_Dimension

YES

Numeric

YES

YES

ST_Disjoint

YES

Numeric

YES

YES

ST_Distance

YES

Numeric

NO*

YES

ST_Equals

YES

Numeric

YES

YES

ST_Intersects

YES

Numeric

YES

YES

ST_Intersection

YES

String

YES

YES

ST_IsClosed

YES

Numeric

YES

YES

ST_IsEmpty

YES

Numeric

YES

YES

ST_IsRing

YES

Numeric

NO

YES

ST_IsSimple

YES

Numeric

YES

YES

ST_EndPoint

YES

String

YES

YES

ST_Envelope

YES

String

YES

YES

ST_ExteriorRing

YES

String

YES

YES

ST_GeometryN

YES

String

YES

YES

ST_GeometryN

YES

String

YES

YES

ST_EndPoint

YES

String

YES

YES

ST_GeometryType

YES

Numeric

NO*

YES

ST_GeomFromWkb

YES

String

YES

YES

ST_GeomFromText

YES

String

YES

YES

ST_InteriorRingN

YES

String

YES

YES

ST_Length

YES

Numeric

YES

YES

ST_LineStringFromWkb

YES

String

YES

YES

ST_MPointFromWkb

YES

String

YES

YES

ST_MLineFromWkb

YES

String

YES

YES

ST_MPolyFromWkb

YES

String

YES

YES

ST_NumInteriorRing

YES

String

YES

YES

ST_NumGeometries

YES

String

YES

YES

ST_NumPoints

YES

String

YES

YES

ST_Overlaps

YES

String

YES

YES

ST_Perimeter

YES

String

YES

YES

ST_Point

YES

String

YES

YES

ST_PointFromWkb

YES

String

YES

YES

ST_PointN

YES

String

YES

YES

ST_PointOnSurface

YES

String

NO

YES

ST_PolyFromWkb

YES

String

YES

YES

ST_Relate

YES

String

YES

YES

ST_SetSRID

YES

Numeric

YES

YES

ST_StartPoint

YES

Numeric

YES

YES

ST_SymDifference

YES

String

YES

YES

ST_Touches

YES

Numeric

YES

YES

ST_Union

YES

String

YES

YES

ST_Within

YES

Numeric

YES

YES

ST_X

YES

Numeric

YES

YES

ST_Y

YES

Numeric

YES

YES

Specific functions of the PostgreSql database server

If your application can be used with another database server than PostgreSql, you should avoid to use these functions. It’s a good practice to name function with the SP prefix, but do not forget that you can name all functions as you want when you declare it into your configuration files or in your bootstrap.

Specific PostgreSQL Spatial functions

Implemented

Type

Sp_AsGeoJson

YES

String

Sp_Azimuth

YES

String

Sp_ClosestPoint

YES

String

Sp_Collect

YES

String

Sp_ContainsProperly

YES

Numeric

Sp_CoveredBy

YES

Numeric

Sp_Covers

YES

Numeric

Sp_Distance_Sphere

YES

Numeric

Sp_DWithin

YES

Numeric

Sp_Expand

YES

Numeric

Sp_GeogFromText

YES

String

Sp_GeographyFromText

YES

String

Sp_GeomFromEwkt

YES

Numeric

Sp_GeometryType

YES

Numeric

Sp_LineCrossingDirection

YES

Numeric

Sp_LineSubstring

YES

Numeric

Sp_LineLocatePoint

YES

Numeric

Sp_LineInterpolatePoint

YES

String

Sp_MakeEnvelope

YES

String

Sp_MakeBox2D

YES

String

Sp_MakeLine

YES

String

Sp_MakePoint

YES

String

Sp_NPoints

YES

Numeric

Sp_Scale

YES

Numeric

Sp_Simplify

YES

Numeric

Sp_Split

YES

Numeric

Sp_SnapToGrid

YES

String

Sp_Summary

YES

String

Sp_Transform

YES

Numeric

Sp_Translate

YES

Numeric

Specific functions of the MySql database server

If your application can be used with another database server than MySql, you should avoid to use these functions.

Specific MySQL Spatial functions

Implemented

Type

Sp_Distance

YES

Numeric

Sp_Buffer

YES

Numeric

Sp_BufferStrategy

YES

Numeric

Sp_GeometryType

YES

Numeric

Sp_LineString

YES

Numeric

Sp_MBRContains

YES

Numeric

Sp_MBRDisjoint

YES

Numeric

Sp_MBREquals

YES

Numeric

Sp_MBRDisjoint

YES

Numeric

Sp_MBRIntersects

YES

Numeric

Sp_MBROverlaps

YES

Numeric

Sp_MBRTouches

YES

Numeric

Sp_MBRWithin

YES

Numeric

Sp_Point

YES

Numeric

Nota: Since MySql 5.7, a lot of functions are deprecated. These functions have been removed from doctrine spatial extensions, because they are replaced by their new names. As example, the GeomFromText function does no more exist. It has been replaced by the Standard function ST_GeomFromText since MySql 5.7. So if you was using GeomFromText, removed it and use the standard function declared in the StGeomFromText class.

Contributing

Documentation

This documentation is done with sphinx. All documentation are stored in the docs directory. To contribute to this documentation (and fix the lot of typo), you need to install python, sphinx and the “readthedocs” template.

  1. Fork this project,

  2. Locally clone your forked project,

  3. Edit files in the docs directory

  4. Launch the make html

  5. Verify that documentation is improved

  6. Commit your contribution with an explicit message

  7. Push your commit on your forked project,

  8. Do a pull request on your forked project to the longitude-one/doctrine-spatial project

Source code

How to create a new function?

It’s pretty easy to create a new function. A few lines code are sufficient.

Where to store your class?

If your function is described in the OGC Standards or in the ISO/IEC 13249-3, the class implementing the function shall be create in the lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/Standard directory.

If your spatial function is not described in the OGC Standards nor in the ISO, your class should be prefixed by Sp (specific). If your class is specific to MySql, you shall create it in the lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/MySql directory. If your class is specific to PostgreSQL, you shall create it in the lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/PostgreSql directory. If your class is not described in the OGC Standards nor in the ISO norm, but exists in MySQL and in PostgreSQL, accepts the same number of arguments and returns the same results (which is rarely the case), then you shall create it in the lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/Common directory.

Which name for your function?

Create a new class. It’s name shall be the same than the function name in camel case prefixed with St or Sp. The standards are alive, they can be updated at any time. Regulary, new spatial function are defined by consortium. So, to avoid that a new standardized function as the same name from an existing function, the St prefix is reserved to already standardized function.

If your function is described in the OGC Standards or in the ISO/IEC 13249-3, the prefix shall be St else your class shall be prefixed with SP. As example, if you want to create the spatial ST_Z function, your class shall be named StZ in the Standard directory. If you want to create the ST_Polygonize PostgreSql function which is not referenced in the OGC nor in, then you shall name your class SpPolygonize and store them in the PostgreSql directory.

Which method to implements?

Now you know where to create your class, it should extends AbstractSpatialDQLFunction and you have to implement four functions:

  1. getFunctionName() shall return the SQL function name,

  2. getMaxParameter() shall return the maximum number of arguments accepted by the function,

  3. getMinParameter() shall return the minimum number of arguments accepted by the function,

  4. getPlatforms() shall return an array of each platform accepting this function.

As example, if the new spatial function exists in PostgreSQL and in MySQL, getPlatforms() should be like this:

<?php

// ...

/**
 * Get the platforms accepted.
 *
 * @return string[] a non-empty array of accepted platforms
 */
protected function getPlatforms(): array
{
    return ['postgresql', 'mysql'];
}

Do not hesitate to copy and paste the implementing code of an existing spatial function.

If your function is more specific and need to be parse, you can overload the parse method. The PostgreSQL SnapToGrid can be used as example.

All done! Your function is ready to used, but, please, read the next section to implement tests.

How to test your new function?

Please, create a functional test in the same way. You have a lot of example in the functions test directory.

Setup

Here is an example of setup, each line is commented to help you to understand how to setup your test.

<?php

use LongitudeOne\Spatial\Exception\InvalidValueException;
use LongitudeOne\Spatial\Exception\UnsupportedPlatformException;
use LongitudeOne\Spatial\Tests\Helper\PointHelperTrait;
use LongitudeOne\Spatial\Tests\OrmTestCase;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\ORMException;

/**
 * Foo DQL functions tests.
 * Thes tests verify their implementation in doctrine spatial.
 *
 * @author  Alexandre Tranchant <alexandre.tranchant@gmail.com>
 * @license https://alexandre-tranchant.mit-license.org MIT
 *
 * Please prevers the three above annotation.
 *
 * Group is used to exclude some tests on some environment.
 * Internal is to avoid the use of the test outer of this library
 * CoversDefaultClass is to avoid that your test covers other class than your new class
 *
 * @group dql
 *
 * @internal
 * @coversDefaultClass
 */
class SpFooTest extends OrmTestCase
{
    // To help you to create some geometry, I created some Trait.
    // use it to be able to call some methods which will store geometry into your database
    // In this example, we use a trait that will create some points.
    use PointHelperTrait;

    /**
     * Setup the function type test.
     *
     * @throws Exception                when connection failed
     * @throws ORMException                 when cache is not set
     * @throws UnsupportedPlatformException when platform is unsupported
     */
    protected function setUp(): void
    {
        //If you create point entity in your test, you shall add the line above or the **next** test will failed
        $this->usesEntity(self::POINT_ENTITY);
        //If the method exists in mysql, You shall test it. Comment this line if function does not exists on MySQL
        $this->supportsPlatform('mysql');
        //If the method exists in postgresql, You shall test it. Comment this line if function does not exists on PostgreSql
        $this->supportsPlatform('postgresql');

        parent::setUp();
    }

    /**
     * Test a DQL containing function to test in the select.
     *
     * @throws Exception                when connection failed
     * @throws ORMException                 when cache is not set
     * @throws UnsupportedPlatformException when platform is unsupported
     * @throws InvalidValueException        when geometries are not valid
     *
     * @group geometry
     */
    public function testSelectSpBuffer()
    {
        //The above protected method come from the point helper trait.
        $pointO = $this->createPointO();
        //Please do not forget to flush and clear cache
        $this->getEntityManager()->flush();
        $this->getEntityManager()->clear();

        //We create a query using your new DQL function SpFoo
        $query = $this->getEntityManager()->createQuery(
            'SELECT p, ST_AsText(SpFoo(p.point, :p) FROM LongitudeOne\Spatial\Tests\Fixtures\PointEntity p'
        );
        //Optionnaly, you can use parameter
        $query->setParameter('p', 'bar', 'string');
        //We retrieve the result
        $result = $query->getResult();

        //Now we test the result
        static::assertCount(1, $result);
        static::assertEquals($pointO, $result[0][0]);
        static::assertSame('POLYGON((-4 -4,4 -4,4 4,-4 4,-4 -4))', $result[0][1]);
    }

Now, open the OrmTestCase.php file] and declare your function in one of this three methods:

  • addStandardFunctions

  • addMySqlFunctions

  • addPostgreSqlFunctions

You can launch the test. This document helps you how to config your dev environment. Please do not forgot to update documentation by adding your function in one of these three tables:

Quality of your code

Quality of code is auto-verified by php-cs-fixer, php code sniffer and php mess detector.

Before a commit, launch the quality script:

composer check-quality-code

You can launch PHPCS-FIXER to fix errors with:

composer phpcsfixer

You can launch PHP Code Sniffer only with: .. code-block:: bash

composer phpcs

You can launch PHP Mess Detector only with:

composer phpmd

Test environment

If you want to contribute to this library, you’re welcome. This section will help you to prepare your development environment.

How to test library?

Doctrine library is available for MySQL and PostGreSQL. Be aware that MariaDB spatial functions does not returns the same results than MySQL spatial functions. Then tests failed on MariaDB. So do not use MariaDB to test MySQL abstraction.

How to test on MySQL?

I suppose that composer and MySQL are already installed on your dev environment.

  1. Create a role that can create database and locally connect with a password,

  2. Create a phpunit.mysql.xml file copied from phpunit.mysql.xml.dist file,

  3. Edit this phpunit.mysql.xml to change connection parameters.

  4. run the command composer test-mysql

How to test on PostgreSQL?

I supposed that composer, PgSQL and its Postgis extension are installed.

  1. Create a role that is a superuser because this user will create a database and create postgis extension,

  2. Create a phpunit.pgsql.xml file copied from phpunit.pgsql.xml.dist file,

  3. Edit this phpunit.pgsql.xml to change connection parameters.

  4. run the command composer test-pgsql

How to test with the three PHP versions?

This library is available for PHP7.2, PHP7.3 and PHP7.4 versions. So you have to test library with this three versions.

If you use an IDE like PHPStorm, you can create configurations to launch the six tests suite with the corresponding to:

  • MySQL, PHP7.2 and PHPUnit 8.5

  • PostgreSQL, PHP7.2 and PHPUnit

  • MySQL, PHP7.3 and PHPUnit 9.0

  • PostgreSQL, PHP7.3 and PHPUnit

  • MySQL, PHP7.4 and PHPUnit 9.0

  • PostgreSQL, PHP7.4 and PHPUnit

In this section, I described an easy way to switch PHP version via console. (But there are a lot of ways to do it.)

I suppose you have installed all php versions on your dev environment.

  1. Download symfony binary,

  2. Verify that PHP7.2,PHP7.3 and PHP7.4 are available:

 symfony local:php:list
┌─────────┬────────────────────────────────┬─────────┬─────────┬─────────────┬─────────┬─────────┐
│ Version │           Directory            │ PHP CLI │ PHP FPM │   PHP CGI   │ Server  │ System? │
├─────────┼────────────────────────────────┼─────────┼─────────┼─────────────┼─────────┼─────────┤
│ 7.1.30  │ C:\Users\alexandre\Php\php-7.1 │ php.exe │         │ php-cgi.exe │ PHP CGI │         │
│ 7.2.25  │ C:\Users\alexandre\Php\php-7.2 │ php.exe │         │ php-cgi.exe │ PHP CGI │         │
│ 7.3.12  │ C:\Users\alexandre\Php\php-7.3 │ php.exe │         │ php-cgi.exe │ PHP CGI │         │
│ 7.4.1   │ C:\Users\alexandre\Php\php-7.4 │ php.exe │         │ php-cgi.exe │ PHP CGI │ *       │
└─────────┴────────────────────────────────┴─────────┴─────────┴─────────────┴─────────┴─────────┘
  1. Create a .php-version containing the PHP version to change php version

echo 7.2 > .php-version

Now PHP 7.2 will be used each time you use one of this command:

$ symfony php -v
PHP 7.2.26 (cli) (built: Dec 17 2019 15:29:44) ( NTS MSVC15 (Visual C++ 2017) x64 )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Xdebug v2.9.0, Copyright (c) 2002-2019, by Derick Rethans

$ symfony composer
  1. Download PHPUnit.phar for version 8 and version 9: Go on https://phar.phpunit.de

  2. You should now have a phpunit-8.phar and a phpunit-9.phar in your directory

  3. This script launch the six test-suites:

$ echo 7.2 > .php-version
$ symfony php phpunit-8.phar --configuration phpunit.mysql.xml
$ symfony php phpunit-8.phar --configuration phpunit.pgsql.xml
$ echo 7.3 > .php-version
$ symfony php phpunit-9.phar --configuration phpunit.mysql.xml
$ symfony php phpunit-9.phar --configuration phpunit.pgsql.xml
$ echo 7.4 > .php-version
$ symfony php phpunit-9.phar --configuration phpunit.mysql.xml
$ symfony php phpunit-9.phar --configuration phpunit.pgsql.xml