我做了一些研究,在阅读this和this(以及所有相关问题)之后,我仍然无法确定在Symonfy 2中更新多对多关系的正确方法教义。我觉得应该有一种非常简单的方法,我仍然没有找到它。
我有这2个实体:
class student_main
{
/**
* @ORM\ManyToMany(targetEntity="support_log", inversedBy="student_main")
* @ORM\JoinTable(name="support_log_student")
**/
private $support_log;
和
class support_log
{
/**
* @ORM\ManyToMany(targetEntity="student_main", mappedBy="support_log")
**/
private $student;
我想从support_log
开始。在控制器中,在更新操作中,我有类似的东西:
if ($editForm->isValid()) {
//add the relationship the user added
foreach($students as $student){
if(!$em->getRepository('mybundle:student_main')->hasSupportLog($entity,$student)){
$entity->addstudent_main($student);//*
}
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('support_log_edit', array('id' => $id)));
}
当然,正如教条文档所说,我相应地更改了该函数(addstudent_main):
public function addstudent_main(student_main $student)
{
$student->addsupport_log($this); // the important addition
$this->student[] = $student;
}
这很好用,我的问题更多是关于删除关系。在表单中有一个多选,用户可能会选择一些已经相关的学生和一些不相关的学生。感觉应该有一种自动的方法,但我必须做很多代码。
在控制器中,略高于我之前编写的代码,我把它放在:
//delete all old relationship
foreach($idsldstudents as $idst){ //I take Id's because the doctrine collection is updating always..
$stu=$em->getRepository('MyBundle:student_main')->find($idst);
$stu->deletesupport_log($entity);//I had to create that method (in the entity, I do "$this->support_log->removeElement($support_log)")
$em->persist($stu);
$em->flush();
}
我删除了有问题的实体的所有关系(当然,注意是双向关系,因此必须先在另一方删除),然后添加用户选择的关系
还有其他方法,但我没有找到任何简单的方法。在所有这些中我都有同样的问题:
有没有办法自动解决这两个问题呢? (我有一种强烈的感觉,一定有 - 也许有更好的关系宣言? - 这就是我要问的原因。)
提前致谢
编辑: 我的表单没有什么特别之处,我想我甚至没有触及生成的代码。它显示我想要的多选,默认的Symfony2,你必须使用 ctrl 键来选择多个。这是代码:
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('student')
...
;
}
钥匙在这里依赖?
答案 0 :(得分:2)
到目前为止,(并且为了避免永远无法回答问题),看起来没有“我仍然没有找到的简单方法”来做到这一点。根据评论,这将是我的问题的答案。
但是由于最后评论的改进,代码可以得到改进并使其更加优雅。如果在实体层面我们有:gist.github.com/3121916(来自评论)
然后,控制器中的代码可以减少一点:
$editForm->bindRequest($request);
if ($editForm->isValid()) {
//delete all old relationships, we can go from student:
foreach($em->getRepository('mybundle:student_main')->findAll() as $oldstudent)
{
$oldstudent->removeSupportLog($entity);
//if they are related, the relationship will be deleted.
//(check the code from the url)
}
//add the relationship the user added in the widget
$students=$entity->getStudent();
foreach($students as $student)
{
$entity->addstudent_main($student);
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('support_log_edit', array('id' => $id)));
}
它仍然不是我期望的“神奇”symfony解决方案,但到目前为止我能做的最好(可能将此代码组合在存储库中的函数中,以使其更优雅)。
如果你有更好的想法,我会全力以赴。
答案 1 :(得分:2)
我向所有正在寻找解决方案的人提出我的解决方案。
我使用Symfony 2.5。
我的帖子'实体具有多对多的双向性。
控制器:
public function editPostAction(Post $post, Request $request)
{
$form = $this->createForm(new NewPost(), $post, [
'action' => $this->generateUrl('admin_edit_post', ['id' => $post->getId()])
]);
$form->handleRequest($request);
if( $form->isSubmitted() )
{
$this->get('post.repository')->update();
}
return $this->render('BlogJakonAdminPanelBundle:Post:post-edit.html.twig', array(
'form' => $form->createView(),
'errors' => $form->getErrors(true)
));
}
我通过以下路由绑定我的实体:
admin_edit_post:
path: /post/edit/{id}
defaults: { _controller: BlogJakonAdminPanelBundle:Post:editPost }
我的存储库:
public function update()
{
try {
$this->getEntityManager()->flush();
} catch (\Exception $e) {
$this->getEntityManager()->getConnection()->rollback();
return false;
}
return true;
}
表格类:
class NewPost extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
[...]
->add('categories', 'entity', array(
'class' => 'BlogJakon\PostBundle\Entity\Category',
'property' => 'name',
'multiple' => true,
'expanded' => true)
)
->add(
'save',
'submit',
[
'label' => 'Add post'
]
)
->getForm();
}
public function setDefaultOption(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
[
'data_class' => 'BlogJakon\PostBundle\Entity\Post',
'csrf_protection' => true
]
);
}
public function getName()
{
return 'newPost';
}
}
值得一提的是,Symfony可以通过仅向路由添加id来找到给定的实体(Post):
/后/编辑/ {ID}
答案 2 :(得分:2)
根据学说文档,它只会检查关联的拥有方是否有变化。
http://doctrine-orm.readthedocs.org/en/latest/reference/unitofwork-associations.html
因此,最好的方法是不断更新边实体协议。
在这种情况下,您必须删除并在student main中添加support_log以添加和删除方法
class support_log
{
/**
* @ORM\ManyToMany(targetEntity="student_main", mappedBy="support_log")
**/
private $student;
public function addStudent($student) {
$this->student[] = $student;
$student->addSupportLog($this);
}
public function removeStudent($student) {
$student->removeSupportLog($this);
$this->student->removeElement($student);
}
这一切都不需要修改控制器动作。 重要的是在关联的反面执行此操作!
答案 3 :(得分:1)
ManyToMany双向,注释上带有indexBy属性,为我修复了这个
学生班级注释应该是
class student_main
{
/**
* @ORM\ManyToMany(targetEntity="support_log", mappedBy="student_main")
**/
private $support_log;
支持类注释应该是
class support_log
{
/**
* @ORM\ManyToMany(targetEntity="student_main", inversedBy="support_log", indexBy="id")
* @ORM\JoinTable(name="support_log_student",
* joinColumns={@ORM\JoinColumn(name="support_log_id",referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="student_id", referecedColumnName="id")}
* )
**/
private $student;
现在symfony 2表格应该是
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('student', 'entity', array(
'class' => '<<ENTER YOUR NAMESPACE PATH TO ENTITY>>\Entity\Student',
'property' => 'Name', //property you want to display on the select box
'label' => 'Belongs to Students',
'multiple' => true,
'constraints' => array(
new NotBlank(array('message' => 'Please choose atleast one student'))
)
))
....
;
}
通常在Action
中提交表单 if ($editForm->isValid()) {
$entity = $editForm->getData();
$em->persist($entity); //this should take care of everything saving the manyToMany records
$em->flush();
return $this->redirect($this->generateUrl('support_log_edit', array('id' => $id)));
}
请注意:我尚未测试此代码。我重写了这段代码以适应问题中提到的场景。