4

我做了一些研究,在阅读了这个这个(以及所有相关问题)之后,我仍然无法确定在 Symonfy 2 Doctrine 中更新多对多关系的正确方法。感觉应该有一个很简单的方法,我还没有找到。

我有这两个实体:

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 的默认设置,你必须使用ctrlkey 来选择多个。这是代码:

public function buildForm(FormBuilder $builder, array $options)
{
    $builder           
        ->add('student')
        ... 
        ;
}

关键靠这里?

4

4 回答 4

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 解决方案,但到目前为止我能做到的最好(也许将这段代码组合到存储库中的一个函数中,以使其更优雅)。

如果你有更好的想法,我会全力以赴。

于 2013-04-01T00:34:08.950 回答
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):

/post/edit/{id}

于 2014-11-24T19:57:31.043 回答
2

根据条令文档,它只会检查关联的拥有方是否有更改。

http://doctrine-orm.readthedocs.org/en/latest/reference/unitofwork-associations.html

因此,最好的方法是不断更新拥有方实体关联。

在这种情况下,您必须在添加和删除方法中删除和添加学生主目录中的 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);
}

这一切都不需要修改控制器动作。 重要的是在关联的反面实现这一点!

于 2015-03-20T21:04:59.317 回答
1

注释上带有 indexBy 属性的 ManyToMany Bidirectional 为我解决了这个问题

学生班级注释应该是

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 Form 应该是

    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)));
        }           

请注意:我没有测试过这段代码。我重写了这段代码以适应问题中提到的场景。

于 2016-02-16T23:28:07.123 回答