Doctrine не обновляет поле типа простого массива

Краткая история (Изменить)

Можно хранить массив вместо сопоставленной сопоставления. В Symfony2 это довольно просто, используя тип поля коллекции . Например, используя этот метод, вы можете сохранить массив текстовых полей, которые заполняют поле событий массива. Однако, чтобы обновить массив, есть трюк, и этот трюк красиво объясняется @Vadim Ashikhman в принятом ответе.

Длинная история

Иногда полезно и более эффективно хранить массив вместо сопоставленной сопоставления. Однако, после создания, остается сложно обновить этот массив, если размер этого массива не изменится?

У многих людей есть аналогичная проблема, но никто не нашел правильного решения этой проблемы.

Хранить массив

Команда может организовать множество мероприятий. Эти события просто хранятся в массиве с использованием Doctrine вместо использования ассоциации OneToMany. Следовательно, сущность Event не сопоставляется с Doctrine.

Событие Entity (не привязано к Doctrine)

<?php namespace Acme\TestBundle\Entity; ... class Event { /** * @Assert\NotBlank */ private $name; public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } } 

Группа компаний

 <?php namespace Acme\TestBundle\Entity; ... /** * @ORM\Entity() * @ORM\HasLifecycleCallbacks * @ORM\Table(name="teams") */ class Team { /** * @ORM\Column(type="array") * @var array */ protected $events; public function addEvent($event) { if (!in_array($event, $this->events, true)) { $this->events[] = $event; } return $this; } public function removeEvent($event) { if (false !== $key = array_search($event, $this->events, true)) { unset($this->events[$key]); $this->events = array_values($this->events); } return $this; } public function getEvents() { return $this->events; } public function setEvents(array $events) { $this->events = array(); foreach ($events as $event) { $this->addEvent($event); } return $this; } не <?php namespace Acme\TestBundle\Entity; ... /** * @ORM\Entity() * @ORM\HasLifecycleCallbacks * @ORM\Table(name="teams") */ class Team { /** * @ORM\Column(type="array") * @var array */ protected $events; public function addEvent($event) { if (!in_array($event, $this->events, true)) { $this->events[] = $event; } return $this; } public function removeEvent($event) { if (false !== $key = array_search($event, $this->events, true)) { unset($this->events[$key]); $this->events = array_values($this->events); } return $this; } public function getEvents() { return $this->events; } public function setEvents(array $events) { $this->events = array(); foreach ($events as $event) { $this->addEvent($event); } return $this; } 

Форма события

 <?php namespace Acme\TestBundle\Form\Type; ... class EventType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { parent::buildForm($builder, $options); $builder->add('name', 'text'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'Acme\TestBundle\Entity\Event', 'cascade_validation' => true, )); } ... } 

Форма команды

 <?php namespace Acme\TestBundle\Form\Type; ... class TeamType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { parent::buildForm($builder, $options); $builder->add('events','collection', array( 'type' => new EventType(), 'allow_add' => true, 'allow_delete' => true, 'prototype' => true, 'by_reference' => false, 'options' => array('data_class' => 'Acme\TestBundle\Entity\Event'), ) ); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'Acme\TestBundle\Entity\Team', )); } ... } 

Контроллер

 /** * Update a team * * @Route("update/{team_id}", name="updateTeamFromId") * @Template("AcmeTestBundle:Team:teamUpdate.html.twig") */ public function updateTeamAction($team_id, Request $request) { $em = $this->getDoctrine()->getManager(); $repository= $em->getRepository('AcmeTestBundle:Team'); $team_to_update = $repository->find($team_id); $form = $this->createForm(new teamType(), $team_to_update); if ($request->getMethod() == 'POST') { $form->bind($request); if ($form->isValid()){ $em->persist($team_to_update); $em->flush(); return $this->redirect($this->generateUrl('homepage')) ; } } return array( 'form' => $form->createView(), 'team_id' => $team_id, ); } 

Вы можете найти ответ здесь:

Как заставить Doctrine обновить поля типа массива?

Refs:

Doctrine использует идентичный оператор === для сравнения изменений между старыми и новыми значениями. Оператор, используемый на одном и том же объекте с разными данными, всегда возвращает true. Есть другой способ решить эту проблему, вы можете клонировать объект, который необходимо изменить.

 $items = $myEntityObject->getItems(); $items[0] = clone $items[0]; $items[0]->setSomething(123); $myEntityObject->setItems($items); 

Или измените метод setItems ()

 public function setItems($items) { if (!empty($items) && $items === $this->items) { reset($items); $key = key($items); $items[$key] = clone $items[$key]; } $this->items = $items; } 

Вы можете попробовать это внутри контроллера:

 public function updateTeamAction($team_id, Request $request) { $em = $this->getDoctrine()->getManager(); $repository= $em->getRepository('AcmeTestBundle:Team'); $team_to_update = $repository->find($team_id); $form = $this->createForm(new teamType(), $team_to_update); if ($request->getMethod() == 'POST') { $form->bind($request); if ($form->isValid()){ $events = $team_to_update->getEvents(); foreach($events as $key => $value){ $team_to_update->removeEvent($key); } $em->flush(); $team_to_update->setEvents($events); $em->persist($team_to_update); $em->flush(); return $this->redirect($this->generateUrl('homepage')) ; } } return array( 'form' => $form->createView(), 'team_id' => $team_id, ); } 

Вероятно, есть более быстрый способ сделать это, и я знаю, что это не лучший способ сделать это, но пока вы (или кто-то еще) не найдет это решение, вы можете использовать его как временное исправление.