Doctrine orm 如何扩展ZF2中的字段集以使用条令’;s类表继承映射策略

Doctrine orm 如何扩展ZF2中的字段集以使用条令’;s类表继承映射策略,doctrine-orm,doctrine,zend-framework2,Doctrine Orm,Doctrine,Zend Framework2,我正在开发一个使用Doctrine的类表继承映射策略的项目,该策略涉及根据父表中的鉴别器列中的值将父表与多个子表中的一个子表连接起来。我已经有了一个工作原型,其中唯一的联合字段集每个都包含来自父实体的公共元素的所有代码的副本。为了确保一致性并避免过多的代码,我想更改字段集,这样我就有了一个与父实体相关的字段集,而所有其他字段集都只是父实体的扩展(详细说明见)。当我分离字段集,然后试图让它们彼此工作时,我遇到了问题 问题的第一个答案清楚地解释了ZF2中单个字段集如何扩展另一个字段集。然而,答案是使

我正在开发一个使用Doctrine的类表继承映射策略的项目,该策略涉及根据父表中的鉴别器列中的值将父表与多个子表中的一个子表连接起来。我已经有了一个工作原型,其中唯一的联合字段集每个都包含来自父实体的公共元素的所有代码的副本。为了确保一致性并避免过多的代码,我想更改字段集,这样我就有了一个与父实体相关的字段集,而所有其他字段集都只是父实体的扩展(详细说明见)。当我分离字段集,然后试图让它们彼此工作时,我遇到了问题

问题的第一个答案清楚地解释了ZF2中单个字段集如何扩展另一个字段集。然而,答案是使用
init()
将字段集放在一起,对于条令策略,我们需要使用
\u构造
。我开发字段集的第一步是在

class FieldsetParent extends Zend\Form\Fieldset
{
   public function __construct(ObjectManager $objectManager) {

        parent::__construct('parent-fieldset');

        $this->setHydrator(new DoctrineHydrator($objectManager, 'MyModule\Entity\Parent'))
             ->setObject(new Parent());

        $this->add(array('name' => 'fieldA'));
        $this->add(array('name' => 'fieldB'));
        $this->add(array('name' => 'fieldC'));
   }
}
这是:

class FieldsetFoo extends FieldsetParent
{
   public function __construct(ObjectManager $objectManager) {

        parent::__construct('foo-fieldset');

        $this->setHydrator(new DoctrineHydrator($objectManager, 'MyModule\Entity\Foo'))
             ->setObject(new Foo());

        $this->add(array('name' => 'fieldD'));
        $this->add(array('name' => 'fieldE'));
        $this->add(array('name' => 'fieldF'));
        $this->add(array('name' => 'fieldG'));
   }
}
这不起作用,因为它试图从字符串添加字段集,并给出错误消息:

Catchable fatal error: Argument 1 passed to MyModuule\Form\ParentFieldset::__construct() 
must implement interface Doctrine\Common\Persistence\ObjectManager, string given ...
问题的第一个答案解释了在OneTONE策略中如何避免从字符串添加字段集。然而,我使用的是一种不同的ORM策略,我很难解决同样的问题

编辑 根据要求,以下是一些更详细的信息:

class FooController extends AbstractActionController
{
    /**
     * @var Doctrine\ORM\EntityManager
     */
    protected $em;

    public function setEntityManager(EntityManager $em)
    {
        $this->em = $em;
        return $this;
    }

    public function getEntityManager()
    {
        if (null === $this->em) {
            $this->em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
        }
        return $this->em;
    }

    // ... //

    public function editAction()
    {
        $fooID = (int)$this->getEvent()->getRouteMatch()->getParam('fooID');
        if (!$fooID) {
            return $this->redirect()->toRoute('foo', array('action'=>'add'));
        }

        $foo = $this->getEntityManager()->find('MyModule\Entity\Foo', $fooID);

        // Get your ObjectManager from the ServiceManager
        $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');

        // Create the form and inject the ObjectManager
        $form = new EditFooForm($objectManager);
        $form->setBindOnValidate(false);
        $form->bind($foo);
        $form->get('submit')->setAttribute('label', 'Update');

        $request = $this->getRequest();
        if ($request->isPost()) {
            $form->setData($request->getPost());
            if ($form->isValid()) {
                $form->bindValues();
                $this->getEntityManager()->flush();

                return $this->redirect()->toRoute('foo');
            }
        }

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

}

此字段集工作:

class FooFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('foo-fieldset');

        $this->setHydrator(new DoctrineHydrator($objectManager, 'MyModule\Entity\Foo'))
             ->setObject(new Foo());

        $this->add(array('name' => 'fieldA'));
        $this->add(array('name' => 'fieldB'));
        $this->add(array('name' => 'fieldC'));
        $this->add(array('name' => 'fieldD'));
        $this->add(array('name' => 'fieldE'));
        $this->add(array('name' => 'fieldF'));
        $this->add(array('name' => 'fieldG'));

    }

    public function getInputFilterSpecification()
    {
        return array('fieldA' => array('required' => false),);
        return array('fieldB' => array('required' => false),);
        return array('fieldC' => array('required' => false),);
        return array('fieldD' => array('required' => false),);
        return array('fieldE' => array('required' => false),);
        return array('fieldF' => array('required' => false),);
        return array('fieldG' => array('required' => false),);

    }

}
这些字段集提供错误消息:

class FoobarFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('foobar-fieldset');

        $this->setHydrator(new DoctrineHydrator($objectManager, 'MyModule\Entity\Foobar'))
             ->setObject(new Foobar());

        $this->add(array('name' => 'fieldA'));
        $this->add(array('name' => 'fieldB'));
        $this->add(array('name' => 'fieldC'));

    }

    public function getInputFilterSpecification()
    {
        return array('fieldA' => array('required' => false),);
        return array('fieldB' => array('required' => false),);
        return array('fieldC' => array('required' => false),);

    }

}

产生的错误消息是:

Catchable fatal error: Argument 1 passed to MyModule\Form\FoobarFieldset::__construct() 
must implement interface Doctrine\Common\Persistence\ObjectManager, string given, called
in C:\xampp\htdocs\GetOut\module\MyModule\src\MyModule\Form\FooFieldset.php on line 17 
and defined in C:\xampp\htdocs\GetOut\module\MyModule\src\MyModule\Form\FoobarFieldset.php
on line 14
我尝试通过以下更改来避免从字符串添加字段集:

use MyModule\Form\FoobarFieldset;

class FooFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('foo-fieldset');

        $this->setHydrator(new DoctrineHydrator($objectManager, 'MyModule\Entity\Foo'))
             ->setObject(new Foo());

        $this->add(array('name' => 'fieldD'));
        $this->add(array('name' => 'fieldE'));
        $this->add(array('name' => 'fieldF'));
        $this->add(array('name' => 'fieldG'));

        $fieldset = new FoobarFieldset($objectManager);
        $this->add($fieldset);

    }
但这会给出一个
Zend\Form\Exception\InvalidElementException
,其中包含一条消息,
Form
中找不到名为[fieldA]的元素,表示未添加文件集

替代方案 当一切都说了又做了,我真正想做的就是把所有的公共语句放在一个地方,并将它们引入到需要包含它们的各种独特的字段集中。我可以通过使用
include
语句,在没有ZF2的情况下解决这个问题,如下所示:

// FoobarFieldset_fields.php

$this->add(array('name' => 'fieldA'));
$this->add(array('name' => 'fieldB'));
$this->add(array('name' => 'fieldC'));


您遇到的问题与字段集的
\uu构造有关

“家长”

class FoobarFieldset extends Fieldset {
    public function __construct(ObjectManager $objectManager) {}
但是,在“child”中,您正在调用父
\u构造
传递字符串(应该是
$objectManager

还有一些额外的东西可以改进您的代码

当前,您正在使用new关键字创建表单

$form = new EditFooForm($objectManager);
这很好(可以工作),但是您应该通过服务管理器加载它,以便将工厂连接到它

$form = $this->getServiceLocator()->get('MyModule\Form\EditFoForm');
然后,您将注册一个工厂来创建新表单和字段集(将所有构造代码保存在一个地方)

Module.php

public function getFormElementConfig()
{
  return array(
    'factories' => array(
       'MyModule\Form\EditFooForm' => function($fem) {
          // inject form stuff
          $sm = $fem->getServiceLocator();
          $om = $sm->get('object_manager');

          return new EditFooForm($om);
       },
       'MyModule\Form\EditFooFieldset' => function($fem) {
          // inject fieldset stuff
          $sm = $fem->getServiceLocator();
          $om = $sm->get('object_manager');

          $fieldset = new EditFooFieldset($om);

          $hydrator = $sm->get('HydratorManager');
          // you can also create the hydrator via a factory
          // and inject it outside the form, meaning you don't need to do so
          // within the form
          $hydrator = $hydrator->get('MyFooHydrator'); 
          $fieldset->setHydrator($hydrator);

          return $fieldset;
       },

    ),
  );
}
最后,;允许Zend\Form\Factory通过
$this->add()
添加字段集来创建字段集(而不是使用
新建
在表单中创建字段集)

请注意,在上一个示例中,表单元素添加到
init()
中,这是因为当您从服务管理器请求表单元素时,
FormElementManager
将调用
init()
。这是一个重要的关注点分离,因为它允许您通过
\u构造
注入(创建表单时)提供依赖项,然后在设置所有表单属性后分别添加表单元素

解释如下:

[…]您不能直接实例化表单类,而是通过
Zend\form\FormElementManager
获取它的实例:

而且:

如果您是通过扩展
Zend\form\form
来创建表单类,则不能在
\uuu构造中添加自定义元素,或者(正如我们在前面的示例中使用自定义元素的FQCN所做的那样),而是在
init()
方法中添加自定义元素:


您是否为
getFormElementConfig
中的
字段集添加了工厂?在我看来,服务管理器正在将其创建为一个可调用的
(如果您在表单中添加字段集且未注册工厂时使用FQCN,则会自动发生这种情况)。请使用factory类/闭包以及添加字段集的表单更新您的问题。我能够使类表继承映射策略与一个实体一起扩展另一个实体,然后通过单个字段集将数据传递到表单。对于该模型,我不需要使用
getFormElementConfig
。只有当我将字段集分成两个(一个扩展另一个)时,我才会遇到问题。我假设问题在于我是如何做参考的。我对问题进行了编辑,以包含更多信息。在孩子的
\u构造()
中使用
$objectManager
纠正了我的问题。我将在下一步研究建议的改进。
class FoobarFieldset extends Fieldset {
    public function __construct(ObjectManager $objectManager) {}
class FooFieldset extends FoobarFieldset implements InputFilterProviderInterface
{
  public function __construct(ObjectManager $objectManager)
  {
     parent::__construct('foo-fieldset'); // This should be $objectManager, not string
$form = new EditFooForm($objectManager);
$form = $this->getServiceLocator()->get('MyModule\Form\EditFoForm');
public function getFormElementConfig()
{
  return array(
    'factories' => array(
       'MyModule\Form\EditFooForm' => function($fem) {
          // inject form stuff
          $sm = $fem->getServiceLocator();
          $om = $sm->get('object_manager');

          return new EditFooForm($om);
       },
       'MyModule\Form\EditFooFieldset' => function($fem) {
          // inject fieldset stuff
          $sm = $fem->getServiceLocator();
          $om = $sm->get('object_manager');

          $fieldset = new EditFooFieldset($om);

          $hydrator = $sm->get('HydratorManager');
          // you can also create the hydrator via a factory
          // and inject it outside the form, meaning you don't need to do so
          // within the form
          $hydrator = $hydrator->get('MyFooHydrator'); 
          $fieldset->setHydrator($hydrator);

          return $fieldset;
       },

    ),
  );
}
    class FooFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function init()
    {
        //.....
        // Allow the fieldset to be loaded via the FormElementManager 
        // and use the new factory
        $this->add(array(
            'name' => 'foo_fieldset',
            'type' => 'MyModule\Form\EditFooFieldset',
        ));

        // ....

    }
}