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

时间:2016-03-10 14:36:54

标签: zend-framework2 zend-form service-locator zend-framework-mvc servicemanager

我有一个表单(Zend\Form\Form),其中包含一些嵌套的字段集(Zend\Form\Fieldset)。构造非常类似于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!
    }
}

它可以工作,但是fieldset类中有两个依赖项。我想注射它们。为了做到这一点,我创建了FooFieldsetFactory并将/module/MyModule/config/module.config.php扩展为:

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

工厂被忽略了。我想,服务定位器首先尝试按命名空间查找类,但只有在找不到任何内容时,才能查看invokablesfactories。好。然后我创建了一个别名:

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

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

  

Zend \ Form \ FormElementManager :: get无法为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\FormElementManagerfactories列表中。以下是Zend\Form\Factory中的地点:

enter image description here

所以我的配置

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

1 个答案:

答案 0 :(得分:5)

此问题是因为您要在表单__construct()方法而不是init() as suggested in the documentation中添加表单元素。

  

您可以使用工厂而不是invokable来处理元素/ fieldsets / forms中的依赖项。

     

现在是第一个问题。

     

如果要通过扩展Zend\Form\Form来创建表单类,则不能在__construct中添加自定义元素 - 或者(正如我们在上一个使用自定义元素的FQCN的示例中所做的那样) ),而是在init()方法中:

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

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