ArrayCollection (Коллекция форм) столкновение индекса в Symfony 2
Я использую Symfony2 для создания моей страницы.
Когда я пытаюсь обновить коллекцию форм (как описано в записи поваренной книги "" Как встроить коллекцию форм"), я получаю столкновение индексов интерфейса и индексов ArrayCollection в бэкэнде.
У меня есть отношение User ↔ Address (OneToMany). Пользователь хочет создать/обновить/удалить свои адреса, поэтому он может добавлять/удалять в интерфейсе с помощью элементов нового адреса javascript. Он делает следующее:
(1) Добавляет новый адрес (имеет индекс: 0)
(2) Добавляет новый адрес (имеет индекс: 1) и мгновенно удаляет этот адрес снова
(3) Добавляет новый адрес (имеет индекс: 2).
Когда он нажимает кнопку сохранения, следующий код сохраняет/обновляет пользователя (и его адреса):
$this->em->persist($user);
$this->em->flush();
Новые адреса, например, затем корректно сохраняются в базе данных.
Теперь пользователь хочет обновить адрес, например. с индексом 0.
Когда он нажимает на кнопку сохранения, он обновляет адрес с помощью "index 0", но в то же время он снова добавляет адрес с "индексом 2" в базу данных (объект).
Чтобы лучше понять проблему, я нарисовал небольшую иллюстрацию (ручная работа, извините за мои плохие навыки искусства):
Теперь у меня есть два раза адрес с "индексом 1" в моем объекте/базе данных.
Я знаю, почему это происходит, потому что первый адрес "index 1" сопоставляется с элементом ArrayCollection "номер 1", а второй отображается на "номер 2" (из-за имени интерфейса "index 2" ).
Вы можете сказать: "он просто заполняет адреса, пока не достигнет индекса интерфейса в бэкэнд".
Но как я могу исправить это поведение?
Примечание по сайту:
Такое поведение возникает с использованием аякс-запросов, потому что если вы перезагрузите страницу после нажатия кнопки "Сохранить", она правильно вернет адреса в интерфейсе с индексами в бэкэнд.
Мое предложение справиться с этой ситуацией:
Повторное индексирование индексов интерфейса после нажатия кнопки "Сохранить" на стороне сервера
индексов. Является ли это ясным/единственным решением для моей проблемы?
Ответы
Ответ 1
Да, это проблема коллекции форм Symfony, и у нее нет простого решения imho. Но я должен спросить, почему бы вам не сделать то же самое, что делает обновление страницы? Вы можете обновить только фрагмент html с помощью коллекции. HTML-код для фрагмента может быть получен с сервера. Вернемся к вашему вопросу - да, переиндексирование - хорошее решение, пока вы не захотите самостоятельно написать собственный тип коллекций.
symfony/symfony/issues/7828
Аналогичная проблема с проверкой в коллекции - symfony/symfony/issues/7468.
Ну, я думаю, что тип коллекции по умолчанию и учебник в документах Symfony имеют некоторые недостатки. Надеюсь, что поможет.
Ответ 2
Я столкнулся с этой проблемой пару раз за последние два года. Обычно, следуя учебнику Symfony "Как встроить коллекцию форм" , эта работа отлично справляется. Вам нужно немного кодировать javascript, чтобы добавить функциональность "редактировать/обновлять" , но кроме этого - вы должны быть в порядке, используя этот подход.
Если, с другой стороны, у вас действительно сложная форма, которая использует AJAX
для проверки/сохранения/вычисления/бизнес-логики/и т.д., я обнаружил, что обычно лучше хранить конечные данные в массиве в сессия. После отправки формы внутри блока if($form->isValid()){...}
у вас будет
$collection = new ArrayCollection($mySessionPlainArray);
$user->setAddress($collection);
Я хотел бы предупредить о том, что вы будете осторожны с сериализацией своих данных. Вы можете получить некоторые неудобные исключения или неправильное поведение, если вы используете сущности (см. мой question).
Извините, я не могу предоставить больше кода, но решение этой проблемы иногда довольно сложно.
Ответ 3
Я столкнулся с этой проблемой на стороне клиента, изменив код Javascript/JQuery, указанный в документации Symfony.
Вместо нумерации новых элементов путем подсчета подэлементов я просматриваю последний идентификатор элемента и извлекаю его индекс с регулярным выражением.
При добавлении элемента я увеличиваю последний индекс на 1. Таким образом, я никогда не использую тот же индекс.
Вот мой код:
// Initializing default index at 0
var index = 0;
// Looking for collection fields in the form
var $findinput = $container.find(':input');
// If fields found then looking for last existing index
if ( $findinput.length > 0 ) {
// Reading id of last field
var myString = $findinput.last().attr('id')
// Setting regular expression to extract number from id containing letters, hyphens and underscores
var myRegex = /^[-_A-Za-z]+([0-9]+)[-_A-Za-z]*$/
// Executing regular expression on last collection field id
var test = myRegex.exec(myString);
// Extracting last index and incrementing by 1
if (test.length > 0) index = parseInt(test[1]) + 1;
}