8000 Automatically provide Messenger Doctrine schema to "diff" · symfony/symfony@2dd9c3c · GitHub
[go: up one dir, main page]

Skip to content

Commit 2dd9c3c

Browse files
weaverryanfabpot
authored andcommitted
Automatically provide Messenger Doctrine schema to "diff"
1 parent c30d6f9 commit 2dd9c3c

16 files changed

+566
-27
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\SchemaListener;
13+
14+
use Doctrine\Common\EventSubscriber;
15+
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
16+
use Doctrine\DBAL\Events;
17+
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
18+
use Doctrine\ORM\Tools\ToolEvents;
19+
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransport;
20+
use Symfony\Component\Messenger\Transport\TransportInterface;
21+
22+
/**
23+
* Automatically adds any required database tables to the Doctrine Schema.
24+
*
25+
* @author Ryan Weaver <ryan@symfonycasts.com>
26+
*/
27+
final class MessengerTransportDoctrineSchemaSubscriber implements EventSubscriber
28+
{
29+
private const PROCESSING_TABLE_FLAG = self::class.':processing';
30+
31+
private $transports;
32+
33+
/**
34+
* @param iterable|TransportInterface[] $transports
35+
*/
36+
public function __construct(iterable $transports)
37+
{
38+
$this->transports = $transports;
39+
}
40+
41+
public function postGenerateSchema(GenerateSchemaEventArgs $event): void
42+
{
43+
$dbalConnection = $event->getEntityManager()->getConnection();
44+
foreach ($this->transports as $transport) {
45+
if (!$transport instanceof DoctrineTransport) {
46+
continue;
47+
}
48+
49+
$transport->configureSchema($event->getSchema(), $dbalConnection);
50+
}
51+
}
52+
53+
public function onSchemaCreateTable(SchemaCreateTableEventArgs $event): void
54+
{
55+
$table = $event->getTable();
56+
57+
// if this method triggers a nested create table below, allow Doctrine to work like normal
58+
if ($table->hasOption(self::PROCESSING_TABLE_FLAG)) {
59+
return;
60+
}
61+
62+
foreach ($this->transports as $transport) {
63+
if (!$transport instanceof DoctrineTransport) {
64+
continue;
65+
}
66+
67+
$extraSql = $transport->getExtraSetupSqlForTable($table);
68+
if (null === $extraSql) {
69+
continue;
70+
}
71+
72+
// avoid this same listener from creating a loop on this table
73+
$table->addOption(self::PROCESSING_TABLE_FLAG, true);
74+
$createTableSql = $event->getPlatform()->getCreateTableSQL($table);
75+
76+
/*
77+
* Add all the SQL needed to create the table and tell Doctrine
78+
* to "preventDefault" so that only our SQL is used. This is
79+
* the only way to inject some extra SQL.
80+
*/
81+
$event->addSql($createTableSql);
82+
$event->addSql($extraSql);
83+
$event->preventDefault();
84+
85+
return;
86+
}
87+
}
88+
89+
public function getSubscribedEvents(): array
90+
{
91+
return [
92+
ToolEvents::postGenerateSchema,
93+
Events::onSchemaCreateTable,
94+
];
95+
}
96+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\SchemaListener;
13+
14+
use Doctrine\Common\EventSubscriber;
15+
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
16+
use Doctrine\ORM\Tools\ToolEvents;
17+
use Symfony\Component\Cache\Adapter\PdoAdapter;
18+
19+
/**
20+
* Automatically adds the cache table needed for the PdoAdapter.
21+
*
22+
* @author Ryan Weaver <ryan@symfonycasts.com>
23+
*/
24+
final class PdoCacheAdapterDoctrineSchemaSubscriber implements EventSubscriber
25+
{
26+
private $pdoAdapters;
27+
28+
/**
29+
* @param iterable|PdoAdapter[] $pdoAdapters
30+
*/
31+
public function __construct(iterable $pdoAdapters)
32+
{
33+
$this->pdoAdapters = $pdoAdapters;
34+
}
35+
36+
public function postGenerateSchema(GenerateSchemaEventArgs $event): void
37+
{
38+
$dbalConnection = $event->getEntityManager()->getConnection();
39+
foreach ($this->pdoAdapters as $pdoAdapter) {
40+
$pdoAdapter->configureSchema($event->getSchema(), $dbalConnection);
41+
}
42+
}
43+
44+
public function getSubscribedEvents(): array
45+
{
46+
return [
47+
ToolEvents::postGenerateSchema,
48+
];
49+
}
50+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Tests\SchemaListener;
13+
14+
use Doctrine\DBAL\Connection;
15+
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
16+
use Doctrine\DBAL\Platforms\AbstractPlatform;
17+
use Doctrine\DBAL\Schema\Schema;
18+
use Doctrine\DBAL\Schema\Table;
19+
use Doctrine\ORM\EntityManagerInterface;
20+
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
21+
use PHPUnit\Framework\TestCase;
22+
use Symfony\Bridge\Doctrine\SchemaListener\MessengerTransportDoctrineSchemaSubscriber;
23+
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransport;
24+
use Symfony\Component\Messenger\Transport\TransportInterface;
25+
26+
class MessengerTransportDoctrineSchemaSubscriberTest extends TestCase
27+
{
28+
public function testPostGenerateSchema()
29+
{
30+
$schema = new Schema();
31+
$dbalConnection = $this->createMock(Connection::class);
32+
$entityManager = $this->createMock(EntityManagerInterface::class);
33+
$entityManager->expects($this->once())
34+
->method('getConnection')
35+
->willReturn($dbalConnection);
36+
$event = new GenerateSchemaEventArgs($entityManager, $schema);
37+
38+
$doctrineTransport = $this->createMock(DoctrineTransport::class);
39+
$doctrineTransport->expects($this->once())
40+
->method('configureSchema')
41+
->with($schema, $dbalConnection);
42+
$otherTransport = $this->createMock(TransportInterface::class);
43+
$otherTransport->expects($this->never())
44+
->method($this->anything());
45+
46+
$subscriber = new MessengerTransportDoctrineSchemaSubscriber([$doctrineTransport, $otherTransport]);
47+
$subscriber->postGenerateSchema($event);
48+
}
49+
50+
public function testOnSchemaCreateTable()
51+
{
52+
$platform = $this->createMock(AbstractPlatform::class);
53+
$table = new Table('queue_table');
54+
$event = new SchemaCreateTableEventArgs($table, [], [], $platform);
55+
56+
$otherTransport = $this->createMock(TransportInterface::class);
57+
$otherTransport->expects($this->never())
58+
->method($this->anything());
59+
60+
$doctrineTransport = $this->createMock(DoctrineTransport::class);
61+
$doctrineTransport->expects($this->once())
62+
->method('getExtraSetupSqlForTable')
63+
->with($table)
64+
->willReturn('ALTER TABLE pizza ADD COLUMN extra_cheese boolean');
65+
66+
// we use the platform to generate the full create table sql
67+
$platform->expects($this->once())
68+
->method('getCreateTableSQL')
69+
->with($table)
70+
->willReturn('CREATE TABLE pizza (id integer NOT NULL)');
71+
72+
$subscriber = new MessengerTransportDoctrineSchemaSubscriber([$otherTransport, $doctrineTransport]);
73+
$subscriber->onSchemaCreateTable($event);
74+
$this->assertTrue($event->isDefaultPrevented());
75+
$this->assertSame([
76+
'CREATE TABLE pizza (id integer NOT NULL)',
77+
'ALTER TABLE pizza ADD COLUMN extra_cheese boolean',
78+
], $event->getSql());
79+
}
80+
81+
public function testOnSchemaCreateTableNoExtraSql()
82+
{
83+
$platform = $this->createMock(AbstractPlatform::class);
84+
$table = new Table('queue_table');
85+
$event = new SchemaCreateTableEventArgs($table, [], [], $platform);
86+
87+
$doctrineTransport = $this->createMock(DoctrineTransport::class);
88+
$doctrineTransport->expects($this->once())
89+
->method('getExtraSetupSqlForTable')
90+
->willReturn(null);
91+
92+
$platform->expects($this->never())
93+
->method('getCreateTableSQL');
94+
95+
$subscriber = new MessengerTransportDoctrineSchemaSubscriber([$doctrineTransport]);
96+
$subscriber->onSchemaCreateTable($event);
97+
$this->assertFalse($event->isDefaultPrevented());
98+
}
99+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Tests\SchemaListener;
13+
14+
use Doctrine\DBAL\Connection;
15+
use Doctrine\DBAL\Schema\Schema;
16+
use Doctrine\ORM\EntityManagerInterface;
17+
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
18+
use PHPUnit\Framework\TestCase;
19+
use Symfony\Bridge\Doctrine\SchemaListener\PdoCacheAdapterDoctrineSchemaSubscriber;
20+
use Symfony\Component\Cache\Adapter\PdoAdapter;
21+
22+
class PdoCacheAdapterDoctrineSchemaSubscriberTest extends TestCase
23+
{
24+
public function testPostGenerateSchema()
25+
{
26+
$schema = new Schema();
27+
$dbalConnection = $this->createMock(Connection::class);
28+
$entityManager = $this->createMock(EntityManagerInterface::class);
29+
$entityManager->expects($this->once())
30+
->method('getConnection')
31+
->willReturn($dbalConnection);
32+
$event = new GenerateSchemaEventArgs($entityManager, $schema);
33+
34+
$pdoAdapter = $this->createMock(PdoAdapter::class);
35+
$pdoAdapter->expects($this->once())
36+
->method('configureSchema')
37+
->with($schema, $dbalConnection);
38+
39+
$subscriber = new PdoCacheAdapterDoctrineSchemaSubscriber([$pdoAdapter]);
40+
$subscriber->postGenerateSchema($event);
41+
}
42+
}

src/Symfony/Bridge/Doctrine/composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
},
2727
"require-dev": {
2828
"symfony/stopwatch": "^4.4|^5.0",
29+
"symfony/cache": "^5.1",
2930
"symfony/config": "^4.4|^5.0",
3031
"symfony/dependency-injection": "^4.4|^5.0",
3132
"symfony/form": "^5.1",
3233
"symfony/http-kernel": "^5.0",
3334
"symfony/messenger": "^4.4|^5.0",
35+
"symfony/doctrine-messenger": "^5.1",
3436
"symfony/property-access": "^4.4|^5.0",
3537
"symfony/property-info": "^5.0",
3638
"symfony/proxy-manager-bridge": "^4.4|^5.0",

src/Symfony/Component/Cache/Adapter/PdoAdapter.php

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,24 +115,8 @@ public function createTable()
115115
$conn = $this->getConnection();
116116

117117
if ($conn instanceof Connection) {
118-
$types = [
119-
'mysql' => 'binary',
120-
'sqlite' => 'text',
121-
'pgsql' => 'string',
122-
'oci' => 'string',
123-
'sqlsrv' => 'string',
124-
];
125-
if (!isset($types[$this->driver])) {
126-
throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
127-
}
128-
129118
$schema = new Schema();
130-
$table = $schema->createTable($this->table);
131-
$table->addColumn($this->idCol, $types[$this->driver], ['length' => 255]);
132-
$table->addColumn($this->dataCol, 'blob', ['length' => 16777215]);
133-
$table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]);
134-
$table->addColumn($this->timeCol, 'integer', ['unsigned' => true]);
135-
$table->setPrimaryKey([$this->idCol]);
119+
$this->addTableToSchema($schema);
136120

137121
foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
138122
$conn->exec($sql);
@@ -169,6 +153,23 @@ public function createTable()
169153
$conn->exec($sql);
170154
}
171155

156+
/**
157+
* Adds the Table to the Schema if the adapter uses this Connection.
158+
*/
159+
public function configureSchema(Schema $schema, Connection $forConnection): void
160+
{
161+
// only update the schema for this connection
162+
if ($forConnection !== $this->getConnection()) {
163+
return;
164+
}
165+
166+
if ($schema->hasTable($this->table)) {
167+
return;
168+
}
169+
170+
$this->addTableToSchema($schema);
171+
}
172+
172173
/**
173174
* {@inheritdoc}
174175
*/
@@ -467,4 +468,25 @@ private function getServerVersion(): string
467468

468469
return $this->serverVersion;
469470
}
471+
472+
private function addTableToSchema(Schema $schema): void
473+
{
474+
$types = [
475+
'mysql' => 'binary',
476+
'sqlite' => 'text',
477+
'pgsql' => 'string',
478+
'oci' => 'string',
479+
'sqlsrv' => 'string',
480+
];
481+
if (!isset($types[$this->driver])) {
482+
throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
483+
}
484+
485+
$table = $schema->createTable($this->table);
486+
$table->addColumn($this->idCol, $types[$this->driver], ['length' => 255]);
487+
$table->addColumn($this->dataCol, 'blob', ['length' => 16777215]);
488+
$table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]);
489+
$table->addColumn($this->timeCol, 'integer', ['unsigned' => true]);
490+
$table->setPrimaryKey([$this->idCol]);
491+
}
470492
}

0 commit comments

Comments
 (0)
0