Zend Framework 2 Zend_DB MasterSlaveFeature - zend-framework2

The documentation mentions the ability to do master-slave by passing the MasterSlaveFeature to a TableGateway constructor along with the slave adapter as a parameter to the MasterSlaveFeature. My question is how to get access to that slave adapter when instantiating the TableGateway object.
I'm setting my master up in the main global and local configuration files, as per the quick start. How would I go about configuring the slave adapter(s) and how would I set up more than one slave?
Is there a working example of how to do this?
UPDATE: I've made some progress on this. In the global config file, I added another array key called slave and entered the connection information there. Then I created a SlaveAdapterServiceFactory class which mimics the behavior of Zend\Db\Adapter\AdapterServiceFactory but grabs the "slave" config key values instead of "db". Then, in my module config where I do the constructor injection of the master adapter, I also grab the slave adapter and pass it to my model table as the constructor parameter to a new MasterSlaveFeature instance. I also had to alter my model table class to look for Feature stuff getting passed in the constructor. Since the modal table class extends AbstractTableGateway I would expect it to know what to do with the Feature. Hopefully that assumption is correct. This all sounds really confusing! Here is some code. First the global.php from the config/autoload directory:
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=zf2test;host=one.host.com',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'slave' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=zf2test;host=two.host.com',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
'My\Db\Adapter\SlaveAdapter'
=> 'My\Db\Adapter\SlaveAdapterServiceFactory'
),
)
);
Here is the code from the module's module.config.php file:
return array(
'service_manager' => array(
'factories' => array(
'Album\Model\AlbumTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$slaveAdapter = $sm->get('My\Db\Adapter\SlaveAdapter');
$table = new AlbumTable($dbAdapter, new MasterSlaveFeature($slaveAdapter));
return $table;
}
)
)
);
I haven't had a chance to test if the queries are actually going to the correct connections but I'm not getting any errors.
One other question is how to automatically pass these adapters to any models that get created instead of having to do this for each and every model that you instantiate.

This should help you out: MasterSlaveFeature How-To
However talking with the current developer on this area of ZF2 there does appear to be a bug. I've filed a report based on his recommendation so once that is resolved, the example I have shown should work, combined with what you have as well =)

Related

How can I use getServiceLocator() to get an adapter in a Model?

I am learning ZF2.
Can I use getServiceLocator() to get a adapter in a Model?
/config/autoload/global.php
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=zf2tutorial;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',
),
'aliases' => array(
'db' => 'Zend\Db\Adapter\Adapter',
),
),
);
/config/autoload/local.php
return array(
'db' => array(
'username' => 'YOUR USERNAME HERE',
'password' => 'YOUR PASSWORD HERE',
),
);
So, how can I use:
$sm = $this->getServiceLocator();
$this->dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
to get a adapter in a Model?
You will need to inject the adapter into your model when it's created, for example using a factory.
for example in your config:
'service_manager' => array(
'factories' => array(
'Application\Some\Model' => '\Application\Some\ModelFactory'
)
)
You would then create the factory which will inject the adapter into your model:
<?php
namespace Application\Some;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ModelFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$adapter = $serviceLocator->get('Zend\Db\Adapter\Adapter');
return new Model($adapter);
}
}
You would then obviously need to make your model accept the adapter in it's constructor in this case.
Now where ever you need to get an instance of your model with the adapter injected you'd call:
$serviceLocator->get('Application\Some\Model');
This will call the factory and bring back the model complete with adapter.
You would then use the same kind of approach to inject your model into any controllers or service classes where it's needed. As said in a previous post try and avoid injecting the service locator/Service manager directly into your objects, but rather use it to add the items you need (adapters/mappers etc) from inside Factory classes etc.
If you are using the latest Zend Framework version you cannot use the getServiceLocator method in your controller classes since both the ServiceLocatorAwareInterface and the ServiceManagerAwareInterface were removed.
So this line:
$sm = $this->getServiceLocator();
Won't work as you maybe expected in your controller class.
You can also read more on this change in the migration guide:
The following interfaces, traits, and classes were removed:
...
Zend\ServiceManager\ServiceLocatorAwareInterface
Zend\ServiceManager\ServiceLocatorAwareTrait
Zend\ServiceManager\ServiceManagerAwareInterface
The ServiceLocatorAware and ServiceManagerAware interfaces and traits were too often abused under v2, and represent the antithesis of the purpose of the Service Manager component; dependencies should be directly injected, and the container should never be composed by objects.
You will need to refactor your service and probably the best way to do that is to create a service factory where you inject your dependencies.
Check also this blog post (chapter Factories) on more details on how to build a factory.

Get new instances of ZF2 ViewHelper/Form/etc

Is it possible to get a new instance of a ZF2 View Helper, Form and the like on each call?
My main issue at the moment is with view helpers. Each time I call the helper I need it to be constructed as need to have new objects passed into it but it doesn't matter what config I have it only ever passes through the factory the once.
With that the objects which are generated via the factory are the same for each instance, which Is not what I'm wanting.
I've tried various configs such as setting the view helper as shared, but that hasn't helped.
This is my config as it currently stands;
/**
*
*/
'view_helpers' => array(
'shared' => array(
'notes' => false,
),
'factories' => array(
'notes' => __NAMESPACE__ . '\Factory\ViewHelper\Notes',
'note' => __NAMESPACE__ . '\Factory\ViewHelper\Note',
)
),
Any help would be great!
You're almost there.
The shared config key is to mark the services that require a new instance to be created each time you request them.
The problem however, is that you have to use the service name.
'view_helpers' => [
'shared' => [
'notes' => false,
],
'factories' => [
'notes' => __NAMESPACE__ . '\Factory\ViewHelper\Notes',
],
],

ServiceManager returns same Instance while shared = false

I defined an alias for MyClass that I want to be retrievable via ServiceManager, see module.config.php example below. When I retrieve an instance of MyClass in my controller via $this->serviceLocator->get('MyClass') I get a new instane the first time, and then the same instance. Do I miss something in my configuration or should I file a bug?
module.config.php
return array(
'di' => array(
'instance' => array(
'alias' => array(
'MyClass' => 'Some\Namespace\MyClass',
),
'MyClass' => array(
'shared' => false,
),
),
),
);
As my edit was not accepted, I will answer this question myself. In the end I moved to ServiceManager thanks to Stoyan Dimov who pushed me into the right direction. Unfortunately his answer was not correct. I had to use invokables instead of alias. Here is the correct example:
return array(
'invokables' => array(
'MyClass' => 'Some\Namespace\MyClass',
),
'shared' => array(
'MyClass' => false,
),
);
When I was using alias instead of invokables I got an exception that no service with that name can be is available, which means that aliasdoes not work, there has to be an existing service defined by invokables, factories, services or abstract_factories.
Following the previous example I could use:
...
'alias' => array(
'SomethingDifferent' => 'MyClass',
),
...
With a call to $this->serviceLocator->get('SomethingDifferent')I would receive an instance of Some\Namespace\MyClass.
I don't know about the DI but the ServiceManager expects your to specify shared (false) in the root of the configuration array for the manager. Something like this:
'invokables' => array( // Note that you can use also 'factories', 'abstract_factories', etc.
'Some\Namespace\MyClass' => 'Some\Namespace\MyClass',
),
'alias' => array(
'MyClass' => 'Some\Namespace\MyClass',
),
'shared' => array(
'MyClass' => false,
),
See the ServiceManager example configuration on the manual
Hope this helps :)
Stoyan

How to put translator in Zend2 form

Iam new guy to Zend framework and currently Iam working on Zend2...I want to ask about Translator usage in Zend forms....If i want to use translator i directly using for labels in form view i.e.form_view.php like
$this->formLabel()->setTranslator($translator, 'date_of_birth');
But I want to add the translator at the form only i.e.in src/my_module/Form/UserForm.php
like
$this->add(array(
'name' => 'date_of_birth',
'attributes' => array(
'type' => 'text',
'id' => 'date_of_birth',
),
'options' => array(
'label' => 'DateOfBirth',
), //Here there is any option to put translator
));
Please help me...any answer would be help for me like I asked
Thanks in advance
You don't really need to do that. Since the the Translator that is set up using the factory-key translator will automatically be injected into the Form.
The best approach (in my opinion) is to make extensive use of the translator text_domain:
'translator' => array(
'locale' => 'de_DE',
'translation_file_patterns' => array(
array(
'type' => 'phparray',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.php',
'text_domain' => 'MyModuleTextDomain'
),
),
),
With this setup, the Files of your Module will automatically be inserted into the default TranslatorService which every Zend\Form knows of.
So ultimately all you have to do is make the ViewHelpers know of the TextDomain that you are using. And this is done in the following manner:
$this->formLabel()->setTranslatorTextDomain('MyModuleTextDomain');
$this->formButton()->setTranslatorTextDomain('MyModuleTextDomain');
$this->formElementErrors()->setTranslatorTextDomain('MyModuleTextDomain');
You need to do this once inside your respective view.phtml before(!) using the ViewHelpers like $this->formElement($element) or $this->formCollection($form)
And that's really all there is to it. I recall having seen a discussion somewhere about making it easier to pass along Text-Domain-Data, but i can't find it right now. So things may get a little easier in the future ;) For now, 3 lines are all that's needed though!
above answer is quite unnecessary ... as your translator was added automatically to zend form for rendering form labels and ....
only use this code in your module config :
'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
array(
'type' => 'phparray',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.php',
),
),
),
if u use the correct view helpers for rendering form elements (or whole form) it will automatically translated
This is not a recommended approach because forms are translated automatically if you have a translator configured (which you do if you are using the Skeleton Application). However, since you asked how to use the translator directly within your form, I will show you how you can do it. Please carefully consider if you really want to do this, as I cannot imagine a use case where it would be necessary.
To do exactly what you were asking, you can inject the translator into your form. You can do this either in your controller or in a factory. I will be using a factory in this example because it is more DRY.
// In your module's config file
'service_manager' => array(
'factories' => array(
'YourModule\Form\YourForm' => function($sm) {
$translator = $sm->get('Translator');
return new \YourModule\Form\YourForm($translator);
},
),
),
Then in your form class, you can do like this:
namespace YourModule\Form;
class RegisterForm extends \Zend\Form\Form {
public function __construct($translator) {
// Do something
$translated_string = $translator->translate('string to translate');
}
}
Then in your controller, you can do like this:
$your_form = $this->servicelocator->get('YourModule\Form\YourForm');
Or if you don't want to use the factory, you can choose to not add it and do like this instead:
$your_form = new \YourModule\Form\YourForm($this->servicelocator->get('Translator'));
I would recommend going with the factory, though.

Zend Framework 2 (ZF2) dynamic locale

I am looking for a way to add translations to my ZF2 application, using globals in my URL.
Is there anyway to do this for the whole application at once?
A typpical URL would look like this: http://domain.com/en_GB/user/index
The first part (en_GB) should be used to show the correct translation.
Besides that, it would be nice, if it is possible to set this router part optional.
So, if I should go to http://domain.com/user/index (without the locale part) to my application, it should automatically take the browser locale.
I hope I am clear enough, if any additions are needed to this question, feel free to ask.
Thanx in advance
#DrBeza,
Thank you for your answer. I don't know if this is the correct way, but I created the next solution:
in /config/global.php I added this part
'translator' => array(
'locale' => 'nl_NL',
'translation_file_patterns' => array(
array(
'type' => 'phpArray',
'base_dir' => __DIR__ . '/../../language',
'pattern' => '%s.php',
),
),
),
in /module/[modulename]/config/module.config.php I added this part to set the first part of the URL containing the locale
'router' => array(
'routes' => array(
'user' => array(
'options' => array(
'route' => '[/:lang]/user[/:action][/:id]',
'constraints' => array(
'lang' => '([a-z]{2})+(_)+([A-Z]{2})',
),
),
),
),
),
in /config/local.php I added this code to fetch the locale from the URL:
http://domain.com/[locale][module][controller]
$_SERVER['REQUEST_URI_PATH'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segment = explode('/', $_SERVER['REQUEST_URI_PATH']);
And I added this part to load the locale dynamicly:
return array(
'translator' => array(
'locale' => $segment[1],
);
I would suggest extending the Segment route class and adding in the optional locale constraint and segment part if missing. Call the optional variable something application wide such as 'locale'.
Then create a 'route' event in the main module bootstrap, this event will fire once a route has been matched. The callback function that is fired will have access to the RouteMatch object through the passed event, letting you gain access to the 'locale' value. Then you can do some checks and set the application locale.

Resources