doctrine2 – Symfony2表单:如何持久化具有可空关联的实体?

保存表单提交数据时,我无法持久保存新实体实例,其中实体与另一个实体具有可空的关联,并且我尝试将其设置为null.在为表单创建新的实体实例后,将提交的请求绑定到表单并持久化并刷新实体实例,具体取决于我如何填充关联实体的属性,我得到

> UnexpectedTypeException:类型为“object or array”的预期参数,给定“NULL”(如果设置为null),或者
> InvalidArgumentException:通过“AccessLog#document”关系找到一个新实体,该关系未配置为级联实体的持久化操作(如果设置为相关实体的新的空实例,我不想保留).

如果我设置了cascade persist,它会尝试在相关表中创建一条记录(db中的数据模型不允许),即使没有数据可以保留.如果设置级联持续是要走的路,我该如何阻止它尝试创建新记录?处理这个问题的最佳方法是什么?

注意,无论关联是设置为单向还是双向,行为都是相同的.

细节:

我有一个与另一个实体(缩写)有多对一关联的实体:

/** @Entity */
class AccessLog
{
    /** @Id @Column(type="integer") */
    private $access_log_id;

    /** @Column(type="integer", nullable=true) */
    private $document_id;

    /**
     * @ManyToOne(targetEntity="Document", inversedBy="access_logs", cascade={"persist"})
     * @JoinColumn(name="document_id", referencedColumnName="document_id")
     */
    private $document;

    // plus other fields
    // plus getters and setters for all of the above...
}

相关实体没什么特别的:

/** @Entity */
class Document
{
    /** @Id @Column(type="integer") */
    private $document_id;

    /** @Column(length=255) */
    private $name;

    /** @OneToMany(targetEntity="AccessLog", mappedBy="document") */
    private $access_logs;

    // plus other fields
    // plus getters and setters for all of the above...
}

我有一个Symfony表单,用于输入新的AccessLog记录的数据:

class AccessLogFormType extends AbstractType
{
    public function getName()
    {
        return 'access_log_form';
    }

    public function getDefaultOptions(array $options)
    {
        return array('data_class' => 'AccessLog');
    }

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('access_log_id', 'hidden');
        $builder->add('document_id', 'hidden', array(
            'required' => false
        ));
        $builder->add('document', new DocumentType(), array(
            'label' => 'Document',
            'required' => false
        ));
        //...
    }
}

使用以下支持类型定义:

class DocumentType extends AbstractType
{
    public function getName()
    {
        return 'document';
    }

    public function getDefaultOptions(array $options)
    {
        return array('data_class' => 'Document');
    }

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('name', 'text', array(
            'required' => false
        ));
    }
}

我的控制器包括以下内容:

public function save_access_log_action()
{
    $request = $this->get('request');
    $em = $this->get('doctrine.orm')->getEntityManager();

    $access_log = null;

    if ($request->getMethod() === 'POST') {
        $data = $request->request->get('access_log_form');

        if (is_numeric($data['access_log_id'])) {
            $access_log = $em->find('AccessLog', $data['access_log_id']);
        } else {
            $access_log = new AccessLog();
        }

        if (is_numeric($data['document_id'])) {
            $document = $em->find('Document', $data['document_id']);
            $access_log->set_document($document);

        } else {
            // Not calling set_document() since there shouldn't be a
            // related Document entity.
        }

        $form = $this->get('form.factory')
            ->createBuilder(new AccessLogFormType(), $access_log)
            ->getForm();

        $form->bindRequest($request);

        if ($form->isValid()) {
            $em->persist($access_log);
            $em->flush();
        }

    } else {
        // ... (handle get request)
    }

    return $this->render('access_log_form.tpl', array(
        'form' => $form->createView()
    ));
}

上述代码在更新现有访问日志条目或创建新条目并在表单中选择文档时工作正常,但如果没有选择文档则不行.

假设无法更改数据模型,如何在不持久保存新Document实体的情况下持久保存新的AccessLog实体?

似乎解决方案是在persist操作之前将set_document(null)调用移到右边,因为将请求绑定到表单会导致将空Document对象附加到AccessLog对象的$document属性.

删除级联持久性并使关联单向后,此解决方案也可以工作.

感谢@ greg0ire的帮助.

[更新]有必要将@ChangeTrackingPolicy(“DEFERRED_EXPLICIT”)添加到Document实体定义,因为它继续尝试更新与AccessLog记录关联的Document记录,例如在删除现有关联时(导致$name)要在Document上设置为null的属性,然后在db中设置为null.当删除关联时,默认行为是删除/修改数据,这是非常令人沮丧的,即使未指定级联持久性也是如此.

相关文章
相关标签/搜索