Symfony2: Как изменить сущность текущего пользователя с помощью формы?
Я пытаюсь добавить простую форму, чтобы мои пользователи могли редактировать свой профиль. Моя проблема:
Поскольку сущность, "связанная" с формой, совпадает с текущим объектом пользователя ($user === $entity
см. ниже), если проверка формы не удалась, то представление отображается с измененным пользовательским объектом (т. е. со значениями недействительной формы).
Вот мой (классический) контроллер:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
// $user === $entity => true
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Вот я и подумал, как иметь два разных объекта $user
а также $entity
, я использовал clone()
и это работало хорошо для части рендеринга вида ($user
объект не был изменен), но он создал новую запись в базе данных вместо обновления старой.
PS: я знаю, что я должен использовать FOSUserBundle
, Но мне бы очень хотелось понять мою ошибку здесь:)
2 ответа
Я использовал то же решение, что и FOSUserBundle, который вызывает $em->refresh()
на моей сущности, когда проверка формы не удалась:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
$em->refresh($user); // Add this line
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Обратите внимание, что если вы используете то, что называется "виртуальным" полем в " Как обрабатывать загрузку файлов с помощью Doctrine" (в моем случае "picture_file", вам необходимо очистить его вручную:
$em->refresh($user);
$user->picture_file = null; // here
Один из подходов состоит в том, чтобы всегда перенаправлять:
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('profile'));
Конечно, вы теряете сообщения об ошибках и изменения.
Другой подход заключается в определении менеджера сущностей только для вашего UserProvider. $user
больше не будет таким же, как $entity
, Немного лишних накладных расходов, но это, безусловно, решает проблему и предотвращает аналогичные взаимодействия с другими формами, которые могут изменить весь или часть пользовательского объекта.
Аналогичным образом вы сокращаете накладные расходы, создавая менеджер сущностей только для формы своего профиля. При использовании этого метода накладные расходы будут возникать только при редактировании профиля.
Наконец, вы можете спросить себя, действительно ли имело значение, что отображаемые данные были не совсем правильными в данном конкретном случае. Это действительно что-то беспокоит? Кто-нибудь заметит, кроме вас?
См. Как работать с несколькими менеджерами сущностей в Symfony Cookbook
Другая идея заключается в клонировании вашей пользовательской сущности в вашем провайдере. Это разлучит его с менеджером объекта.
Вы также можете использовать $entityManager->detach($user);
удалить пользователя из менеджера сущностей.
И почему пользователь токена в любом случае является сущностью? Рассмотрите возможность создания полностью независимого класса User с минимальным объемом информации, извлекаемой из базы данных вашим поставщиком Это то, что я делаю.