Skip to content

Commit 26d1542

Browse files
authored
Merge pull request #5876 from derrabus/feature/schema-manager-factory
Introduce the `SchemaManagerFactory` interface
2 parents f5196ed + 537b711 commit 26d1542

File tree

10 files changed

+198
-14
lines changed

10 files changed

+198
-14
lines changed

UPGRADE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,25 @@ awareness about deprecated code.
88

99
# Upgrade to 3.6
1010

11+
## Deprecated not setting a schema manager factory
12+
13+
DBAL 4 will change the way the schema manager is created. To opt in to the new
14+
behavior, please configure the schema manager factory:
15+
16+
```php
17+
$configuration = new Configuration();
18+
$configuration->setSchemaManagerFactory(new DefaultSchemaManagerFactory());
19+
20+
$connection = DriverManager::getConnection(
21+
[/* your parameters */],
22+
$configuration,
23+
);
24+
```
25+
26+
If you use a custom platform implementation, please make sure it implements
27+
the `createSchemaManager()`method . Otherwise, the connection will fail to
28+
create a schema manager.
29+
1130
## Deprecated the `url` connection parameter
1231

1332
DBAL ships with a new and configurable DSN parser that can be used to parse a

docs/en/reference/schema-manager.rst

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ A Schema Manager instance helps you with the abstraction of the
55
generation of SQL assets such as Tables, Sequences, Foreign Keys
66
and Indexes.
77

8-
To retrieve the ``SchemaManager`` for your connection you can use
9-
the ``getSchemaManager()`` method:
8+
To create a schema manager for your connection you can use
9+
the ``createSchemaManager()`` method:
1010

1111
.. code-block:: php
1212
1313
<?php
14-
$sm = $conn->getSchemaManager();
14+
$sm = $conn->createSchemaManager();
1515
16-
Now with the ``SchemaManager`` instance in ``$sm`` you can use the
16+
Now with the schema manager instance in ``$sm`` you can use the
1717
available methods to learn about your database schema:
1818

1919
.. note::
@@ -36,7 +36,7 @@ Retrieve an array of databases on the configured connection:
3636
$databases = $sm->listDatabases();
3737
3838
listSequences()
39-
-------------------------------
39+
---------------
4040

4141
Retrieve an array of ``Doctrine\DBAL\Schema\Sequence`` instances
4242
that exist for a database:
@@ -63,7 +63,7 @@ Now you can loop over the array inspecting each sequence object:
6363
}
6464
6565
listTableColumns()
66-
----------------------------
66+
------------------
6767

6868
Retrieve an array of ``Doctrine\DBAL\Schema\Column`` instances that
6969
exist for the given table:
@@ -102,7 +102,7 @@ schema for that table. For example we can add a new column:
102102
$table->addColumn('email_address', 'string');
103103
104104
listTableForeignKeys()
105-
--------------------------------
105+
----------------------
106106

107107
Retrieve an array of ``Doctrine\DBAL\Schema\ForeignKeyConstraint``
108108
instances that exist for the given table:
@@ -123,7 +123,7 @@ object:
123123
}
124124
125125
listTableIndexes()
126-
----------------------------
126+
------------------
127127

128128
Retrieve an array of ``Doctrine\DBAL\Schema\Index`` instances that
129129
exist for the given table:
@@ -232,3 +232,50 @@ table:
232232
0 => 'DROP TABLE user'
233233
)
234234
*/
235+
236+
Overriding the schema manager
237+
-----------------------------
238+
239+
All schema manager classes can be overridden, for instance if your application needs to modify SQL statements emitted
240+
by the schema manager or the comparator. If you want your own schema manager to be returned by
241+
``Connection::createSchemaManager()`` you need to configure a factory for it.
242+
243+
.. code-block:: php
244+
245+
<?php
246+
use Doctrine\DBAL\Configuration;
247+
use Doctrine\DBAL\DriverManager;
248+
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
249+
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
250+
use Doctrine\DBAL\Schema\MySQLSchemaManager;
251+
use Doctrine\DBAL\Schema\SchemaManagerFactory;
252+
253+
class MyCustomMySQLSchemaManager extends MySQLSchemaManager
254+
{
255+
// .. your custom logic.
256+
}
257+
258+
final class MySchemaManagerFactory implements SchemaManagerFactory
259+
{
260+
private readonly SchemaManagerFactory $defaultFactory;
261+
262+
public function __construct()
263+
{
264+
$this->defaultFactory = new DefaultSchemaManagerFactory();
265+
}
266+
267+
public function createSchemaManager(Connection $connection): AbstractSchemaManager
268+
{
269+
$platform = $connection->getDatabasePlatform();
270+
if ($platform instanceof AbstractMySQLPlatform) {
271+
return new MyCustomMySQLSchemaManager($connection, $platform);
272+
}
273+
274+
return $this->defaultFactory->createSchemaManager($connection);
275+
}
276+
}
277+
278+
$configuration = new Configuration();
279+
$configuration->setSchemaManagerFactory(new MySchemaManagerFactory());
280+
281+
$connection = DriverManager::getConnection([/* your connection parameters */], $configuration);

src/Configuration.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
88
use Doctrine\DBAL\Driver\Middleware;
99
use Doctrine\DBAL\Logging\SQLLogger;
10+
use Doctrine\DBAL\Schema\SchemaManagerFactory;
1011
use Doctrine\Deprecations\Deprecation;
1112
use Psr\Cache\CacheItemPoolInterface;
1213

@@ -55,6 +56,8 @@ class Configuration
5556
*/
5657
protected $autoCommit = true;
5758

59+
private ?SchemaManagerFactory $schemaManagerFactory = null;
60+
5861
public function __construct()
5962
{
6063
$this->schemaAssetsFilter = static function (): bool {
@@ -225,4 +228,17 @@ public function getMiddlewares(): array
225228
{
226229
return $this->middlewares;
227230
}
231+
232+
public function getSchemaManagerFactory(): ?SchemaManagerFactory
233+
{
234+
return $this->schemaManagerFactory;
235+
}
236+
237+
/** @return $this */
238+
public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFactory): self
239+
{
240+
$this->schemaManagerFactory = $schemaManagerFactory;
241+
242+
return $this;
243+
}
228244
}

src/Connection.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
2222
use Doctrine\DBAL\Query\QueryBuilder;
2323
use Doctrine\DBAL\Schema\AbstractSchemaManager;
24+
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
25+
use Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
26+
use Doctrine\DBAL\Schema\SchemaManagerFactory;
2427
use Doctrine\DBAL\SQL\Parser;
2528
use Doctrine\DBAL\Types\Type;
2629
use Doctrine\Deprecations\Deprecation;
@@ -160,6 +163,8 @@ class Connection
160163
*/
161164
private bool $isRollbackOnly = false;
162165

166+
private SchemaManagerFactory $schemaManagerFactory;
167+
163168
/**
164169
* Initializes a new instance of the Connection class.
165170
*
@@ -209,6 +214,21 @@ public function __construct(
209214
$this->_expr = $this->createExpressionBuilder();
210215

211216
$this->autoCommit = $config->getAutoCommit();
217+
218+
$schemaManagerFactory = $config->getSchemaManagerFactory();
219+
if ($schemaManagerFactory === null) {
220+
Deprecation::trigger(
221+
'doctrine/dbal',
222+
'https://github.com/doctrine/dbal/issues/5812',
223+
'Not configuring a schema manager factory is deprecated.'
224+
. ' Use %s which is going to be the default in DBAL 4.',
225+
DefaultSchemaManagerFactory::class,
226+
);
227+
228+
$schemaManagerFactory = new LegacySchemaManagerFactory();
229+
}
230+
231+
$this->schemaManagerFactory = $schemaManagerFactory;
212232
}
213233

214234
/**
@@ -1653,10 +1673,7 @@ public function getNativeConnection()
16531673
*/
16541674
public function createSchemaManager(): AbstractSchemaManager
16551675
{
1656-
return $this->_driver->getSchemaManager(
1657-
$this,
1658-
$this->getDatabasePlatform(),
1659-
);
1676+
return $this->schemaManagerFactory->createSchemaManager($this);
16601677
}
16611678

16621679
/**
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema;
6+
7+
use Doctrine\DBAL\Connection;
8+
use Doctrine\DBAL\Exception;
9+
10+
/**
11+
* A schema manager factory that returns the default schema manager for the given platform.
12+
*/
13+
final class DefaultSchemaManagerFactory implements SchemaManagerFactory
14+
{
15+
/** @throws Exception If the platform does not support creating schema managers yet. */
16+
public function createSchemaManager(Connection $connection): AbstractSchemaManager
17+
{
18+
return $connection->getDatabasePlatform()->createSchemaManager($connection);
19+
}
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
/** @internal Will be removed in 4.0. */
10+
final class LegacySchemaManagerFactory implements SchemaManagerFactory
11+
{
12+
public function createSchemaManager(Connection $connection): AbstractSchemaManager
13+
{
14+
return $connection->getDriver()->getSchemaManager(
15+
$connection,
16+
$connection->getDatabasePlatform(),
17+
);
18+
}
19+
}

src/Schema/SchemaManagerFactory.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
/**
10+
* Creates a schema manager for the given connection.
11+
*
12+
* This interface is an extension point for applications that need to override schema managers.
13+
*/
14+
interface SchemaManagerFactory
15+
{
16+
public function createSchemaManager(Connection $connection): AbstractSchemaManager;
17+
}

tests/ConnectionTest.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
use Doctrine\DBAL\ParameterType;
2222
use Doctrine\DBAL\Platforms\AbstractPlatform;
2323
use Doctrine\DBAL\Result;
24+
use Doctrine\DBAL\Schema\AbstractSchemaManager;
25+
use Doctrine\DBAL\Schema\SchemaManagerFactory;
26+
use Doctrine\DBAL\Schema\SqliteSchemaManager;
2427
use Doctrine\DBAL\VersionAwarePlatformDriver;
2528
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
2629
use PHPUnit\Framework\MockObject\MockObject;
@@ -50,7 +53,7 @@ protected function setUp(): void
5053
}
5154

5255
/** @return Connection&MockObject */
53-
private function getExecuteStatementMockConnection()
56+
private function getExecuteStatementMockConnection(): Connection
5457
{
5558
$driverMock = $this->createMock(Driver::class);
5659

@@ -892,6 +895,29 @@ public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGen
892895

893896
$connection->executeCacheQuery($query, [], [], $queryCacheProfile);
894897
}
898+
899+
public function testCustomSchemaManagerFactory(): void
900+
{
901+
$schemaManager = $this->createStub(AbstractSchemaManager::class);
902+
$factory = $this->createMock(SchemaManagerFactory::class);
903+
$factory->expects(self::once())->method('createSchemaManager')->willReturn($schemaManager);
904+
905+
$configuration = new Configuration();
906+
$configuration->setSchemaManagerFactory($factory);
907+
908+
$this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/issues/5812');
909+
910+
$connection = DriverManager::getConnection(['driver' => 'sqlite3', 'memory' => true], $configuration);
911+
self::assertSame($schemaManager, $connection->createSchemaManager());
912+
}
913+
914+
public function testLegacySchemaManagerFactory(): void
915+
{
916+
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/dbal/issues/5812');
917+
918+
$connection = DriverManager::getConnection(['driver' => 'sqlite3', 'memory' => true]);
919+
self::assertInstanceOf(SqliteSchemaManager::class, $connection->createSchemaManager());
920+
}
895921
}
896922

897923
interface ConnectDispatchEventListener

tests/Functional/Schema/SchemaManagerFunctionalTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ protected function setUp(): void
6666
self::markTestSkipped(sprintf('Skipping since connected to %s', get_class($platform)));
6767
}
6868

69-
$this->schemaManager = $this->connection->getSchemaManager();
69+
$this->schemaManager = $this->connection->createSchemaManager();
7070
}
7171

7272
protected function tearDown(): void

tests/TestUtil.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\DBAL\Platforms\DB2Platform;
1313
use Doctrine\DBAL\Platforms\OraclePlatform;
1414
use Doctrine\DBAL\Platforms\SqlitePlatform;
15+
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
1516
use PHPUnit\Framework\Assert;
1617

1718
use function array_keys;
@@ -155,6 +156,8 @@ private static function createConfiguration(string $driver): Configuration
155156
break;
156157
}
157158

159+
$configuration->setSchemaManagerFactory(new DefaultSchemaManagerFactory());
160+
158161
return $configuration;
159162
}
160163

0 commit comments

Comments
 (0)