Форма Symfony2 обновляет отношение один к одному

Мне нужна помощь с обновлением формы Symfony2 One to One. У меня есть объекты: User и UserInfo

Объект пользователя:

class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\Group")
     * @ORM\JoinTable(name="fos_user_user_group",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     */
    protected $groups;

    /**
     * @ORM\OneToOne(targetEntity="UserInfo", mappedBy="user", cascade={"all"})
     */
    private $info;

    /**
     * @ORM\OneToMany(targetEntity="Log", mappedBy="user")
     */
    private $logs;

    public function __construct()
    {
        parent::__construct();
        $this->logs = new ArrayCollection();
    }

    /**
     * Set info
     *
     * @param \AppBundle\Entity\UserInfo $info
     * @return User
     */
    public function setInfo(\AppBundle\Entity\UserInfo $info = null)
    {
        $this->info = $info;
        $info->setUser($this);

        return $this;
    }

    /**
     * Get info
     *
     * @return \AppBundle\Entity\UserInfo 
     */
    public function getInfo()
    {
        return $this->info;
    }

    /**
     * Add logs
     *
     * @param \AppBundle\Entity\Log $logs
     * @return User
     */
    public function addLog(\AppBundle\Entity\Log $logs)
    {
        $this->logs[] = $logs;

        return $this;
    }

    /**
     * Remove logs
     *
     * @param \AppBundle\Entity\Log $logs
     */
    public function removeLog(\AppBundle\Entity\Log $logs)
    {
        $this->logs->removeElement($logs);
    }

    /**
     * Get logs
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getLogs()
    {
        return $this->logs;
    }
}

Сущность UserInfo:

class UserInfo
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="position", type="string", length=255)
     */
    private $position;

    /**
     * @var string
     *
     * @ORM\Column(name="info", type="text")
     */
    private $info;

    /**
     * @var string
     *
     * @ORM\Column(name="fullname", type="string", length=255)
     */
    private $fullname;

    /**
     * @ORM\OneToOne(targetEntity="User", inversedBy="info")
     */
    private $user;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set position
     *
     * @param string $position
     * @return UserInfo
     */
    public function setPosition($position)
    {
        $this->position = $position;

        return $this;
    }

    /**
     * Get position
     *
     * @return string 
     */
    public function getPosition()
    {
        return $this->position;
    }

    /**
     * Set info
     *
     * @param string $info
     * @return UserInfo
     */
    public function setInfo($info)
    {
        $this->info = $info;

        return $this;
    }

    /**
     * Get info
     *
     * @return string 
     */
    public function getInfo()
    {
        return $this->info;
    }

    /**
     * Set fullname
     *
     * @param string $fullname
     * @return UserInfo
     */
    public function setFullname($fullname)
    {
        $this->fullname = $fullname;

        return $this;
    }

    /**
     * Get fullname
     *
     * @return string 
     */
    public function getFullname()
    {
        return $this->fullname;
    }

    /**
     * Set user
     *
     * @param \AppBundle\Entity\User $user
     * @return UserInfo
     */
    public function setUser(\AppBundle\Entity\User $user = null)
    {
        $this->user = $user;

        return $this;
    }

    /**
     * Get user
     *
     * @return \AppBundle\Entity\User 
     */
    public function getUser()
    {
        return $this->user;
    }
}

Это их виды:

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('username')
            ->add('email')
            ->add('password')
            ->add('info', new UserInfoType(), [
                'by_reference' => false,
            ])
            ->add('Submit', 'submit')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\User',
        ]);
    }

    public function getName()
    {
        return 'user';
    }
}

а также

class UserInfoType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('position')
            ->add('info')
            ->add('fullname')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\UserInfo',
            'allow_delete' => true,
        ]);
    }

    public function getName()
    {
        return 'userinfo';
    }
}

Новая запись вставляется без проблем, но когда я хочу обновить эту запись с помощью этого действия:

public function editAction(Request $request, $id)
{
    $em = $this->getDoctrine()->getManager();

    $userRepository = $em->getRepository('AppBundle:User');

    $user = $userRepository->findOneBy(['id' => $id]);

    $form = $this->createForm(new UserType(), $user);

    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $em->persist($user);
        $em->flush();

        return $this->redirectToRoute('user_list');
    }

    return $this->render('@App/User/form.html.twig', [
        'form' => $form->createView(),
    ]);
}

Я получаю эту ошибку:

An exception occurred while executing 'INSERT INTO user_info (position, info, fullname, user_id) VALUES (?, ?, ?, ?)' with params ["321", "321", "321", 14]:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '14' for key 'UNIQ_B1087D9EA76ED395'

Хорошо, я понимаю, что он хочет вставить новую запись, но что мне сделать, чтобы он ОБНОВИЛ ее или УДАЛИЛ существующую и ВСТАВИЛ новую.


person Justinas    schedule 09.03.2016    source источник
comment
как насчет добавления fetch="EAGER" в свойство User класса $info?   -  person Paweł Mikołajczuk    schedule 09.03.2016
comment
@PawełMikołajczuk Нет, все так же.   -  person Justinas    schedule 09.03.2016


Ответы (2)


Попробуйте сделать User стороной-владельцем, а UserInfo обратной стороной (см. документы).

class User extends BaseUser
{
    /**
     * @ORM\OneToOne(targetEntity="UserInfo", inversedBy="user", cascade={"all"})
     */
    private $info;
}

class UserInfo
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

/**
  * @ORM\OneToOne(targetEntity="User", mappedBy="info")
  */
    private $user;
}

Также связано с этим вопросом SO.

Изменить. Чтобы удалить после обновления старые записи UserInfo без ссылок, установите orphanRemoval в свойстве info класса User.

class User extends BaseUser
{
    /**
     * @ORM\OneToOne(targetEntity="UserInfo", inversedBy="user", cascade={"all"}, orphanRemoval=true)
     */
    private $info;
}
person Genti Saliu    schedule 09.03.2016
comment
Теперь он вставляет информацию в таблицу UserInfo и обновляет столбец info_id в таблице User.. Старые записи просто висят в таблице.. Как удалить старые записи? - person Justinas; 09.03.2016
comment
Попробуйте комментарий Павла и добавьте fetch="EAGER" к свойству info в классе User. - person Genti Saliu; 09.03.2016
comment
Вы пробовали orphanRemoval ? - person Genti Saliu; 09.03.2016

Вам нужно использовать аннотацию JoinColumn в классе User:

/**
 * @ORM\OneToOne(targetEntity="UserInfo", inversedBy="user", cascade={"all"})
 * @ORM\JoinColumn(name="info", referencedColumnName="id")
 */
private $info;

Информация о пользователе:

/**
* @ORM\OneToOne(targetEntity="User", mappedBy="info")
*/
private $user;
person Paweł Mikołajczuk    schedule 09.03.2016