Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DBAL v4, Middleware, Driver, Platform, and SchemaManager implementation #61

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,37 @@ for all available versions.
Setup
--

To use the library with the Doctrine ORM, register the
`ORMSchemaEventSubscriber` event subscriber.
Basic setup requires registering Middleware and the SchemaManagerFactory via the
DBAL connection configuration.

```php
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\PgSQL\Driver;
use Jsor\Doctrine\PostGIS\Driver\Middleware;
use Jsor\Doctrine\PostGIS\Event\ORMSchemaEventSubscriber;
use Jsor\Doctrine\PostGIS\Schema\SchemaManagerFactory;

$entityManager->getEventManager()->addEventSubscriber(new ORMSchemaEventSubscriber());
$params = [
// your connection parameters …
];
$config = new Configuration();
$config->setMiddlewares([new Middleware]);
$config->setSchemaManagerFactory(new SchemaManagerFactory());

$connection = new Connection($params, new Driver(), $config);
```

To use it with the DBAL only, register the `DBALSchemaEventSubscriber` event
subscriber.

Additionally, to also use the library with the Doctrine ORM, register the
`ORMSchemaEventListener` event subscriber.

```php
use Jsor\Doctrine\PostGIS\Event\DBALSchemaEventSubscriber;
use Jsor\Doctrine\PostGIS\Event\ORMSchemaEventListener;

$connection->getEventManager()->addEventSubscriber(new DBALSchemaEventSubscriber());
$entityManager->getEventManager()->addEventSubscriber(new ORMSchemaEventSubscriber());
```

### Symfony

For integrating this library into a Symfony project, read the dedicated
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
],
"require": {
"php": "^8.0",
"doctrine/dbal": "^2.13 || ^3.1"
"doctrine/dbal": "^3.2"
},
"require-dev": {
"doctrine/orm": "^2.9",
Expand Down
22 changes: 10 additions & 12 deletions docs/symfony.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,21 @@ Setup

To use the library with the Doctrine ORM (version 2.9 or higher is supported),
register a [Doctrine event subscriber](https://symfony.com/doc/current/doctrine/event_listeners_subscribers.html)
in `config/services.yml`.
in `config/packages/jsor_doctrine_postgis.yaml`.

```yaml
services:
Jsor\Doctrine\PostGIS\Event\ORMSchemaEventSubscriber:
tags:
- { name: doctrine.event_subscriber, connection: default }
```
Jsor\Doctrine\PostGIS\Schema\SchemaManagerFactory:

The library can also be used with DBAL only (versions 2.13 or higher and 3.1 or
higher are supported).
Jsor\Doctrine\PostGIS\Event\ORMSchemaEventListener:
tags: [{ name: doctrine.event_listener, event: postGenerateSchemaTable, connection: default }]

```yaml
services:
Jsor\Doctrine\PostGIS\Event\DBALSchemaEventSubscriber:
tags:
- { name: doctrine.event_subscriber, connection: default }
Jsor\Doctrine\PostGIS\Driver\Middleware:
tags: [ doctrine.middleware ]

doctrine:
dbal:
schema_manager_factory: Jsor\Doctrine\PostGIS\Schema\SchemaManagerFactory
```

### Database Types
Expand Down
65 changes: 65 additions & 0 deletions src/Driver/Driver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Jsor\Doctrine\PostGIS\Driver;

use Doctrine\DBAL;
use Doctrine\DBAL\Connection\StaticServerVersionProvider;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\ServerVersionProvider;
use Doctrine\DBAL\Types\Type;
use Jsor\Doctrine\PostGIS\Types\GeographyType;
use Jsor\Doctrine\PostGIS\Types\GeometryType;
use Jsor\Doctrine\PostGIS\Types\PostGISType;
use Jsor\Doctrine\PostGIS\Utils\Doctrine;

final class Driver extends AbstractPostgreSQLDriver
{
private DBAL\Driver $decorated;

public function __construct(DBAL\Driver $decorated)
{
$this->decorated = $decorated;
}

public function connect(array $params): DBAL\Driver\Connection
{
$connection = $this->decorated->connect($params);
if (!Type::hasType(PostGISType::GEOMETRY)) {
Type::addType(PostGISType::GEOMETRY, GeometryType::class);
}

if (!Type::hasType(PostGISType::GEOGRAPHY)) {
Type::addType(PostGISType::GEOGRAPHY, GeographyType::class);
}

return $connection;
}

public function getDatabasePlatform(?ServerVersionProvider $versionProvider = null): PostgreSQLPlatform
{
return new PostGISPlatform();
}

/**
* @param string $version
*/
public function createDatabasePlatformForVersion($version): AbstractPlatform
{
// Remove when DBAL v3 support is dropped.
if (Doctrine::isV3()) {
return $this->getDatabasePlatform();
}

return $this->getDatabasePlatform(new StaticServerVersionProvider($version));
}

public function getExceptionConverter(): ExceptionConverter
{
return $this->decorated->getExceptionConverter();
}
}
15 changes: 15 additions & 0 deletions src/Driver/Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Jsor\Doctrine\PostGIS\Driver;

use Doctrine\DBAL;

final class Middleware implements DBAL\Driver\Middleware
{
public function wrap(DBAL\Driver $driver): DBAL\Driver
{
return new Driver($driver);
}
}
139 changes: 139 additions & 0 deletions src/Driver/PostGISPlatform.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

declare(strict_types=1);

namespace Jsor\Doctrine\PostGIS\Driver;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\PostgreSQLSchemaManager;
use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
use Jsor\Doctrine\PostGIS\Schema\SchemaManager;
use Jsor\Doctrine\PostGIS\Schema\SpatialIndexes;
use Jsor\Doctrine\PostGIS\Schema\SpatialIndexSqlGenerator;

final class PostGISPlatform extends PostgreSQLPlatform
{
public function createSchemaManager(Connection $connection): PostgreSQLSchemaManager
{
/** @var PostgreSQLPlatform $platform */
$platform = $connection->getDatabasePlatform();

return new SchemaManager($connection, $platform);
}

public function getAlterSchemaSQL(SchemaDiff $diff): array
{
$sql = parent::getAlterSchemaSQL(SpatialIndexes::filterSchemaDiff($diff));

$spatialIndexSqlGenerator = new SpatialIndexSqlGenerator($this);

foreach ($diff->getAlteredTables() as $tableDiff) {
$table = $tableDiff->getOldTable();

SpatialIndexes::ensureSpatialIndexFlags($tableDiff);

foreach (SpatialIndexes::extractSpatialIndicies($tableDiff->getAddedIndexes()) as $index) {
$sql[] = $spatialIndexSqlGenerator->getSql($index, $table);
}

foreach (SpatialIndexes::extractSpatialIndicies($tableDiff->getModifiedIndexes()) as $index) {
$sql[] = $this->getDropIndexSQL($index->getName(), $table->getName());
$sql[] = $spatialIndexSqlGenerator->getSql($index, $table);
}
}

return $sql;
}

public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES): array
{
SpatialIndexes::ensureSpatialIndexFlags($table);

$spatialIndexes = SpatialIndexes::extractSpatialIndicies($table->getIndexes());
foreach ($spatialIndexes as $index) {
$table->dropIndex($index->getName());
}

$sql = parent::getCreateTableSQL($table, $createFlags);

$spatialIndexSqlGenerator = new SpatialIndexSqlGenerator($this);
foreach ($spatialIndexes as $index) {
$sql[] = $spatialIndexSqlGenerator->getSql($index, $table);
}

return $sql;
}

public function getCreateTablesSQL(array $tables): array
{
$sql = [];
$spatialIndexSqlGenerator = new SpatialIndexSqlGenerator($this);

/** @var Table $table */
foreach ($tables as $table) {
SpatialIndexes::ensureSpatialIndexFlags($table);

$spatialIndexes = SpatialIndexes::extractSpatialIndicies($table->getIndexes());
foreach ($spatialIndexes as $index) {
$table->dropIndex($index->getName());
}
$sql = [...$sql, ...$this->getCreateTableWithoutForeignKeysSQL($table)];

foreach ($spatialIndexes as $index) {
$table->addIndex($index->getColumns(), $index->getName(), $index->getFlags(), $index->getOptions());
}

foreach ($spatialIndexes as $spatialIndex) {
$sql[] = $spatialIndexSqlGenerator->getSql($spatialIndex, $table);
}
}

foreach ($tables as $table) {
foreach ($table->getForeignKeys() as $foreignKey) {
$sql[] = $this->getCreateForeignKeySQL(
$foreignKey,
$table->getQuotedName($this),
);
}
}

return $sql;
}

public function getAlterTableSQL(TableDiff $diff): array
{
$table = $diff->getOldTable();
$spatialIndexSqlGenerator = new SpatialIndexSqlGenerator($this);

SpatialIndexes::ensureSpatialIndexFlags($diff);

$sql = parent::getAlterTableSQL(SpatialIndexes::filterTableDiff($diff));

foreach (SpatialIndexes::extractSpatialIndicies($diff->getAddedIndexes()) as $spatialIndex) {
$sql[] = $spatialIndexSqlGenerator->getSql($spatialIndex, $table);
}

foreach (SpatialIndexes::extractSpatialIndicies($diff->getModifiedIndexes()) as $index) {
$sql[] = $this->getDropIndexSQL($index->getName(), $table->getName());
$sql[] = $spatialIndexSqlGenerator->getSql($index, $table);
}

/** @var ColumnDiff $columnDiff */
foreach ($diff->getModifiedColumns() as $columnDiff) {
if ($columnDiff->getOldColumn()->getPlatformOption('srid') !== $columnDiff->getNewColumn()->getPlatformOption('srid')) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws an exception when the 'srid' option is not defined (example: the column is not one from PosGis)

I would add something like this before the check:

            if( ! $columnDiff->getNewColumn()->hasPlatformOption('srid')) {
                continue;
            }

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is thrown when running bin/console doctrine:schema:validate -v

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for the testing! Fix pushed.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR! 👍

Unfortunately DBAL v4 is not (yet) compatible with Symfony, thus my tests will be limited to v3 only from now on

$sql[] = sprintf(
"SELECT UpdateGeometrySRID('%s', '%s', %d)",
$table->getName(),
$columnDiff->getNewColumn()->getName(),
(int) $columnDiff->getNewColumn()->getPlatformOption('srid')
);
}
}

return $sql;
}
}
Loading