Изменения в элементах ArrayCollection выполняются, но не сохраняются в базе данных.

вступление

Итак, у меня есть объект Presence, за которым следит Doctrine.
У меня также есть другой объект Decision, за которым Doctrine не следит.

Объект Presence имеет атрибут $decisions, тип ArrayCollection и содержит от 0 до n объектов Decision.

Поскольку я не храню каждый объект Decision, содержащийся в ArrayCollection, я ввел атрибут $decision как массив Doctrine, и пока все работает.

Я могу получить весь объект Presence из базы данных, и он содержит все объекты Decision, которые я добавил к нему.

Проблема

Когда я редактирую один объект Decision, который присутствует в коллекции ArrayCollection моего объекта Presence, например:

    foreach($Presence->getDecisions() as $decision) {
        $decision->setMorning($value);
    }

Модификации применяются локально, и я могу видеть их, когда делаю dump($Presence);

Presence {#2354 ▼
  #id: 8
  #decisions: ArrayCollection {#1409 ▼
    -elements: array:6 [▼
      0 => Decision {#1408 ▼
        #token_id: "005867b2915438c03383bb831d87c26346c586d6ecd46b735c7a23b4fabb682a8ac34a90ea3d53cc55c2209deaa83337abeb2b98ded43967fcfb0f4d04d5ada6"
        #date: DateTime @1531692000 {#1407 ▶}
        #morning: -1
        #afternoon: 0
      }
      1 => Decision {#1406 ▶}
      2 => Decision {#1404 ▶}
      3 => Decision {#1402 ▶}
      4 => Decision {#1400 ▶}
      5 => Decision {#1398 ▶}
    ]
  }
  #last_edit_date: DateTime @1532109183 {#2454 ▼
    date: 2018-07-20 19:53:03.062940 Europe/Berlin (+02:00)
  }
  #user: User {#1393 ▶}
}

На этом dump() видно, что атрибут #morning: -1 является результатом вызова сеттера.

Но когда я пытаюсь сбросить изменения с помощью $this->entityManager->flush();, коллекция ArrayCollection не была изменена.

Сначала я подумал, что это проблема обнаружения изменений, поэтому я добавил поле #last_edit_date DateTime в свой объект Presence.
Самое интересное, что этот атрибут прекрасно обновляется при очистке.

Кажется, что ArrayCollection по какой-то причине не будет обновляться.

ОБНОВИТЬ

Чтобы завершить мой пост, вот дополнительная информация:

Полный метод в контроллере

/**
* @Route(name="edit_decision", path="/edit-decision/{tokenDate}/{dayPart}/{newValue}")
*/
public function EditDecisionAction(Request $request)
{
    $token = $request->get('tokenDate');
    $dayPart = $request->get('dayPart');
    $newValue = intval($request->get('newValue'));

    $myPresence = $this->em->getRepository('AppBundle:Presence')->findOneBy([
        'user' => $this->connectedUser
    ]);

    /** @var ArrayCollection $decisionCollection */
    $decisionCollection = $myPresence->getDecisions();

    $found = FALSE;
    
    /** @var Decision $decision */
    foreach ($decisionCollection as $decision) {
        if ($decision->getTokenId() == $token) {
            
            $found = TRUE;

            if ($dayPart === 'morning') {
                $decision->setMorning($newValue);
            }elseif ($dayPart === 'afternoon') {
                $decision->setAfternoon($newValue);
            }
            break;
        }
    }
    
    if (!$found) {
        throw $this->createNotFoundException();
    }
    
    // I set a new Date to trigger a modification on the Presence Entity : It works
    $myPresence->setLastEditDate(new \DateTime());
    
    // Here is the dump that you can see above
    dump($myPresence);
    
    $this->em->flush();
    
    try{
        $this->em->flush();
        return $this->json([
            'status' => TRUE
        ]);
    }catch (\Exception $exception) {
        return $this->json([
            'status' => FALSE
        ]);
    }
}

Атрибут, содержащий ArrayCollection :

/**
 * Doctrine Type Array
 *
 * @ORM\Column(type="array", nullable=true)
 */
 protected $decisions;

Любая помощь или даже подсказки приветствуются! Спасибо :)

ОБНОВЛЕНИЕ 2

Возможно, я нашел решение благодаря этому сообщению:
Как заставить Doctrine обновлять поля типа массива?

Проблема возникает из-за типа массива доктрины, под которым я храню свои объекты:

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

Обновление 3

Nope по-прежнему не сохраняет изменения, внесенные в элементы ArrayCollection в базе данных. Собираюсь попробовать Клонирование: Добавление: Удаление старого, и мы увидим, что произойдет.


person Matt    schedule 20.07.2018    source источник
comment
Вместо дампа лучше помогла бы полная функция в контроллере. Можете ли вы завершить свой ответ, пожалуйста? Также добавьте часть вашего представления ветки, где вы отображаете форму, и FormType, который вы используете в своем контроллере.   -  person Preciel    schedule 20.07.2018
comment
Возможно, вам потребуется использовать on-flush: $unitOfWork-›recomputeSingleEntityChangeSet($classMetadata, $entity) Или вам нужно использовать предварительное обновление: doctrine-project.org/projects/doctrine-orm/en/latest/reference/   -  person nicandr    schedule 21.07.2018
comment
Вот еще одна ссылка stackoverflow.com/questions/18762695/   -  person nicandr    schedule 21.07.2018
comment
Добавьте свой контроллер и ветку   -  person Shaithya Kannan    schedule 23.07.2018


Ответы (1)


ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ

Итак, я, наконец, заставил его работать, используя трюк.

Основная проблема заключалась в поведении обнаружения изменений Doctrine в отношении типа Array.

Как указано выше, доктрина делает: Массив === Массив для проверки наличия изменений. Довольно странно, потому что, когда вы ДОБАВЛЯЕТЕ или УДАЛАЕТЕ элемент из своей коллекции ArrayCollection, для Doctrine коллекция по-прежнему эквивалентна предшествующему состоянию. Должен быть способ проверить эквивалентность между двумя объектами, например, в Java .equals, чтобы устранить эти неоднозначности.

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

$decisionCollection = $myPresence->getDecisions();

Я размещаю свои сеттеры и соответствующим образом изменяю свою коллекцию:

foreach ($myPresence->getDecisions() as $key => $decision) {
    if ($decision->getTokenId() == $token) {
                
        if ($dayPart === 'morning') {
            $decision->setMorning($newValue);
        } elseif ($dayPart === 'afternoon') {
            $decision->setAfternoon($newValue);
        }
                
        break;
    }
}

А потом Трюк:

$myPresence->unsetDecisions();
$this->em->flush();

Я полностью удаляю свою коллекцию ($this-›collection = NULL) и сбрасываю ее в первый раз.
Это вызывает обновление с момента массива !== NULL
И затем я устанавливаю свою коллекцию с обновленной, которая содержит установленный объекты :

$myPresence->setDecisions($decisionCollection);
$myPresence->setLastEditDate(new \DateTime());

И сбросьте в последний раз, чтобы сохранить новые изменения.

Спасибо всем за ваше время и ваши комментарии!

person Matt    schedule 23.07.2018