Doctrine OneToOne Bi- and Unidirectional with ZF2 Fieldsets not saving/hydrating

Doctrine OneToOne Bi- and Unidirectional with ZF2 Fieldsets not saving/hydrating

我有 2 个 Doctrine 实体(EnvironmentEnvironmentConfig)。他们有 bi-directional OneToOne 关系。

每个实体都有自己的 Fieldset,因此很容易重复使用。

要创建一个 Environment 它也可以有一个 EnvironmentConfig,但不是必需的。为了允许它们同时制作,我有一个使用 EnvironmentFieldsetEnvironmentConfigFieldset.

EnvironmentForm

表单正确呈现。但是,它保存 Environment 而不是 EnvironmentConfig

我的设置哪里出错了,如何解决?

下面的代码,省略 getters/setters 会太多。

实体

// abstract class AbstractEntity has $id + getters/setters.

class Environment extends AbstractEntity
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected $name;

    /**
     * @var EnvironmentConfig
     * @ORM\OneToOne(targetEntity="Environment\Entity\EnvironmentConfig", inversedBy="environment")
     */
    protected $config;

    /**
     * @var EnvironmentScript
     * @ORM\OneToOne(targetEntity="EnvironmentScript")
     */
    protected $script;

    //Getters/setters
}

class EnvironmentConfig extends AbstractEntity
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected $name;

    /**
     * @var Environment
     * @ORM\OneToOne(targetEntity="Environment\Entity\Environment", mappedBy="config")
     */
    protected $environment;

    //Getters/setters
}

字段集

class EnvironmentFieldset extends AbstractFieldset
{
    /**
     * {@inheritdoc}
     */
    public function init()
    {
        //Loads id element validation
        parent::init();

        $this->add([
            'name' => 'name',
            'type' => Text::class,
            'options' => [
                'label' => _('Name'),
                'label_attributes' => [
                    'class' => 'col-xs-2 col-form-label',
                ],
            ],
            'attributes' => [
                'id' => 'name',
                'class' => 'form-control'
            ],
        ]);

        $this->add([
            'name' => 'environment_config',
            'type' => EnvironmentConfigFieldset::class,
            'options' => [
                'use_as_base_fieldset' => false,
            ],
        ]);

        $this->add([
            'type' => ObjectSelect::class,
            'name' => 'environment_script',
            'options' => [
                'object_manager' => $this->getEntityManager(),
                'target_class'   => EnvironmentScript::class,
                'property'       => 'id',
                'display_empty_item' => true,
                'empty_item_label'   => '---',
                'label_generator' => function ($targetEntity) {
                    return $targetEntity->getId() . ' - ' . $targetEntity->getName();
                },
            ],
        ]);
    }
}

class EnvironmentConfigFieldset extends AbstractFieldset
{
    /**
     * {@inheritdoc}
     */
    public function init()
    {
        //Loads id element validation
        parent::init();

        $this->add([
            'name' => 'name',
            'type' => Text::class,
            'options' => [
                'label' => _('Name'),
                'label_attributes' => [
                    'class' => 'col-xs-2 col-form-label',
                ],
            ],
            'attributes' => [
                'id' => 'name',
                'class' => 'form-control'
            ],
        ]);

    }
}

表格

class EnvironmentForm extends AbstractForm
{
    /**
     * EnvironmentForm constructor.
     * @param null $name
     * @param array $options
     */
    public function __construct($name = null, array $options)
    {
        //Also adds CSRF
        parent::__construct($name, $options);
    }

    /**
     * {@inheritdoc}
     */
    public function init()
    {
        //Call parent initializer. Adds submit button.
        parent::init();

        $this->add([
            'name' => 'environment',
            'type' => EnvironmentFieldset::class,
            'options' => [
                'use_as_base_fieldset' => true,
            ],
        ]);
    }
}

编辑:添加调试数据和AddAction()

以上调试在下面函数的 persist() 行完成。

public function addAction($formName, array $formOptions = null, $route, array $routeParams = [])
{
    if (!$this->formElementManager instanceof FormElementManagerV2Polyfill) {

        throw new InvalidArgumentException('Dependency FormElementManagerV2Polyfill not set. Please check Factory for this function.');
    }

    if (!class_exists($formName)) {

        throw new ClassNotFoundException('Given class could not be found. Does it exist?');
    }

    /** @var AbstractForm $form */
    $form = $this->getFormElementManager()->get($formName, (is_null($formOptions) ? [] : $formOptions));

    /** @var Request $request */
    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());

        if ($form->isValid()) {
            $entity = $form->getObject();

            $this->getEntityService()->getEntityManager()->persist($entity);
            $this->getEntityService()->getEntityManager()->flush();

            $this->flashMessenger()->addSuccessMessage(
                _('Successfully created object.')
            );

            $this->redirectToRoute($route, $this->getRouteParams($entity, $routeParams));
        }
    }

    return [
        'form' => $form,
        'validationMessages' => $form->getMessages() ?: '',
    ];
}

您创建了一个名为 'environment_config' 的字段,但在 class Environment 中您调用了 protected $config;。两个名称必须相同。 'environment_script' 字段和 $script 属性存在同样的问题。

另一件事,您想动态创建一个 EnvironmentConfig,因此您必须在 $config 注释中添加一个 cascade 选项,以便能够从 Environment:

/**
 * @var EnvironmentConfig
 * @ORM\OneToOne(targetEntity="Environment\Entity\EnvironmentConfig", inversedBy="environment", cascade={"persist"})
 */
protected $config;