Zend Framework 2:从配置创建表单 - 如何修复 ServiceNotCreatedException

Zend Framework 2: Creating forms from configuration - how to fix ServiceNotCreatedException

我想通过配置在 Zend Framework 2 中创建一个表单,类似于 docs 中所述。因此,不要以 class 的形式对每个元素执行此操作:

// [...]

$this->add(array(
    'name' => 'fname',
    'type' => 'text',     
));

// [...]v

我想这样做:

// [...]

$form = $factory->createForm(array(
    'elements' => array(
        array(
            'spec' => array(
                'name' => 'fname',
                'type' => 'text',
            ),
        ),
    ),
));

// [...]

由于我是这个框架的新手,我仍然有点不确定事情的进展以及正确的做事方式,但我认为我的轨道很好——我只需要让事情正常进行更了解他们。

目前我的代码导致了一个异常,这让我有点困惑,虽然我以某种方式理解它:

An exception was raised while creating "MyForm"; no instance returned

除了我的困惑,我已经知道我的代码可能哪里出错了。

表单 class 看起来很简单,因为这里不会注入实际元素:

namespace My\Form;

use Zend\Form\Form;

class MyForm extends Form
{
    public function __construct()
    {
        parent::__construct();

        $this->setAttribute('method', 'post');
        $this->setAttribute('action', '/some-action');
    }
}

表单元素的配置如下所示:

namespace My\Form;

use Zend\Form\Factory;

class MyFormFactory extends Factory
{
    public function __construct()
    {
        $factory = new Factory();

        $form = $factory->createForm(array(
            'elements' => array(
                array(
                    'spec' => array(
                        'name' => 'fname',
                        'type' => 'text',
                    ),
                ),
            ),
        ));

        return $form;
    }
}

为了将两者连接起来,我在模块的 module.config.php:

中有这个服务管理器配置
'service_manager' => array(
    'factories' => array(
        'MyForm' => function ($servicemanager) {
            $form = new \My\Form\MyForm();
            $factory = $servicemanager->get('MyFormFactory');
            $form->add($factory);
            return $form;
        },
        'MyFormFactory' => function ($servicemanager) {
            $factory = new \My\Form\MyFormFactory();
            return $factory;
        },
    ),
),

最后但同样重要的是,控制器:

 namespace My\Controller;


use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class MyController extends AbstractActionController
{
    public function indexAction()
    {
        $form = $this->getServiceLocator()->get('MyForm');

        return new ViewModel(array(
            'form' => $form,
        ));
    }
}

如前所述,唯一发生的事情是非常清楚的异常:MyForm class 没有 return 实例。为了解决这个问题,我已经尝试 return 一些东西,但是那没有做任何事情。

需要更改什么才能创建表单?我的方法好还是我走错了路?

这里的一些事情可能会让你将来的生活更轻松。

  1. ZF2 附带一个 AbstractPluginManager 专门用于表单和表单元素,称为 FormElementManager。可以使用 form_elements 配置键从此管理器配置表单。

    在此处找到有关此内容的文档:http://framework.zend.com/manual/current/en/modules/zend.form.advanced-use-of-forms.html

    用这个代替一般的ServiceManager

  2. 在您的 MyForm 工厂回调中,$form->add() 需要 ElementInterfaceTraversable 的实例,您正在将其传递给工厂.我假设这是抛出 InvalidArgumentException。为此检查您的日志。我认为这是您问题的根源。

    此外,在您的 MyFormFactory 中,您正试图 return 来自工厂构造函数的表单。这将产生一些有趣的结果(加上打破 SRP),我会重新考虑 class 完全。

  3. 为获得最佳实践,请尽可能避免在控制器中使用 ServiceManager。我知道它在那里,而且我知道如果您需要服务,抓住它是一件容易的事,但该表格实际上应该在控制器自己的工厂中注入到您的控制器中(在 controllers -> factories 配置键)

为了通过配置创建表单,您将需要使用 Zend\Form\Factory,这将return 表单元素基于您的配置提供它。让我们称之为 "FormFactory".

但是,要在 FormElementManager 中将此表单注册为名为 'MyForm' 的服务,您需要 创建一个服务管理器工厂。 "factory" 是 class 实现 Zend\ServiceManager\FactoryInterface。让我们称之为 "ServiceFactory".

如果我要更正您的代码,我会按以下方式进行。

  • 删除classMyForm。没有真正需要实际扩展基本形式 Zend\Form\Form;这是因为您只设置了表单的属性,可以通过 FormFactory 来完成(您给它配置这些属性,它将 return 一个已经设置好的新表单)。

  • 删除MyFormFactory。为了创建我们的新表单,我们不需要扩展表单工厂或创建它的新实例,而是我们 使用 它是客户端代码中的 API。

  • 删除service_manager下的配置。我们应该使用 'FormElementManager' 注册我们的表单(使用 `form_elements' 配置键)。 'plugin manager' 配置为 return 表单元素。

通过这些更改,您只需一个 "ServiceFactory" 即可创建表单。例如。

namespace My\Factory;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class MyFormFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $formElementManager)
    {
        // Get the config of the new form
        $config = $this->getElementConfig($formElementManager);

        // Get the service manager
        $serviceManager = $formElementManager->getServiceLocator();

        // Get the form factory (service that creates the form)
        // from the service manager, FormFactory is the default
        $formFactory = new \Zend\Form\Factory($formElementManager); 

        // Create the form using the factory and config and then return it
        return $formFactory->create($config);
    }

    /** Return the config for the factory **/
    protected function getElementConfig(ServiceLocatorInterface $formElementManager)
    {
        // you could use the form element manager / service manager
        // here to load the config from elsewhere...
        return [
            'name' => 'myform',
            'type' => 'Zend\Form\Form',
            'attributes' => [
                'method' => 'post',
                'action' => '/foo/bar',
            ],
            'fieldsets' => [
                /....
            ],
            'elements' => [
               //...
            ]
        ];
    }

}

然后在您的 module.config.php 中您可以将工厂注册为新服务​​

'form_elements' => [
    'factories' => [
        'MyForm' => 'My\Factory\MyFormFactory',
    ],
],

注意上面的配置在 'form_elements' 键下。当我们使用表单元素管理器注册服务时,您的控制器代码也应该更新。

$serviceManager = $this->getServiceLocator();
$form = $serviceManager->get('FormElementManager')->get('MyForm');