【问题标题】:How to Extend a Fieldset in ZF2 to work with Doctrine’s Class Table Inheritance mapping strategy如何在 ZF2 中扩展字段集以使用 Doctrine 的类表继承映射策略
【发布时间】:2014-04-24 23:36:07
【问题描述】:

我正在使用 Doctrine 的类表继承映射策略开发一个项目,该策略涉及根据父表中鉴别器列中的值将父表与多个子表之一连接起来。我有一个工作原型,其中唯一的综合字段集每个都包含来自父实体的公共元素的所有代码的重复副本。为了确保一致性并避免过多代码,我想更改字段集,以便我有一个与父实体相关的单个字段集,所有其他字段集只是父实体的扩展(详细说明包含在 How to Extend a Fieldset in ZF2 )。当我分离字段集然后尝试让它们相互协作时遇到了问题。

How to Extend a Fieldset in ZF2 问题的第一个答案清楚地解释了单个字段集如何在 ZF2 中扩展另一个字段集。但是,答案使用init() 来组合字段集,而对于 Doctrine 策略,我们需要使用__construct。我开发字段集的第一个步骤是构建DoctrineModule/docs 中规定的一些代码,这将我们带到了这一点:

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 ...

zend2 doctrine 2 form intgergration OneToOne 问题的第一个答案解释了在 OneToOne 策略的情况下如何避免从字符串中添加字段集。但是,我正在使用不同的 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 EditFooForm extends Form
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('edit_foo_form');

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

        $fooFieldset = new FooFieldset($objectManager);
        $fooFieldset->setUseAsBaseFieldset(true);
        $this->add($fooFieldset);

        // submit elements
    }
}

此字段集有效:

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),);

    }

}

use MyModule\Form\FoobarFieldset;

class FooFieldset extends FoobarFieldset 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'));

    }

    public function getInputFilterSpecification()
    {
        return array('fieldD' => array('required' => false),);
        return array('fieldE' => array('required' => false),);
        return array('fieldF' => array('required' => false),);
        return array('fieldG' => 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);

    }

但这会给出带有消息No element by the name of [fieldA] found in formZend\Form\Exception\InvalidElementException,表明未添加文件集。

另一种选择

说到底,我真正想做的就是将所有常用语句保存在一个地方,并将它们绘制到需要包含它们的各种独特字段集中。我可以在没有 ZF2 的情况下使用 include 语句解决这个问题,如下所示:

// FoobarFieldset_fields.php

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

// FoobarFieldset_filters.php

return array('fieldA' => array('required' => false),);
return array('fieldB' => array('required' => false),);
return array('fieldC' => array('required' => false),);

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());

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

    }

    public function getInputFilterSpecification()
    {
        include 'FoobarFieldset_filters.php';
        return array('fieldD' => array('required' => false),);
        return array('fieldE' => array('required' => false),);
        return array('fieldF' => array('required' => false),);
        return array('fieldG' => array('required' => false),);

    }

}

【问题讨论】:

  • 您是否在getFormElementConfig 中为Fieldsets 添加了工厂?在我看来,服务管理器将其创建为invokable(如果您在将字段集添加到表单并且没有注册工厂时使用 FQCN,这将自动发生)。请使用工厂类/闭包以及添加字段集的表单更新您的问题。
  • 我能够让类表继承映射策略与一个实体一起扩展另一个实体,然后通过单个字段集将数据传递给表单。对于那个模型,我不需要使用getFormElementConfig。只有当我将字段集分成两个(一个扩展另一个)时,我才会遇到问题。我假设问题在于我如何进行参考。我已编辑问题以包含更多信息。

标签: doctrine-orm doctrine zend-framework2


【解决方案1】:

您遇到的问题与字段集的 __construct 相关

“父母”

class FoobarFieldset extends Fieldset {
    public function __construct(ObjectManager $objectManager) {}

但是,在“子”中,您正在调用父 __construct 并传递一个字符串(应该是 $objectManager

class FooFieldset extends FoobarFieldset implements InputFilterProviderInterface
{
  public function __construct(ObjectManager $objectManager)
  {
     parent::__construct('foo-fieldset'); // This should be $objectManager, not string

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

目前您正在使用 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() 添加它来创建 fieldset(而不是使用 new 在表单中创建字段集)

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

        // ....

    }
}

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

这是解释within the documentation

[...]你不能直接实例化你的表单类,而是通过Zend\Form\FormElementManager获取它的一个实例:

还有:

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

【讨论】:

  • __construct() 中为孩子使用$objectManager 纠正了我的问题。接下来我将研究建议的改进。
猜你喜欢
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 2015-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
相关资源
最近更新 更多