zendframework / zend-db

Db component from Zend Framework
BSD 3-Clause "New" or "Revised" License
101 stars 122 forks source link

Zend\Db\Adapter\AdapterServiceFactory always invoke Zend\Db\Adapter\Adapter #353

Open raivirtual opened 5 years ago

raivirtual commented 5 years ago

I created my own Db\Adapter\AdapterServiceFactory so I could be able to call my own Db\Adapter.

namespace RVMvp\Db\Adapter;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\AdapterServiceFactory as ZendAdapterServiceFactory;

class AdapterServiceFactory extends ZendAdapterServiceFactory
{
    public function createService(ServiceLocatorInterface $container)
    {
        return $this($container, Adapter::class);
    }
}

It should create a service of RVMvp\Db\Adapter\Adapter but keep creating Zend\Db\Adapter\Adapter.

This is happening because the invokable method is calling directly the Zend\Db\Adapter\Adapter instead of the requested name provided on createService method:

namespace Zend\Db\Adapter;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class AdapterServiceFactory implements FactoryInterface
{
    /**
     * Create db adapter service
     *
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param array $options
     * @return Adapter
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $config = $container->get('config');
        return new Adapter($config['db']); // HERE IT'S CALLING DIRECTLY
    }

    /**
     * Create db adapter service (v2)
     *
     * @param ServiceLocatorInterface $container
     * @return Adapter
     */
    public function createService(ServiceLocatorInterface $container)
    {
        return $this($container, Adapter::class);
    }
}

I did my workaround adding the invokable method to my own AdapterServiceFactory:

public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
 {
        $config = $container->get('config');
        return new $requestedName($config['db']); // CHANGED TO REQUESTED NAME
 }

I'm suggesting a bugfix so we don't need to override the invokable method.

alextech commented 5 years ago

I did my workaround adding the invokable method to my own AdapterServiceFactory:

That is how Service Manager expects factories to be. Depending on how you configured your service manager, this is not be a problem. I am guessing you have

invokables => [ RVMvp\Db\Adapter\AdapterServiceFactory::class]

somewhere in your module confugration?

raivirtual commented 5 years ago

But it's not calling by Service Manager. It's instancing directly.

pine3ree commented 5 years ago

Hello @raivirtual , You should define a factory for the adapter interface:

'dependencies' => [
    //...
    'factories' => [
        //...
        \Zend\Db\Adapter\AdapterInterface::class => \My\Db\AdapterFactory::class,
        //...
    ],
    //...
],

In servicemanager config the Zend\Db\Adapter\Adapter::class service name is actually an alias for Zend\Db\Adapter\AdapterInterface::class::class (see ConfigProvider in zend-db), but you should build your dependencies upon the AdapterInterface::class interface as service name. kind regards

weierophinney commented 4 years ago

This repository has been closed and moved to laminas/laminas-db; a new issue has been opened at https://github.com/laminas/laminas-db/issues/21.