Configure multi DB connections on ZF2 - zend-framework2

I'm actualy a beginner in ZF2
I managed to use multiple BDD on the same application and it works.
(I'm talking about this : configure multiple databases in zf2 ).
Though, I'd have a little question...
Is it ok to declare my custom factory in global.php ? (in the service_manager thing).
Or do I need to declare it inside each module ? (in module.php)
Declaring it into global.php actualy works, but I was wondering if it's not breaking the spirit of the framework or something...
Thanks for your time !
Tounu

Store your connection settings in a local config:
config/autoload/local.php
this is in case you have multiple environments with different databases/connection credentials etc. for example, you may gave a staging setup, and a live setup, both using a separate database.
You can also then use multiple connections inside your application this way too.
there's nothing to stop you setting up multiple connections in here, and using them as needed in your database adapters etc.
local.php
return array(
/**
* Database Connection One
*/
'db' => array(
'driver' => 'pdo',
'dsn' => 'mysql:dbname=dbnamehere;host=localhost',
'username' => 'root',
'password' => '',
),
/**
* Database Connection Two
*/
'db_two' => array(
'driver' => 'pdo',
'dsn' => 'mysql:dbname=anotherdb;host=localhost',
'username' => 'root',
'password' => '',
),
If you are using version control (you should be!) this also allows you to exclude the .local config files from your repository to avoid storing password etc in there, and allows for easier deployment to multiple environments.
You can setup multiple adapters to use different connections too:
global.php
return array(
/**
* Database Adapter(s)
*/
'service_manager' => array(
'factories' => array(
/**
* Adapter One - this factory will use the default 'db' connection
*/
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
/**
* Adapter Two - use the second connection
*/
'Application\Db\AdapterTwo' => function($sm) {
$config = $sm->get('Config');
return new Adapter($config['db_two']);
},
),
),
);

To connect multiple database at a time, follow these steps:
Step 1:
Create /module/MyModule/ and add into application.config.ini to access.
Step 2:
Create Module.php in /module/MyModule/ directory with the following scripts
<?php
namespace MyModule;
use MyModule\MyAdapterFactory;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements ServiceProviderInterface{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__.'/Db/Adapter/',
),
),
);
}
public function getServiceConfig()
{
return array(
'factories' => array(
'adapter1' => new MyAdapterFactory('db_adapter1'),
'adapter2' => new MyAdapterFactory('db_adapter2'),
),
);
}
}
Step 3:
Create MyAdapterFactory.php in the path: /module/MyModule/src/MyModule/Db/Adapter/ with the following scripts.
<?php
namespace MyModule;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\Adapter;
class MyAdapterFactory implements FactoryInterface
{
protected $configKey;
public function __construct($key)
{
$this->configKey = $key;
}
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('Config');
return new Adapter($config[$this->configKey]);
}
}
?>
Step 4:
Add the following scripts in your getServiceConfig().
'YourModule\Model\YourTable' => function($sm) {
$tableGateway = $sm->get('YourTableGateway');
$table = new YourTable($tableGateway);
return $table;
},
'YourTableGateway' => function ($sm) {
$adapter1 = $sm->get('adapter1');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new YourModel());
return new TableGateway('tbl_name', $adapter1, null, $resultSetPrototype);
},
Step 5:
Add method into your controller to access your table as below:
Declare this on start of the class:
protected $this->yourTable;
public function getYourTable()
{
if (!$this->yourTable) {
$sm = $this->getServiceLocator();
$this->yourTable = $sm->get('YourModule\Model\YourTable');
}
return $this->yourTable;
}
Then, You can call your Model methods for Select, Update, Insert using this function (getYourTable()) in your controller.

Related

ZF2 translator is not working in controller?

I am trying to translate in the controller by ServiceLocator, but this is not translating and I have tried many sulotions in stackoverflow but with out success. My system uses multiple languages and my goal is to use transtor in view, controller, form and filter. Tranlator in my view is working. Any sugestion and help will be appreciated.
Not working in controller:
$this->getServiceLocator()->get('translator')->translate('my text',$myLocale);
My Application mudole.config.php:
'service_manager' => array(
'abstract_factories' => array(
'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
'Zend\Log\LoggerAbstractServiceFactory',
),
'factories' => array(
'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory',
),
),
'translator' => array(
'locale' => 'en_US',// 'locale' => 'dk_DK',
'translation_file_patterns' => array(
array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
I changed the local in mudole.config.php to another language but still not translating.
View Helper/Forms
ZF2 ships with the view helper Zend\I18n\View\Helper\Translate; this is why you can already use the method $this->translate($text) in the view.
However all view helper classes that extend from Zend\I18n\View\Helper\AbstractTranslatorHelper (which includes all form view helpers) are also 'translation capable'.
You would need to pass in the translator using $viewHelper->setTranslator($translator) and enabling translation via $viewHelper->setTranslatorEnabled(true).
Controller Plugin
Unfortunately there is no default plugin (that I could find) to handle translators in controllers; I guess you could argue that text content shouldn't be in the controller anyway.
You could easily create one such as the example below. The key is to pass your new translator service as a dependency via a factory.
namespace MyModule\Controller\Plugin;
use Zend\Mvc\Controller\AbstractPlugin;
use Zend\I18n\Translator\Translator as TranslatorService;
class Translator extends AbstractPlugin
{
protected $translatorService;
public function __construct(TranslatorService $translatorService)
{
$this->translatorService = $translatorService;
}
public function invoke($text = null, array $options = [])
{
if (null == $text) {
return $this;
}
return $this->translate($text, $options);
}
public function translate($text, array $options = [])
{
return $this->translatorService->translate($text);
}
}
And create the factory class.
namespace MyModule\Controller\Plugin;
use MyModule\Controller\Plugin\Translator;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class TranslatorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllerPluginManager)
{
$serviceManager = $controllerPluginManager->getServiceLocator();
return new Translator($serviceManager->get('translator'));
}
}
Register the service in module.config.php.
return [
'controller_plugins' => [
'factories' => [
'translate' => 'MyModule\\Controller\\Plugin\\TranslateFactory',
]
],
];
Then you can just call it within a controller class.
// Directly
$this->translate($text, $options);
// Or fetch the plugin first
$this->translate()->translate($text, $options);
It seems that the locale is sets not directly in the translating text, but by $this->getServiceLocator()->get('translator')->setLocale($locale), and now it is translating my text.
My Application mudole.config.php:
'service_manager' => array(
'factories' => array(
'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory',
),
),
'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
And in the controller:
$this->getServiceLocator()->get('translator')->setLocale($locale);
echo $c=$this->getServiceLocator()->get('translator')->translate('Book'); // Print(Danish): Bog
You can use my ZfTranslate controller plugin.
installation
composer require mikica/zf2-translate-plugin
You need to register new module. Add in file config/application.config.php:
'modules' => array(
'...',
'ZfTranslate'
),
Usage in controller
<?php
$this->translate('translate word');
$this->translate('translate word', 'locale');
AlexP answer is the best way to do it.
But remains a question, why your way doesn't work?
It should work. But it doesn't because you are in different namespaces, so you are using distinct domains among the files. You doing something like that:
namespace MyModule\Controller;
class MyController {
public function someAction() {
$this->getServiceLocator()->get('translator')->translate('my text',__NAMESPACE__,$myLocale);
}
}
While at your `module.config.php', you probably using this namespace:
namespace MyModule;
return array(
//...
'translator' => array(
//...
),
);
Note that in the example of the controller __NAMESPACE__ is equals MyModule\Controller. While at the config file the __NAMESPACE__ is equals MyModule. You need to fix it, passing the same value in both cases.
In other words, there are several approachs to solve this, like AlexP's, for instance. But, any one of them need to have the domain of translator (the value of the 'text_domain' key) when you configure it equals the domain parameter (second paramater) of the translate method when you call it.
The faster solution is changing the $domain parameter to string at the controller file:
$this->getServiceLocator()->get('translator')->translate('my text','MyModule',$myLocale);
Another solution should be creating a constant and using it at the files (controllers, views and config).

ZF2 set static dbAdapter in config loader

I have following setup in my development.global.php:
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
'dbAdapter' => function($sm) {
$config = $sm->get('config');
$config = $config['db'];
$dbAdapter = new Zend\Db\Adapter\Adapter($config);
return $dbAdapter;
},
),
),
An then, I'm loading static adapter in onBootstrap() of one of Module's Model class:
$dbAdapter = $e->getApplication()->getServiceManager()->get('dbAdapter');
\Zend\Db\TableGateway\Feature\GlobalAdapterFeature::setStaticAdapter($dbAdapter);
Is there any possibility to set that just once in config autoloader ? Currrently, if I do that, I still need to call setStaticLOader somewhere in the Module code.
UPDATE: as stated below, that's imposible - at least by standard way.
You can not avoid calling it explicitly in onBootstrap.
General rule is for global/static state to be avoided. Explicitly inject db adapter in factories for you TDG objects instead.
If you still insist on using it, suggest to use delegator factory to make it a bit more flexible. See blogpost for more info about delegators.
Add this to your module config:
'service_manager' => array(
'aliases' => array(
'globalDbAdapter' => 'Zend\Db\Adapter\Adapter',
),
'delegators' => array(
// Use alias to make it easier to chose which adapter to set as global
'globalDbAdapter' => array(
'YourModule\Factory\GlobalDbAdapterDelegator',
),
),
)
and then delegator factory:
namespace YourModule\Factory;
use Zend\ServiceManager\DelegatorFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\TableGateway\Feature\GlobalAdapterFeature;
class GlobalDbAdapterDelegator implements DelegatorFactoryInterface
{
public function createDelegatorWithName(
ServiceLocatorInterface $serviceLocator,
$name,
$requestedName,
$callback
) {
$dbAdapter = $callback();
GlobalAdapterFeature::setStaticAdapter($dbAdapter);
return $dbAdapter;
}
}
and finally in onBootstrap method
// Force creation of service
$e->getApplication()->getServiceManager()->get('globalDbAdapter');

Is there a way to add a log writer factory into the logger configuration?

I write logger config and used Zend\Log\LoggerServiceFactory for configure Logger. If I use base writers all work. But I want add to a logger my own writer who created by factory, and this is donn`t work.
Is there a way to use config and base logger factory to add writer from own factory?
Update: here is my code
This is my config where I define factory for Logger, factory for writer and configs for base writers
// config/autoload/global.php
return array(
'service_manager' => array(
'factories' => array(
'Logger' => 'Zend\Log\LoggerServiceFactory',
'Rollbar' => 'Yassa\Rollbar\Log\Writer\Rollbar'
),
),
'log' => array(
'writers' => array(
array(
'name' => 'stream',
'options' => array(
...
),
),
array(
'name' => 'stream',
'options' => array(
...
),
),
array(
'name' => 'Rollbar',
),
),
),
);
Yassa\Rollbar\Log\Writer\Rollbar - it`s a factory from yassa\rollbar module (github)
Without Rollbar writer this config does what I need - create and configure standart writers.
Thus I call logger from aontroller:
$this->getServiceLocator()->get('Logger')->info('test');
I researched the component code and did not find a way to use the factory to create my own writer. Unfortunately currently this is not possible.
I think I found the solution. The solution is not very nice, but nothing better I figured it out.
I wrote a replacement for standard factory (\Zend\Log\LoggerServiceFactory) and transfer logger configuration from the Logger constructor in the my factory. In addition, I added a check for the writer factory.
Here is the resulting class:
<?php
namespace Application\Factory;
use Zend\Log\Exception\InvalidArgumentException;
use Zend\Log\Logger;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class LoggerServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
// Configure the logger
$config = $serviceLocator->get('Config');
$options = isset($config['log']) ? $config['log'] : array();
$logger = new Logger();
if (is_array($options)) {
if (isset($options['writers']) && is_array($options['writers'])) {
foreach ($options['writers'] as $writer) {
if (!isset($writer['name'])) {
throw new InvalidArgumentException('Options must contain a name for the writer');
}
$priority = (isset($writer['priority'])) ? $writer['priority'] : null;
$writerOptions = (isset($writer['options'])) ? $writer['options'] : null;
if ($serviceLocator->has($writer['name'])) {
$writer = $serviceLocator->get($writer['name']);
$logger->addWriter($writer, $priority, $writerOptions);
} else {
$logger->addWriter($writer['name'], $priority, $writerOptions);
}
}
}
if (isset($options['exceptionhandler']) && $options['exceptionhandler'] === true) {
Logger::registerExceptionHandler($logger);
}
if (isset($options['errorhandler']) && $options['errorhandler'] === true) {
Logger::registerErrorHandler($logger);
}
}
return $logger;
}
}
Can anybody suggest a better solution?

ServiceNotFoundException in ZendFramework 2, example from Rob Allen

for the past few weeks I have been following ZF2 especially Rob Allen's 'Album' example , I have created the example DB-'zf2tutorial' and example table-'album', which works fine fetching all the items when I use php-mysql, so problems with the data in the DB.
My local.php looks like this
config.autoload/local.php:
return array(
'db' => array
(
'driver' => "Pdo",
'dsn' => "mysql:dbname=zf2tutorial;hostname=localhost",
'username' => "user", //here I added my valid username
'password' => "password", //here I added my valid password
'driver_options' => array
(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"
),
),
);
Module.php
**module/Album/Model.php
<?php
namespace Album;
use Album\Model\AlbumTable;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getServiceConfiguration()
{
return array(
'factories' => array(
'album-table' => function($sm) //also tried this 'Album\Model\AlbumTable' => function($sm)
{
$dbAdapter = $sm->get('db-adapter');//also tried this $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new AlbumTable($dbAdapter);
return $table;
},
),
);
}
}
I just wanted to check whether the zf2turorial/album works or not it does throw this error which is similar to this post here in stackoverlow.
The error which it is throwing is:
Additional information:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
..\vendor\zendframework\zendframework\library\Zend\ServiceManager\ServiceManager.php:392
Message:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Album\Model\AlbumTable
I have followed ZF2 Beta 5 tutorial as well, but still encountering this problem. In case if anyone has a solution, please do share with us.
Thanks
seems like somebody has forgotten to update the newest changes in zf2.
the solution:
the file module/Album/Module.php has to contain this content:
<?php
namespace Album;
use Album\Model\AlbumTable;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements ServiceProviderInterface
then you have to rename
public function getServiceConfig()
to
public function getServiceConfiguration()
public function getServiceConfiguration() is not the correct function name
rename getServiceConfiguration() to getServiceConfig()
although it is not mandatory you should declare the class Module as
class Module implements ServiceProviderInterface
I am not sure but try to put the code into
config.autoload/global.php: like this way
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=kd_tutorial;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
I have encoutered the same problem when try to implement code follow zend tutorial. The main reason is order-folder
module
ModuleX
config
src
ModuleX //may be you forget this one
Controller
Factory
Model
...
view
Module.php
in Module.php, getConfig(), getAutoloaderConfig() is enough to run
class Module implements AutoloaderProviderInterface, ConfigProviderInterface{
public function getAutoloaderConfig(){
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig(){
return include __DIR__ . '/config/module.config.php';
}
Modify config/autoload/global.php with following code:
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=YourDBName;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
and You should put your database credentials in config/autoload/local.php so that they are not in the git repository (as local.php is ignored):
return array(
'db' => array(
'username' => 'YourDBUsername',
'password' => 'YourDBPassword',
),
);
If you want more details refer link1 or link2
Just got the same issue.
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Cache\Model\UniversityStorage
In my case the problem was, that PHPUnit could not find the key Cache\Model\UniversityStorage in the service manager (registry) because it's being added in the Module class of the Cache module and the Cache module was missing in the PHPUnit config file and was also not being loaded:
/module/Application/test/phpunit.config.php
<?php
return array(
'modules' => array(
'Application',
'Cache', // <- was missing
),
'module_listener_options' => array(
'config_glob_paths' => array(
'../../../config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'module',
'vendor',
),
),
);
I've added the Cache module to the module list. Now it works.
The problem is that within config/autoload/global.php the service is named "Zend\Db\Adapter\Adapter" while within module/Album/Module.php it was searching for "Zend\Db\Adapter" (missing additional \Adapter).
Adding missing "\Adapter" within module/Album/Module.php like this fixed it for me.
'AlbumTableGateway' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
In module/Album/src/Album/Model/Album.php, use instead:
array('name' => 'striptags'),
array('name' => 'stringtrim'),
See the above namespaces created by Zend at vendor/zendframework/zendframework/library/Zend/Filter/FilterPluginManager.php
If you are getting similar error for different modules and the code seems jus right then ADD an entry in composer.json
"Album\": "module/Album/src/"
in autoload and autoload-dev section like this where Album is the name of your Module
"autoload": {
"psr-4": {
"Application\\": "module/Application/src/",
"Album\\": "module/Album/src/"
}
},
"autoload-dev": {
"psr-4": {
"ApplicationTest\\": "module/Application/test/",
"Album\\": "module/Album/src/"
}
},
after this run "composer update" and reload the page. check if error is gone.

How do I get the Service Manager in Zend Framework 2 beta4 to create an instance for album-table?

This is the Rob Allen's Quick start Tutorial for Zend Framework beta4.
Error Message:Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for album-table
It seems like it fails trying to make a connection to the db, but I have not found way to tell. It's uses a closure to return an instance from the ServiceManager, but gets the above error message.
module/Album/Module.php
namespace Album;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getServiceConfiguration()
{
$albumTable = array(
'factories' => array(
'album-table' => function($sm) {
$dbAdapter = $sm->get('db-adapter');
$table = new AlbumTable($dbAdapter);
return $table;
},
),
);
return $albumTable;
}
}
namespace Application;
use Zend\Db\Adapter\Adapter as DbAdapter,
class Module
{
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getServiceConfiguration()
{
$factoryDBAdaptor = array(
'factories' => array(
'db-adapter' => function($sm) {
$config = $sm->get('config');
$config = $config['db'];
$dbAdapter = new DbAdapter($config);
return $dbAdapter;
},
),
);
return $factoryDBAdaptor;
}
}
config\autoload\global.php
return array(
'db' => array(
'driver' => 'PDO',
'dsn' => 'mysql:dbname=zf2tutorial;hostname=localhost',
'username' => 'user',
'password' => 'password',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
);
It's related to the fact that Zend Framework's master has changed since Beta 4 and so my beta 4-targeted tutorial no longer works with latest ZF master.
Also, the SM may have previous exceptions, so you should check if there are any previous exceptions as that may show an underlying error.
Update
As of 11th July 2012, my tutorial is now updated for Beta 5. It now uses the Db Adapter's ServiceFactory to create the adapter and so you don't even need to modify Application's Module class any more.
Make sure the main Module.php has a reference the getServiceConfiguration(). I had the same problem and had forgotten to include it.
module/Application/Module.php:
<?php
namespace Application;
use Zend\Db\Adapter\Adapter as DbAdapter;
class Module
{
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getServiceConfiguration()
{
return array(
'factories' => array(
'db-adapter' => function($sm) {
$config = $sm->get('config');
$config = $config['db'];
$dbAdapter = new DbAdapter($config);
return $dbAdapter;
},
),
);
}
}
update your composer.json file with following line.
"zendframework/zendframework": "dev-master#18c8e223f070deb07c17543ed938b54542aa0ed8"
run following commands you will be good to go.
php composer.phar self-update
php composer.phar update
php composer.phar install
Fixed this error by disabling toolbar. Just go to config/autoload/zend-developer-tools.local-development and set toolbar to false.
'toolbar' => [
/**
* Enables or disables the Toolbar.
*
* Expects: bool
* Default: false
*/
'enabled' => false,

Resources