如何在 Zend Framework 2 中为表单字段集使用服务?

How to use services for a form fieldset in Zend Framework 2?

我有一个表格 (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!
    }
}

它可以工作,但是字段集 class 中有两个依赖项。我想给他们注射。为此,我创建了一个 FooFieldsetFactory 并将 /module/MyModule/config/module.config.php 扩展为:

'service_manager' => [
    'factories' => [
        'Storage\Form\Fieldset\FooFieldset' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

工厂只是被忽略了。我想,服务定位器首先尝试通过命名空间查找 class,只有在没有找到的情况下,才会查看 invokablesfactories。好的。然后我创建了一个别名:

'service_manager' => [
    'factories' => [
        'Storage\Form\Fieldset\FooFieldset' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],
'aliases' => [
    'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\FooFieldset',
],

... 并尝试在我的表单 class 中使用它而不是 Storage\Form\Fieldset\FooFieldset。但是现在我得到一个例外:

Zend\Form\FormElementManager::get was unable to fetch or create an instance for Storage\Form\Fieldset\Foo

我也直接试过这个:

'service_manager' => [
    'factories' => [
        'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

没有效果,同样的错误。

这也没有用(同样的错误):

'form_elements' => [
    'factories' => [
        'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

因此,为字段集引用服务似乎不起作用。还是我做错了什么?

如何使用表单字段集服务?


更新

通过调试我发现,找不到我的 Foo 字段集工厂,因为它还没有添加到 Zend\Form\FormElementManager. Here is the place in the Zend\Form\Factoryfactories 列表中:

所以我的配置

'form_elements' => [
    'factories' => [
        'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

被忽略。如何解决这个问题?


更新 附加信息,我如何创建我的 Form 对象。

/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);
    }
}

这个问题是因为您在表单 __construct() 方法中添加表单元素,而不是 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 the init() method:

原因是新表单的工厂(用于使用add()创建新元素)必须有应用程序的表单元素管理器injected after the form's constructor has been called此表单元素管理器实例包含对在 form_elements 配置键下注册的自定义表单元素的所有引用。

通过以__construct the form factory will lazy load a new instance of the form element manager的形式调用add();它将能够创建所有默认表单元素,但不会知道您的自定义表单元素。