EDIT 9 november 2012 : there will be soon a better solution for such a problem starting from Zend Framework 2.1. You can learn more in my another blog post here.
Sometimes, you will need to have the instance of the service manager in your Forms classes, for example the Doctrine 2′s entity manager, or retrieving fieldsets that need specific dependencies.
A simple solution to this problem is to create a class that extends Zend\Form\Form and implements ServiceManagerAwareInterface. Then, the setServiceManager will call a function called init, where you will create your forms, while being able to retrieve the service manager.
Here is such a class :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php namespace Common\Form; use Zend\Form\Form as BaseForm; use Zend\ServiceManager\ServiceManager; use Zend\ServiceManager\ServiceManagerAwareInterface; class Form extends BaseForm implements ServiceManagerAwareInterface { /** * @var ServiceManager */ protected $serviceManager; /** * Init the form */ public function init() { } /** * @param ServiceManager $serviceManager * @return Form */ public function setServiceManager(ServiceManager $serviceManager) { $this->serviceManager = $serviceManager; // Call the init function of the form once the service manager is set $this->init(); return $this; } } |
And for example a Register form :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace Member\Form\Student; use Common\Form\Form; class Register extends Form { /** * Init the form */ public function init() { var_dump($this->serviceManager); // VALID SERVICE MANAGER } } |
Thanks for this post. We ran into this issue when building a form containing a select box, whose options needed to be dynamically populated by a call to a service. If the form has a hard dependency on a service or object, I personally prefer not to make it aware of a service locator, but rather declare the dependency explicitly as a constructor parameter. You can then have a simple factory for your form, such as this:
‘my_form’ => function($sm) {
$productService = $sm->get(‘product_service’);
return new Form\Product($productService);
}
This way, there’s no hidden dependencies within the form due to the form internally pulling things with $sm->get(). You can easily tell exactly what is required for the form to operate properly.
However, when pulling your form from the service manager, this does require you create a factory that instantiates the form with he requried dependencies. Your method may appeal to a RAD methodology, as no factories would be required — the form could function as a simple invokable due to automatically calling init() when the service manager is injected by the initializer.
This is the good idea. The only (minor) problem is that if this is not the main form that needs it BUT a fieldset (contained in the form), we cannot use the factory to create fieldset (as we must give it the service manager as a parameter).
I’ll try to code this solution and update my blog post.
Hi Michael!
Did you find out a solution about using service manager into fieldsets ?
Hi,
I didn’t find an acceptable solution. Therefore I switched back to created a Registry (yes, with static functions) to be able to retrieve the entity manager in my forms. Even if this is not a recommended practice, I believe in some cases it can be the simplest/cleanest solution. In this case, it is.
What to you think about create fieldset factories and inject them into forms ?
Or maybe such like this into myform:
$fieldset = new MyFielset();
$fieldset->setServiceManager($this->getServiceManager);
?
Hey thanks for the post! Have a question. I don’t see how the setServiceManager method gets called. Does it happen automatically just by implementing ServiceManagerAwareInterface or would we have to write some more code somewhere to call it first?
Thanks
Ha, yeah. It’s not that “magical”. You have to pull the form from the ServiceManager. And the ServiceManager, when creating the form object, will automatically “inject” the ServiceManager to every class that implements ServiceManagerAwareInterface.
Basically, you have to add an entry in invokables array of the SM :
‘invokables’ => array(
‘my.form’ => ‘Common\Form\Form’
)
and then pull it in your code :
$form = $serviceManager->get(‘my.form’);
and your form will contain the service manager.
I enjoy, cause I discovered exactly what I used to be looking for.
You’ve ended my four day lengthy hunt! God Bless you man. Have a great day. Bye
Hi,
You’re welcome ! However remember that starting from ZF 2.1, there is a better way to handle those dependencies (see here: http://www.michaelgallego.fr/blog/2012/11/09/discover-whats-coming-for-zendform-in-zf-2-1/).
Have a merry christmas.