phppdoslimphp-dislim-4

How to set up and inject multiple PDO database connections in slim 4?


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?


Solution

  • You have multiple options:

    1. Extending PDO
    2. Autowired objects

    1. Extending PDO

    use PDO;
    
    class PDO2 extends PDO
    {
        // must be empty
    }
    

    The container definition:

    use PDO2;
    
    // ...
    
    return [
        PDO::class => function (ContainerInterface $container) {
            return new PDO(...);
        },
    
        PDO2::class => function (ContainerInterface $container) {
            return new PDO2(...);
        },
    ];
    

    Usage

    use PDO;
    use PDO2;
    
    class MyRepository
    {
        private $pdo;
    
        private $pdo2;
        
        public function __construct(PDO $pdo, PDO2 $pdo2)
        {
            $this->pdo = $pdo;
            $this->pdo2 = $pdo2;
        }
    }
    

    2. Autowired objects

    See Matthieu Napoli's answer: https://stackoverflow.com/a/57758106/1461181