phpzend-framework3zend-translatezend-servicemanager

ZF3 Translator Remote Loader Factory Unable to resolve service



I have a problem using MVCTranslator with translations stored in the database.

I configured the translations to use remote_translation, but I can not create a factory for my custom loader. My files look like this:

module.config.php

'translator' => [                
    'locale' => 'en_US',
    'translation_file_patterns' => [
        [
            'type'     => 'phparray',
            'base_dir' => getcwd() .  '/data/language',
            'pattern'  => '%s.php',
            'text_domain' => 'default',
        ],             
    ],        
    'remote_translation' => [
        [                
            'type' => Model\DatabaseTranslationLoader::class,
            'text_domain' => 'default',                
        ]
    ],

],

Module.php

namespace MyNamespace;

use Zend\ModuleManager\Feature\ConfigProviderInterface;

class Module implements ConfigProviderInterface
{
    public function getConfig()
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    /*
    ....
    ...
    ....
    */      

    public function getControllerConfig()
    {        
        return [
            'factories' => [
                Controller\MyController::class => function($container) {                   

                    $translator = $container->get('MvcTranslator');                    

                    $translator->getPluginManager()->setFactory(Translator\DatabaseTranslationLoader::class, Factory\DatabaseTranslationLoaderFactory::class); 

                    return new Controller\MyController(
                        $translator
                    );
                },
            ],
        ];
    }

}

Translator\DatabaseTranslationLoader.php

namespace MyNamespace\Translator;

use Zend\Db\Adapter\Adapter as DbAdapter;
use Zend\I18n\Translator\Loader\RemoteLoaderInterface;

class DatabaseTranslationLoader implements RemoteLoaderInterface
{
    protected $dbAdapter;

    public function __construct(DbAdapter $adapter)
    {        
        $this->dbAdapter = $adapter;
    }

    public function load($locale, $filename)
    {
        // Database operations
    }

Factory\DatabaseTranslationLoaderFactory.php

namespace MyNamespace\Factory\DatabaseTranslation;

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

class DatabaseTranslationLoaderFactory implements FactoryInterface
{       
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $dbAdapter = $container->get(Zend\Db\Adapter\AdapterInterface:class);           

        return new Translator\DatabaseTranslationLoader($dbAdapter);
    }
}

With this configuration, when I try to retrieve database adapter in DatabaseTranslationLoaderFactory receives an exception from the service manager:

Unable to resolve service "Zend\Db\Adapter\Adapter" to a factory; are you certain you provided it during configuration?

It looks like the factory has an empty container (a new ServiceManager instance?).

What am I doing wrong? Does anyone have any idea how to do this? I will be grateful for any suggestions.

EDIT:

As mentioned above, it looks like this is a new serviceMenager instance in the DatabaseTranslationLoaderFactory.
The same error gets when I try to do:

$container->get('router')

or

$container->get('request')

Solution

  • After hours of searching, I found a solution to my problem.
    The explanation of the problem was found in Zend\I18n\Translator\Translator.php:

    public function setPluginManager(LoaderPluginManager $pluginManager)
    {
        $this->pluginManager = $pluginManager;
    
        return $this;
    }
    
    public function getPluginManager()
    {
        if (! $this->pluginManager instanceof LoaderPluginManager) {
            $this->setPluginManager(new LoaderPluginManager(new ServiceManager));
        }
    
        return $this->pluginManager;
    }
    

    From the above, getPluginManager by default creates a new LoaderPluginManager instance with a new instance of ServiceManager.

    In my case, to have access to the ServiceManager in the DatabaseTranslationLoaderFactory I had to first use the setPluginManager method, as follows:

    Module.php

    namespace MyNamespace;
    
    use Zend\ModuleManager\Feature\ConfigProviderInterface;
    
    class Module implements ConfigProviderInterface
    {
        public function getConfig()
        {
            return include __DIR__ . '/../config/module.config.php';
        }
    
        /*
        ....
        ...
        ....
        */      
    
        public function getControllerConfig()
        {        
            return [
                'factories' => [
                    Controller\MyController::class => function($container) {                   
    
                        $translator = $container->get('MvcTranslator');  
    
                        // This line solved my problem
                        $translator->setPluginManager(new \Zend\I18n\Translator\LoaderPluginManager($container));
    
                        $translator->getPluginManager()->setFactory(Translator\DatabaseTranslationLoader::class, Factory\DatabaseTranslationLoaderFactory::class); 
    
                        return new Controller\MyController(
                            $translator
                        );
                    },
                ],
            ];
        }
    }
    

    I do not know if this solution is correct, but it works.
    Maybe it will help someone :)