Skip to content

Commit 62201f9

Browse files
authored
Merge pull request #3956 from dmaicher/cli_multi_connection
allow using multiple connections for CLI commands
2 parents a8544ca + b10d4db commit 62201f9

File tree

8 files changed

+193
-29
lines changed

8 files changed

+193
-29
lines changed

bin/doctrine-dbal.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
34
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
45
use Symfony\Component\Console\Helper\HelperSet;
56

@@ -41,17 +42,17 @@
4142
exit(1);
4243
}
4344

44-
$commands = [];
45-
$helperSet = require $configFile;
45+
$commands = [];
46+
$helperSetOrConnectionProvider = require $configFile;
4647

47-
if (! $helperSet instanceof HelperSet) {
48-
foreach ($GLOBALS as $helperSetCandidate) {
49-
if ($helperSetCandidate instanceof HelperSet) {
50-
$helperSet = $helperSetCandidate;
48+
if (! $helperSetOrConnectionProvider instanceof HelperSet && ! $helperSetOrConnectionProvider instanceof ConnectionProvider) {
49+
foreach ($GLOBALS as $candidate) {
50+
if ($candidate instanceof HelperSet) {
51+
$helperSetOrConnectionProvider = $candidate;
5152

5253
break;
5354
}
5455
}
5556
}
5657

57-
ConsoleRunner::run($helperSet, $commands);
58+
ConsoleRunner::run($helperSetOrConnectionProvider, $commands);

lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
use Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords;
2222
use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords;
2323
use Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords;
24+
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
25+
use Exception;
2426
use InvalidArgumentException;
2527
use Symfony\Component\Console\Command\Command;
2628
use Symfony\Component\Console\Input\InputInterface;
@@ -30,6 +32,9 @@
3032
use function assert;
3133
use function count;
3234
use function implode;
35+
use function is_string;
36+
use function trigger_error;
37+
use const E_USER_DEPRECATED;
3338

3439
class ReservedWordsCommand extends Command
3540
{
@@ -54,6 +59,20 @@ class ReservedWordsCommand extends Command
5459
'sqlanywhere16' => SQLAnywhere16Keywords::class,
5560
];
5661

62+
/** @var ConnectionProvider|null */
63+
private $connectionProvider;
64+
65+
public function __construct(?ConnectionProvider $connectionProvider = null)
66+
{
67+
parent::__construct();
68+
$this->connectionProvider = $connectionProvider;
69+
if ($connectionProvider !== null) {
70+
return;
71+
}
72+
73+
@trigger_error('Not passing a connection provider as the first constructor argument is deprecated', E_USER_DEPRECATED);
74+
}
75+
5776
/**
5877
* If you want to add or replace a keywords list use this command.
5978
*
@@ -73,12 +92,14 @@ protected function configure()
7392
$this
7493
->setName('dbal:reserved-words')
7594
->setDescription('Checks if the current database contains identifiers that are reserved.')
76-
->setDefinition([new InputOption(
77-
'list',
78-
'l',
79-
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
80-
'Keyword-List name.'
81-
),
95+
->setDefinition([
96+
new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'),
97+
new InputOption(
98+
'list',
99+
'l',
100+
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
101+
'Keyword-List name.'
102+
),
82103
])
83104
->setHelp(<<<EOT
84105
Checks if the current database contains tables and columns
@@ -121,8 +142,7 @@ protected function configure()
121142
*/
122143
protected function execute(InputInterface $input, OutputInterface $output)
123144
{
124-
$conn = $this->getHelper('db')->getConnection();
125-
assert($conn instanceof Connection);
145+
$conn = $this->getConnection($input);
126146

127147
$keywordLists = (array) $input->getOption('list');
128148
if (! $keywordLists) {
@@ -178,4 +198,24 @@ protected function execute(InputInterface $input, OutputInterface $output)
178198

179199
return 0;
180200
}
201+
202+
private function getConnection(InputInterface $input) : Connection
203+
{
204+
$connectionName = $input->getOption('connection');
205+
assert(is_string($connectionName) || $connectionName === null);
206+
207+
if ($this->connectionProvider === null) {
208+
if ($connectionName !== null) {
209+
throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.');
210+
}
211+
212+
return $this->getHelper('db')->getConnection();
213+
}
214+
215+
if ($connectionName !== null) {
216+
return $this->connectionProvider->getConnection($connectionName);
217+
}
218+
219+
return $this->connectionProvider->getDefaultConnection();
220+
}
181221
}

lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace Doctrine\DBAL\Tools\Console\Command;
44

5+
use Doctrine\DBAL\Connection;
6+
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
57
use Doctrine\DBAL\Tools\Dumper;
8+
use Exception;
69
use LogicException;
710
use RuntimeException;
811
use Symfony\Component\Console\Command\Command;
@@ -14,20 +17,37 @@
1417
use function is_numeric;
1518
use function is_string;
1619
use function stripos;
20+
use function trigger_error;
21+
use const E_USER_DEPRECATED;
1722

1823
/**
1924
* Task for executing arbitrary SQL that can come from a file or directly from
2025
* the command line.
2126
*/
2227
class RunSqlCommand extends Command
2328
{
29+
/** @var ConnectionProvider|null */
30+
private $connectionProvider;
31+
32+
public function __construct(?ConnectionProvider $connectionProvider = null)
33+
{
34+
parent::__construct();
35+
$this->connectionProvider = $connectionProvider;
36+
if ($connectionProvider !== null) {
37+
return;
38+
}
39+
40+
@trigger_error('Not passing a connection provider as the first constructor argument is deprecated', E_USER_DEPRECATED);
41+
}
42+
2443
/** @return void */
2544
protected function configure()
2645
{
2746
$this
2847
->setName('dbal:run-sql')
2948
->setDescription('Executes arbitrary SQL directly from the command line.')
3049
->setDefinition([
50+
new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'),
3151
new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'),
3252
new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', 7),
3353
new InputOption('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'),
@@ -43,7 +63,7 @@ protected function configure()
4363
*/
4464
protected function execute(InputInterface $input, OutputInterface $output)
4565
{
46-
$conn = $this->getHelper('db')->getConnection();
66+
$conn = $this->getConnection($input);
4767

4868
$sql = $input->getArgument('sql');
4969

@@ -69,4 +89,24 @@ protected function execute(InputInterface $input, OutputInterface $output)
6989

7090
return 0;
7191
}
92+
93+
private function getConnection(InputInterface $input) : Connection
94+
{
95+
$connectionName = $input->getOption('connection');
96+
assert(is_string($connectionName) || $connectionName === null);
97+
98+
if ($this->connectionProvider === null) {
99+
if ($connectionName !== null) {
100+
throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.');
101+
}
102+
103+
return $this->getHelper('db')->getConnection();
104+
}
105+
106+
if ($connectionName !== null) {
107+
return $this->connectionProvider->getConnection($connectionName);
108+
}
109+
110+
return $this->connectionProvider->getDefaultConnection();
111+
}
72112
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Doctrine\DBAL\Tools\Console;
4+
5+
use OutOfBoundsException;
6+
7+
final class ConnectionNotFound extends OutOfBoundsException
8+
{
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Doctrine\DBAL\Tools\Console;
4+
5+
use Doctrine\DBAL\Connection;
6+
7+
interface ConnectionProvider
8+
{
9+
public function getDefaultConnection() : Connection;
10+
11+
/**
12+
* @throws ConnectionNotFound in case a connection with the given name does not exist.
13+
*/
14+
public function getConnection(string $name) : Connection;
15+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Doctrine\DBAL\Tools\Console\ConnectionProvider;
4+
5+
use Doctrine\DBAL\Connection;
6+
use Doctrine\DBAL\Tools\Console\ConnectionNotFound;
7+
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
8+
use function sprintf;
9+
10+
class SingleConnectionProvider implements ConnectionProvider
11+
{
12+
/** @var Connection */
13+
private $connection;
14+
15+
/** @var string */
16+
private $defaultConnectionName;
17+
18+
public function __construct(Connection $connection, string $defaultConnectionName = 'default')
19+
{
20+
$this->connection = $connection;
21+
$this->defaultConnectionName = $defaultConnectionName;
22+
}
23+
24+
public function getDefaultConnection() : Connection
25+
{
26+
return $this->connection;
27+
}
28+
29+
public function getConnection(string $name) : Connection
30+
{
31+
if ($name !== $this->defaultConnectionName) {
32+
throw new ConnectionNotFound(sprintf('Connection with name "%s" does not exist.', $name));
33+
}
34+
35+
return $this->connection;
36+
}
37+
}

lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
use Symfony\Component\Console\Application;
1212
use Symfony\Component\Console\Command\Command;
1313
use Symfony\Component\Console\Helper\HelperSet;
14+
use TypeError;
15+
use function sprintf;
16+
use function trigger_error;
17+
use const E_USER_DEPRECATED;
1418

1519
/**
1620
* Handles running the Console Tools inside Symfony Console context.
@@ -20,6 +24,8 @@ class ConsoleRunner
2024
/**
2125
* Create a Symfony Console HelperSet
2226
*
27+
* @deprecated use a ConnectionProvider instead.
28+
*
2329
* @return HelperSet
2430
*/
2531
public static function createHelperSet(Connection $connection)
@@ -30,20 +36,31 @@ public static function createHelperSet(Connection $connection)
3036
}
3137

3238
/**
33-
* Runs console with the given helperset.
39+
* Runs console with the given connection provider or helperset (deprecated).
3440
*
35-
* @param Command[] $commands
41+
* @param ConnectionProvider|HelperSet $helperSetOrConnectionProvider
42+
* @param Command[] $commands
3643
*
3744
* @return void
3845
*/
39-
public static function run(HelperSet $helperSet, $commands = [])
46+
public static function run($helperSetOrConnectionProvider, $commands = [])
4047
{
4148
$cli = new Application('Doctrine Command Line Interface', Version::VERSION);
4249

4350
$cli->setCatchExceptions(true);
44-
$cli->setHelperSet($helperSet);
4551

46-
self::addCommands($cli);
52+
$connectionProvider = null;
53+
if ($helperSetOrConnectionProvider instanceof HelperSet) {
54+
@trigger_error(sprintf('Passing an instance of "%s" as the first argument is deprecated. Pass an instance of "%s" instead.', HelperSet::class, ConnectionProvider::class), E_USER_DEPRECATED);
55+
$connectionProvider = null;
56+
$cli->setHelperSet($helperSetOrConnectionProvider);
57+
} elseif ($helperSetOrConnectionProvider instanceof ConnectionProvider) {
58+
$connectionProvider = $helperSetOrConnectionProvider;
59+
} else {
60+
throw new TypeError(sprintf('First argument must be an instance of "%s" or "%s"', HelperSet::class, ConnectionProvider::class));
61+
}
62+
63+
self::addCommands($cli, $connectionProvider);
4764

4865
$cli->addCommands($commands);
4966
$cli->run();
@@ -52,12 +69,12 @@ public static function run(HelperSet $helperSet, $commands = [])
5269
/**
5370
* @return void
5471
*/
55-
public static function addCommands(Application $cli)
72+
public static function addCommands(Application $cli, ?ConnectionProvider $connectionProvider = null)
5673
{
5774
$cli->addCommands([
58-
new RunSqlCommand(),
75+
new RunSqlCommand($connectionProvider),
5976
new ImportCommand(),
60-
new ReservedWordsCommand(),
77+
new ReservedWordsCommand($connectionProvider),
6178
]);
6279
}
6380

@@ -74,14 +91,17 @@ public static function printCliConfigTemplate()
7491
following sample as a template:
7592
7693
<?php
77-
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
78-
79-
// replace with the mechanism to retrieve DBAL connection in your app
80-
$connection = getDBALConnection();
94+
use Doctrine\DBAL\Tools\Console\ConnectionProvider\SingleConnectionProvider;
8195
8296
// You can append new commands to $commands array, if needed
8397
84-
return ConsoleRunner::createHelperSet($connection);
98+
// replace with the mechanism to retrieve DBAL connection(s) in your app
99+
// and return a Doctrine\DBAL\Tools\Console\ConnectionProvider instance.
100+
$connection = getDBALConnection();
101+
102+
// in case you have a single connection you can use SingleConnectionProvider
103+
// otherwise you need to implement the Doctrine\DBAL\Tools\Console\ConnectionProvider interface with your custom logic
104+
return new SingleConnectionProvider($connection);
85105

86106
HELP;
87107
}

lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
/**
99
* Doctrine CLI Connection Helper.
10+
*
11+
* @deprecated use a ConnectionProvider instead.
1012
*/
1113
class ConnectionHelper extends Helper
1214
{

0 commit comments

Comments
 (0)