问题
I could make an instance of PDO
and inject it successfully. I defined the PDO::class
directly and injected it in the constructor with __construct(PDO $pdo)
. I would need something like PDO1::class
and PDO2::class
to inject it like follows: __construct(PDO1 $pdo1, PDO2 $pdo2)
but that obviously doesn't work. There is only one PDO
class and what I need to do is 2 instances of it with different database credentials.
What is the best way to do it?
I set up one definition of a database via PDO like this and it works:
File: dependencies.php
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
PDO::class => function (ContainerInterface $c) {
$dbSettings = $c->get('settings')['db1'];
$dsn = 'mysql:host=' . $dbSettings['host'] . ';dbname=' . $dbSettings['dbname'];
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
return new PDO($dsn, $dbSettings['user'], $dbSettings['pass'], $options);
},
]);
};
File: index.php
...
// Set up dependencies
$dependencies = require __DIR__ . '/../app/dependencies.php';
$dependencies($containerBuilder);
// Build PHP-DI Container instance
$container = $containerBuilder->build();
// Set container to create App with on AppFactory
AppFactory::setContainer($container);
// Instantiate the app
$app = AppFactory::create();
...
File SomeRepository.php
use PDO;
class SomeRepository{
protected $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
}
I've seen something like this in this article:
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
'db1' => function (ContainerInterface $c) {
$db1Settings = $c->get('settings')['db1'];
$dsn = 'mysql:host=' . $db1Settings['host'] . ';dbname=' . $db1Settings['dbname'];
$options = [ ... ];
return new PDO($dsn, $db1Settings['user'], $db1Settings['pass'],$options);
},
'db2' => function (ContainerInterface $c) {
$db2Settings = $c->get('settings')['db2'];
$dsn = 'mysql:host=' . $db2Settings['host'] . ';dbname=' . $db2Settings['dbname'];
$options = [ ... ];
return new PDO($dsn, $db2Settings['user'], $db2Settings['pass'],$options);
},
]);
};
But is it the best way to do it? And how can I access the connections in a repository class without having to inject the whole container?
回答1:
You have multiple options:
- The connection proxy
- Extending PDO
- Autowired objects
1. The connection proxy
Example:
use PDO;
class ConnectionProxy
{
private $pdo;
private $pdo2;
public function __construct(PDO $pdo, PDO $pdo2)
{
$this->pdo = $pdo;
$this->pdo2 = $pdo2;
}
public function getPdo(): PDO
{
return $this->pdo;
}
public function getPdo2(): PDO
{
return $this->pdo2;
}
}
The container definition:
return [
ConnectionProxy::class => function (ContainerInterface $c) {
return new ConnectionProxy(
$c->get('db1'),
$c->get('db2')
);
},
'db1' => function (ContainerInterface $c) {
return new PDO();
},
'db2' => function (ContainerInterface $c) {
return new PDO();
},
];
Usage
class MyService
{
private $pdo;
private $pdo2;
public function __construct(ConnectionProxy $connectionProxy)
{
$this->pdo = $connectionProxy->getPdo();
$this->pdo2 = $connectionProxy->getPdo2();
}
}
2. Extending PDO
class PDO1 extends PDO
{
}
class PDO2 extends PDO
{
}
The container definition:
return [
PDO1::class => function (ContainerInterface $c) {
return new PDO();
},
PDO2::class => function (ContainerInterface $c) {
return new PDO();
},
];
Usage
class MyService
{
private $pdo;
private $pdo2;
public function __construct(PDO1 $pdo, PDO2 $pdo2)
{
$this->pdo = $pdo;
$this->pdo2 = $pdo2;
}
}
3. Autowired objects
See Matthieu Napoli's answer: https://stackoverflow.com/a/57758106/1461181
回答2:
If you have multiple instances of a class in your app (here you have multiple instances of the PDO
class), then you must configure which one to inject every time.
That means that PDO
cannot be autowired, because PHP-DI cannot decide which instance you want depending on the service/controller/etc.
You need to use configuration (see http://php-di.org/doc/php-definitions.html#autowired-objects) to define which instance (db1
or db2
in your example) to inject for each service.
return [
MyService::class => DI\autowire()
->constructorParameter('pdo', DI\get('db1'))
->constructorParameter('pdo2', DI\get('db2')),
'db1' => function (ContainerInterface $c) {
return new PDO();
},
'db2' => function (ContainerInterface $c) {
return new PDO();
},
];
来源:https://stackoverflow.com/questions/57758020/how-to-set-up-and-inject-multiple-pdo-database-connections-in-slim-4