I have a form (Zend\Form\Form
) with some nested fieldsets (Zend\Form\Fieldset
) in it. The construction is pretty similar to that in the Form Collections tutorial.
Storage\Form\MyForm
|_'Storage\Form\Fieldset\FooFieldset'
|_'Storage\Form\Fieldset\BarFieldset'
|_'Storage\Form\Fieldset\BazFieldset'
...
MyForm
class MyForm {
public function __construct()
{
...
$this->add(
[
'type' => 'Storage\Form\Fieldset\FooFieldset',
'options' => [
'use_as_base_fieldset' => true
]
]
);
}
}
FooFieldset
class FooFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('foo');
$this->setHydrator(new ClassMethodsHydrator())->setObject(new Foo()); // dependencies!
}
}
It works, but the fieldset class has two dependencies in it. I want to inject them. In order to do it, I created a FooFieldsetFactory
and extended the /module/MyModule/config/module.config.php
by:
'service_manager' => [
'factories' => [
'Storage\Form\Fieldset\FooFieldset' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
],
],
The factory is simply being ignored. I guess, the service locator first tries to find the class by namespace and only if nothing is found, takes a look in the invokables
and factories
. OK. Then I created an alias:
'service_manager' => [
'factories' => [
'Storage\Form\Fieldset\FooFieldset' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
],
],
'aliases' => [
'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\FooFieldset',
],
... and tried to use it instead of Storage\Form\Fieldset\FooFieldset
in my form class. But now I get an exception:
Zend\Form\FormElementManager::get was unable to fetch or create an instance for Storage\Form\Fieldset\Foo
I've also tried this directly:
'service_manager' => [
'factories' => [
'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
],
],
No effect, the same error.
And this also didn't work (the same error):
'form_elements' => [
'factories' => [
'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
],
],
So the referencing a service for a fieldset seems not to work. Or am I doing something wrong?
How to use services for form fieldsets?
UPDATE
With some debuggin I found out, that my Foo
fieldset factory cannot be found, because it has not be added to the factories
list of the Zend\Form\FormElementManager
. Here is the place in the Zend\Form\Factory
:
So my config
'form_elements' => [
'factories' => [
'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
],
],
is ignored. How to fix this?
UPDATE Additional information, how I'm creating my Form
object.
/module/Foo/config/module.config.php
return [
'controllers' => [
'factories' => [
'Foo\Controller\My' => 'Foo\Controller\Factory\MyControllerFactory'
]
],
'service_manager' => [
'factories' => [
'Foo\Form\MyForm' => 'Foo\Form\Factory\MyFormFactory',
],
],
];
/module/Foo/src/Foo/Form/Factory/MyFormFactory.php
namespace Foo\Form\Factory;
use ...;
class MyFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$form = new MyForm();
$form->setAttribute('method', 'post')
->setHydrator(new ClassMethods())
->setInputFilter(new InputFilter());
return $form;
}
}
/module/Foo/src/Foo/Controller/Factory/MyControllerFactory.php
namespace Foo\Controller\Factory;
use ...;
class MyControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$fooPrototype = new Foo();
$realServiceLocator = $serviceLocator->getServiceLocator();
// $myForm = $realServiceLocator->get('Foo\Form\MyForm'); <-- This doesn't work correctly for this case. The FormElementManager should be used instead.
$formElementManager = $realServiceLocator->get('FormElementManager');
$myForm = $formElementManager->get('Foo\Form\MyForm');
return new MyController($myForm, $fooPrototype);
}
}
This issue is because you are adding your form elements in the forms __construct()
method rather than init()
as suggested in the documentation.
You can use a factory instead of an invokable in order to handle dependencies in your elements/fieldsets/forms.
And now comes the first catch.
If you are creating your form class by extending
Zend\Form\Form
, you must not add the custom element in the__construct
-or (as we have done in the previous example where we used the custom element’s FQCN), but rather in theinit()
method:
The reason is that the new form's factory (which is used to create new elements using add()
) must have the application's form element manager injected after the form's constructor has been called This form element manager instance contains all the references to your custom forms elements which are registered under the form_elements
configuration key.
By calling add()
in the form __construct
the form factory will lazy load a new instance of the form element manager; which will be able to create all default form elements but will not have any knowledge of your custom form element.