---
title: Contributing to Doctrine Spatial
description: Doctrine Spatial 6.x contributing guide, migrated from the collected library documentation.
canonical_html: ./contributing.html
canonical_markdown: ./contributing.md
---

[View HTML version](./contributing.html)

> **Current version** — This documentation describes the current major version of `doctrine-spatial`.
> [View the library roadmap](../index.md)

Welcome to the contribution guide for Doctrine Spatial! This document provides comprehensive instructions and guidelines for those interested in
making valuable contributions to the project's development and enhancement. We encourage participation from all levels of expertise, from beginners to
seasoned developers.

## Contributing Avenues

The Doctrine Spatial project welcomes contributions in two primary areas:

1. **Documentation Enhancement:** Improve the project's documentation by addressing typos, clarifying instructions, adding helpful examples, or
   translating documentation into other languages. This helps ensure that users have clear and accessible information to guide them effectively in
   using Doctrine Spatial.

1. **Codebase Improvement:** Contribute to the project's codebase by fixing bugs, adding new features, optimizing performance, or enhancing existing
   functionalities. This direct involvement in the code helps maintain and strengthen Doctrine Spatial's core capabilities.

## Documentation

The documentation is built with Sphinx and its sources are stored in the `docs` directory. To preview changes locally, set `APP_FOLDER` as described
in [the test environment guide](test-environment.md) and start the `service_doc` Compose service:

```bash
cd docker
docker-compose up -d service_doc
```

Open <http://localhost:8800>. The `spatial-doc` container runs `sphinx-autobuild` and reloads the site after changes to `.rst` files.

1. Edit files in the `docs` directory,
1. Verify that documentation is improved,
1. Commit your contribution with an explicit message,
1. Push your commit and create a pull request 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][OGC Standards] or in the [ISO/IEC 13249-3][ISO/IEC 13249-3], the class implementing the function
**shall** be create in the lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/[Standard directory][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][MySql directory]. If your class is
specific to PostgreSQL, you shall create it in the lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/[PostgreSql directory][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][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. Regularly, 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][OGC Standards] or in the [ISO/IEC 13249-3][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][Standard directory].
If you want to create the [ST_Polygonize][ST_Polygonize] PostgreSql function which is not referenced in the OGC nor in ISO, then you shall name your
class `SpPolygonize` and store them in the [PostgreSql directory][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,
1. `getMaxParameter()` shall return the maximum number of arguments accepted by the function,
1. `getMinParameter()` shall return the minimum number of arguments accepted by the function,
1. `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
<?php

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;

/**
 * Get the platforms accepted.
 *
 * @return class-string<AbstractPlatform>[] a non-empty array of accepted platforms
 */
protected function getPlatforms(): array
{
    return [PostgreSQLPlatform::class, MySQLPlatform::class];
}
```

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][SnapToGrid] can be used as
example.

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

Don't forget to check your code respect our standard of quality:

```bash
docker exec spatial-php8 composer quality
```

### 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][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
<?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\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\ORM\Exception\ORMException;
use PHPUnit\Framework\Attributes\CoversClass;

/**
 * Foo DQL functions tests.
 * These tests verify their implementation in doctrine spatial.
 *
 * @author  Alexandre Tranchant <alexandre.tranchant@gmail.com>
 * @license https://alexandre-tranchant.mit-license.org MIT
 *
 * Please preserve 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
 * CoversClass documents the production class exercised by this test.
 *
 * @group dql
 *
 * @internal
 */
#[CoversClass(SpFoo::class)]
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.
     */
    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 function is available on MySQL, test it there.
        $this->supportsPlatform(MySQLPlatform::class);
        // If the function is available on PostgreSQL, test it there.
        $this->supportsPlatform(PostgreSQLPlatform::class);

        parent::setUp();
    }

    /**
     * Test a DQL containing function to test in the select.
     */
    public function testSelectSpBuffer()
    {
        //The above protected method come from the point helper trait.
        //It creates a point at origin (0 0) and persist it in database
        $pointO = $this->persistPointOrigin();

        //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][OrmTestCase.php file] and declare your function in the appropriate registration method:

- `addStandardFunctions`
- `addSpecificMariaDbFunctions`
- `addSpecificMySqlFunctions`
- `addSpecificPostgreSqlFunctions`
- `addSpecificSqlServerFunctions`

You can launch the test. This [document](test-environment.md) helps you how to config your dev environment. Update the relevant table in [the
glossary](glossary.md). If the function is specific to a platform not yet covered there, add a platform-specific section.

## Quality of your code

Code quality is checked by PHP-CS-Fixer, PHPStan, PHPMD, and PHP_CodeSniffer.

Before a commit, install the quality scripts:

```bash
docker exec spatial-php8 composer install --working-dir=quality/php-cs-fixer
docker exec spatial-php8 composer install --working-dir=quality/php-code-sniffer
docker exec spatial-php8 composer install --working-dir=quality/php-mess-detector
docker exec spatial-php8 composer install --working-dir=quality/php-stan
```

Then, you can check the quality of your code with:

```bash
docker exec spatial-php8 quality/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=quality/php-cs-fixer/.php-cs-fixer.php --dry-run --allow-risky=yes
docker exec spatial-php8 quality/php-stan/vendor/bin/phpstan analyse --configuration=quality/php-stan/php-stan.neon lib tests --error-format=table --no-progress --no-interaction --no-ansi --level=9 --memory-limit=256M
docker exec spatial-php8 quality/php-mess-detector/vendor/bin/phpmd lib text quality/php-mess-detector/ruleset.xml
docker exec spatial-php8 quality/php-mess-detector/vendor/bin/phpmd tests text quality/php-mess-detector/test-ruleset.xml
docker exec spatial-php8 quality/php-code-sniffer/vendor/bin/phpcs --standard=quality/php-code-sniffer/phpcs.xml -s
```

You can launch PHPCS-FIXER to fix errors with:

```bash
docker exec spatial-php8 quality/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=quality/php-cs-fixer/.php-cs-fixer.php --allow-risky=yes
```

Fix all errors, then commit your code.

[Common directory]: https://github.com/longitude-one/doctrine-spatial/tree/main/lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/Common

[MySql directory]: https://github.com/longitude-one/doctrine-spatial/tree/main/lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/MySql

[PostgreSql directory]: https://github.com/longitude-one/doctrine-spatial/tree/main/lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/PostgreSql

[Standard directory]: https://github.com/longitude-one/doctrine-spatial/tree/main/lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/Standard

[ISO/IEC 13249-3]: https://www.iso.org/standard/60343.html

[OGC standards]: https://www.ogc.org/standards/sfs

[ST_Polygonize]: https://postgis.net/docs/manual-2.5/ST_Polygonize.html

[SnapToGrid]: https://github.com/longitude-one/doctrine-spatial/tree/main/lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/PostgreSql/SpSnapToGrid.php

[functions test directory]: https://github.com/longitude-one/doctrine-spatial/tree/main/tests/LongitudeOne/Spatial/ORM/Query/AST/Functions/

[OrmTestCase.php file]: https://github.com/longitude-one/doctrine-spatial/blob/main/tests/LongitudeOne/Spatial/Tests/OrmTestCase.php
