更新(从反面)条令2中的双向多对多关系



CustomerKeyword:是"关键字/客户"关系的反面

/**
* @ORMManyToMany(targetEntity="Keyword", mappedBy="customers",
*     cascade={"persist", "remove"}
* )
*/
protected $keywords;

创建新客户时,应选择一个或多个关键字。实体表单字段为:

$form->add($this->factory->createNamed('entity', 'keywords', null, array(
'class'    => 'AcmeHelloBundleEntityKeyword',
'property' => 'select_label',
'multiple' => true,
'expanded' => true,
)));

在我的控制器代码中,在绑定请求并检查表单是否有效后,我需要保持客户和所有客户/关键字的关联,即联接表。

然而,客户是反面,所以这不起作用:

if($request->isPost()) {
$form->bindRequest($request);
if(!$form->isValid()) {
return array('form' => $form->createView());
}
// Valid form here   
$em = $this->getEntityManager();
$em->persist($customer);    
$em->flush();
}

带有"级联"选项的事件,此代码失败。$customer->getKeywords()将返回DoctrineORMPersistentCollection,其中仅包含选定的关键字。

我应该手动检查删除/添加了哪个关键字,然后从拥有方进行更新吗?

好的,找到了方法,即使我还没有完全满意。关键是此示例表单集合字段类型。基本上,我以前的表单定义是:

$customer->getKeywords() = $postData; // $postData is somewhere in form framework

这只是将(选定关键字的)集合分配给客户关键字。未在Keyword实例(拥有方)上调用任何方法。关键选项是by_reference(对我来说,这只是一个坏名字,但无论如何…):

$form
->add($this->factory->createNamed('entity', 'keywords', null, array(
// ...
'by_reference' => false
))
);

通过这种方式,表单框架将调用setter,即$customer->setKeywords(Collection $keywords)。在这种方法中,你可以"告诉"拥有方存储你的关联:

public function setKeywords(Collection $keywords)
{
foreach($keywords as $keyword) {
$keyword->addCustomer($this); // Owning side call!
}
$this->keywords = $keywords;
return $this;
}

(始终使用contains方法在拥有方检查重复实例)。

此时,将只添加选中的关键字($keyword参数)。需要管理删除未检查的关键字(控制器端):

$originalKeywords = $customer->getKeywords()->toArray(); // When GET or POST
// When POST and form valid
$checkedKeywords = $customer->getKeywords()->toArray(); // Thanks to setKeywords
// Loop over all keywords
foreach($originalKeywords as $keyword) {
if(!in_array($keyword, $checkedKeywords)) { // Keyword has been unchecked
$keyword->removeCustomer($customer);
$manager->persist($keyword);
}
}

丑陋,但有效。我会将要删除的代码移到Customer类,但这根本不可能。如果你能找到更好的解决方案,请告诉我!

我使用的解决方案与@gredmo略有不同。根据条令文件:当你满足这个假设时,你可以使用孤儿移除:

当使用orphanRemoval=true选项时,条令假设这些实体是私有的,并且不会被其他实体重用。如果你忽略了这一假设,即使你将孤立实体分配给另一个实体,你的实体也会被条令删除。

我有这个实体类:

class Contract {
/**
* @ORMOneToMany(targetEntity="ContractParticipant", mappedBy="contract", cascade={"all"}, orphanRemoval=true)
**/
}
protected $participants;

表单处理(伪代码):

// $_POST carry the Contract entity values
$received = [];
foreach ($_POST['participants'] as $key => $participant) {
if ((!$relation = $collection->get($key))) {
// new entity
$collection[$key] = $relation = $relationMeta->newInstance();
} else {
// editing existing entity
}
$received[] = $key;
$this->mapper->save($relation, $participant);   // map POST data to entity
}
foreach ($collection as $key => $relation) {
if ($this->isAllowedRemove() && !in_array($key, $received)) {
// entity has been deleted
unset($collection[$key]);
}
}

不要忘记保持实体端部齐平。Flush还会删除删除的实体。

$this->em->persist($entity);
$this->em->flush();

相关内容

  • 没有找到相关文章