Search code examples
phpzend-frameworkfactoryzf3

ZF3 multiple database adapters


in ZF2 it was possible to configurate multiple adapters like this in the module.config.php:

'db' => array(
    'adapters'=>array(
        'db1' => array(
            'driver' => 'Pdo',
            'dsn' => 'mysql:dbname=zf2;host=localhost',
            'driver_options' => array(
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
            ),
            'username' => 'zf2',
            'password' => 'zf2test',
        ),
        'db2' => array(
            'driver' => 'Pdo',
            'dsn' => 'mysql:dbname=zf1;host=localhost',
            'driver_options' => array(
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
            ),
            'username' => 'zf1',
            'password' => 'zf1test',
        ),
    )

),

In the controller factory I could get them via the ServiceManager:

class AlbumControllerFactory implements FactoryInterface
{

public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $albumTable = $serviceLocator->getServiceLocator()->get('Album\Model\AlbumTable');
        $db1Adapter = $serviceLocator->getServiceLocator()->get('db1');
        $db2Adapter = $serviceLocator->getServiceLocator()->get('db2');

        return new AlbumController($albumTable, $db1Adapter, $db2Adapter);
    }
}

Now I'm trying to do the same in Zend Framework 3 - but this nested array configuration doesn't work:

Fatal error: Uncaught exception 'Zend\Db\Adapter\Exception\InvalidArgumentException' with message 'createDriver expects a "driver" key to be present inside the parameters' in /var/www/USER/teckert/zf3/vendor/zendframework/zend-db/src/Adapter/Adapter.php:262

I think that in ZF 2 the adapters key are already handled when the dbAdapter is trying to create the driver - but this is not happening in ZF 3.

Any hints are warmly welcomed...

The manual of the zend-db with the adapters section wasn't clear enough for me

EDIT

According to this doc I've added the following snippet to the global config file:

'service_manager' => [
    'abstract_factories' => [
        \Zend\Db\Adapter\AdapterAbstractServiceFactory::class,
    ],
],

While trying to get the dbAdapter with $container->get('db1') in my AlbumTableFactory I get this error:

Unable to resolve service "db1 to a factory; are you certain you provided it during configuration?

Solution

  • Okay I finally resolved the problem.

    As mentioned by @Pieter I needed the following array content in my config.php:

    'service_manager' => [
        'abstract_factories' => [
            \Zend\Db\Adapter\AdapterAbstractServiceFactory::class,
        ],
    ],
    

    Additionally I had to change the process my classes and dependent factories are talking to each other.

    1. AlbumControllerFactory is called and get's the dependent AlbumTable service ($container->get(AlbumTable::class);) which then triggers the AlbumTableFactory
    2. AlbumTableFactory then is preparing the constructor injection for the AlbumTable with $tableGateway = $container->get(AlbumTableGateway::class);
    3. Now I combined the logic from the AlbumTableFactory and the AlbumTableGatewayFactory into one AbstractFactoryInterface (I removed the AlbumTableGatewayFactory completely)
    // AlbumTableFactory implements AbstractFactoryInterface
    
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $dbAdapter = $container->get('db1');
        $resultSetPrototype = new ResultSet();
        $resultSetPrototype->setArrayObjectPrototype(new Album());
    
        $tableGateway = new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
    
        return new AlbumTable($tableGateway);
    }