Doctrine orm Symfony2表单:如何持久化具有可空关联的实体?
在保存表单提交数据时,我在持久化一个新实体实例时遇到了问题,其中该实体与另一个实体具有可为null的关联,并且我尝试将其设置为null。在为表单创建新实体实例、将提交的请求绑定到表单并持久化和刷新实体实例之后,根据我为关联实体填充属性的方式,我可以Doctrine orm Symfony2表单:如何持久化具有可空关联的实体?,doctrine-orm,symfony,symfony-forms,Doctrine Orm,Symfony,Symfony Forms,在保存表单提交数据时,我在持久化一个新实体实例时遇到了问题,其中该实体与另一个实体具有可为null的关联,并且我尝试将其设置为null。在为表单创建新实体实例、将提交的请求绑定到表单并持久化和刷新实体实例之后,根据我为关联实体填充属性的方式,我可以 UnexpectedTypeException:类型为“object或array”的预期参数,给定为NULL(如果设置为NULL),或 InvalidArgumentException:通过关系“AccessLog#document”找到了一个新实体
UnexpectedTypeException:类型为“object或array”的预期参数,给定为NULL(如果设置为NULL),或
InvalidArgumentException:通过关系“AccessLog#document”找到了一个新实体,该关系未配置为级联实体的持久化操作
(如果设置为相关实体的新的空实例,我不想持久化)/** @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()
));
}
当更新现有的访问日志条目或创建新条目并且在表单中选择了文档时,上述代码可以正常工作,但如果未选择任何文档,则不能正常工作
假设数据模型无法更改,如何在不保留新文档实体的情况下保留新的AccessLog实体?您是否阅读了的页面?我认为,如果文档存在,您首先需要
删除它,如果它不存在,那么您就不应该调用setDocument()
。为了避免第二个错误,§解释了如何配置级联(请参阅最后一个代码段)。解决方案似乎是在持久化操作之前将set_document(null)调用移到右侧,因为将请求绑定到表单导致将空文档对象附加到AccessLog对象的$Document属性
此解决方案在移除级联并使关联单向后也可以工作
感谢@greg0ire的帮助
[更新]还需要在文档实体定义中添加@ChangeTrackingPolicy(“DEFERRED\u EXPLICIT”)
,因为它会继续尝试更新与AccessLog记录关联的文档记录,例如在删除现有关联时(这导致文档上的$name属性设置为null,然后在db中设置为null)。当删除关联时,默认行为是删除/修改数据,即使没有指定cascade persist。我认为您的问题可能是在AccessLog
中定义文档设置器的方式
如果您这样做:
setDocument(Document $document)
您将永远无法设置文档(null)
,因为null不是文档的实例。为此
setDocument($document) or setDocument(Document $docuement = null)
在AccessLogFormType中,您应该更改
$builder->add('document', new DocumentType(), array(
'label' => 'Document',
'required' => false
));
致:
这允许您在不设置文档的情况下保存实体。这在Symfony2 master中已修复。空表单现在不再生成空对象,而是返回null。为什么需要执行设置文档(null)
?为什么不直接调用setDocument
?我已经读过了,但我不想使用双向关联。我只是尝试了一下,但还是出现了第二个错误,没有调用set\u document(),在文档实体上设置了cascade persist。这令人困惑,因为错误使它听起来好像没有看到我刚刚设置的cascade persist:为了清楚起见,我添加了一个$access\u logs
属性(和getter/setter)到带有注释@OneToMany(targetEntity=“AccessLog”,mappedBy=“Document”,cascade)的文档中={“persist”}
,我在AccessLog中$document属性的manytone注释中添加了inversedBy=“access\u logs”
。我认为cascade={“persist”}
应该在AccessLogOk中的另一个关系上,该关系清除了错误,但现在它正在尝试对document表进行插入(这是我的数据模型在数据库级别不允许的)即使没有新文档。我如何防止它尝试创建空记录?似乎假设即使联接列可为空,也必须存在相关实体。请使用新代码更新您的问题,这将使我更容易回答。
$builder->add('document', new DocumentType(), array(
'label' => 'Document',
'required' => false
));
$builder->add('document', new DocumentType(), array(
'label' => 'Document',
'required' => false,
'empty_value' => '',
'empty_data' => null
));