Intereting Posts
Ошибка печати изображения из общей папки laravel Проверить, заблокирован ли файл Заполнение html-форм данными mysql с использованием php, следующего вверху 404 Ошибка с Restler для всего URL-адреса jQuery анализирует / отображает данные json от php json_encode Быстрая реализация PHP-кода Aho-Corasick Javascript-функция post и вызов php-скрипта Удалить изображение с помощью php и сохранить прозрачный png Какая разница в ./ ../ / Если я храню объект в сеансе PHP, когда я получаю этот объект, является ли он полноценным объектом, на который я могу вызвать функции? preg_match, чтобы найти слово, которое заканчивается определенным символом Самый простой способ передачи данных между php и android API PayPal Rest: как отключить регистрацию / оплату с помощью диалогового окна кредитных карт при экспресс-выписке Создание образа без сохранения его как локального файла Неверный вывод при использовании индексирования массива в строке UTF-8

Правильный способ обновления двунаправленного отношения Many to Many symfony2-doctrine

Я провел некоторое исследование, и после прочтения этого и этого (и всех связанных вопросов) я все еще не могу понять, что является правильным способом обновить многие отношения во многих отношениях в Доктрине Symonfy 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; } - 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') ... ; } 

Ключ полагается здесь?

До сих пор (и чтобы вовремя не оставлять вопрос без ответа), похоже, что нет «простого способа, которого я еще не нашел» для этого. Это будет ответ на мой вопрос, согласно комментариям.

Но код может быть улучшен и сделать его более элегантным благодаря усовершенствованиям последнего комментария. Если на уровне сущности мы имеем это: 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, которое я ожидал, но пока что лучшее, что я могу сделать (возможно, группировать этот код внутри функции в репозитории, чтобы сделать его более элегантным).

Если у вас есть лучшие идеи, я все уши.

Я представляю свое решение всем, кто ищет решение.

Я использую 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 может найти данный объект (Post), добавив только идентификатор для маршрутизации:

/ Запись / редактирование / {ID}

Согласно документации доктрины, он будет проверять только принадлежность ассоциации к изменениям.

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

Нет необходимости изменять действия контроллера. Важно реализовать это на обратной стороне ассоциации!

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

При подаче формы обычно внутри действия

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

Обратите внимание: я не тестировал этот код. Я переписал этот код, чтобы он соответствовал сценарию, упомянутому в вопросе.