OneToMany不保存更改

时间:2018-08-08 15:35:07

标签: php symfony symfony-forms symfony-3.4

我在两个实体之间有一个oneToMany-ManyToOne关系。

在“部门”编辑页面(所有者,ManyToOne)上进行编辑时,更改将保存到部门表中,
但是从Utilisateur编辑页面(反面,OneToMany)进行编辑,所做的更改将不会保存到“部门”表中。

有人可以举例说明为什么它不起作用吗?

src / AppBundle / Entity / Utilisateur.php

class Utilisateur implements UserInterface, \Serializable {
    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Departement", mappedBy="commercial")
     */
    private $departements;

    /**
     * Constructor
     */
    public function __construct() {
        $this->departements=new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * @param \AppBundle\Entity\Departement $departement
     * @return Utilisateur
     */
    public function addDepartement(\AppBundle\Entity\Departement $departement)
    {
        $this->departements[] = $departement;

        return $this;
    }

    /**
     * @param \AppBundle\Entity\Departement $departement
     */
    public function removeDepartement(\AppBundle\Entity\Departement $departement)
    {
        $this->departements->removeElement($departement);
    }

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

src / AppBundle / Entity / Departement.php

class Departement {
    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Utilisateur", inversedBy="departements")
     */
    private $commercial;

    /**
     * @param \AppBundle\Entity\Utilisateur $commercial
     * @return Departement
     */
    public function setCommercial(\AppBundle\Entity\Utilisateur $commercial=null) {
        $this->commercial=$commercial;

        return $this;
    }

    /**
     * @return \AppBundle\Entity\Utilisateur
     */
    public function getCommercial() {
        return $this->commercial;
    }
}

src / AppBundle / Form / Utilisateur / Edit3dhType.php

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('departements', EntityType::class, array(
        'class'=>Departement::class,
        'expanded'=>true,
        'multiple'=>true
    ));
}

src / AppBundle / Controller / UtilisateurController.php

/**
 * @Route("/dashboard/utilisateurs/edit-{id}", name="security_edit_user")
 * @Method({"GET", "POST"})
 *
 * @param Request $request
 * @param Utilisateur $utilisateur
 * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
 */
public function editAction(Request $request, Utilisateur $utilisateur) {
    $logo=$utilisateur->getLogo();
    if($utilisateur->getLogo() !== null) {
        $utilisateur->setLogo(new File($this->getParameter('dir_picto').$logo));
    }

    $form=$this->createForm(Edit3dhType::class, $utilisateur, array(
        'action'=>$this->generateUrl('security_edit_user', array('id'=>$utilisateur->getId())),
        'method'=>'POST',
    ));

    $form->handleRequest($request);
    if($form->isSubmitted() && $form->isValid()) {
        if($utilisateur->getLogo() !== null) {
            $utilisateur->setLogo($this->container->get('service.upload')->uploadPicto($utilisateur->getLogo()));
        } else {
            $utilisateur->setLogo($logo);
        }

        $em=$this->getDoctrine()->getManager();
        $em->flush();
    }

    return $this->render('security/edit.html.twig', array(
        'user'=>$this->getUser(),
        'utilisateur'=>$utilisateur,
        'form'=>$form->createView(),
    ));
}

4 个答案:

答案 0 :(得分:0)

您需要确保该实体由Doctrine管理。您可以通过将对象作为参数(persist来调用$em->persist($utilisateur)方法来实现此目的。

虽然您通过Doctrine检索了对象,但该对象已经被管理,因此Doctrine可能不知道它也应该通过OneToMany关系持久化对象。如@Dirk所述,设置级联操作肯定会有所作为。您可以像这样将其添加到Utilisateur类中:

@ORM\OneToMany(targetEntity="AppBundle\Entity\Departement", mappedBy="commercial", cascade={"persist"})

当您试图(通过关系)持久保存一个不受教义管理的实体时,您应该收到一个异常。所以很奇怪你没有一个...

检查表单是否实际上在创建Department实例并将其添加到Utilisateur类中(在将“部门”字段添加到表单时,您没有指定类型)。

答案 1 :(得分:0)

您需要使用cascade={"persist"}定义级联操作,如下所示:

class Departement {
    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Utilisateur", inversedBy="departements", cascade={"persist"} )
     */
    private $commercial;
}

答案 2 :(得分:0)

在FormType中,您需要执行以下操作。

public function buildForm(FormBuilderInterface $builder, array $options)     {

 $builder->add('departements', CollectionType::class, array(
            'entry_type'=> DepartmentType::class,
            'allow_add' => true,
            'allow_delete' => true,
            'allow_extra_fields' => true,
            'by_reference' => false // calls addDepartement / removeDepartement on parent(Utilisateur) automatically
        ));}

有关信息,请参阅:https://symfony.com/doc/current/form/form_collections.html

答案 3 :(得分:0)

所以...感谢TEDx在经过数小时的搜索和阅读,大量的转储以及与熊的小聊天之后向我提示了一些文档,...我才了解了以下内容。

首先,Doctrine似乎只检查关系拥有方的数据。这意味着如果从反面完成,它将无法处理任何事情。

第二,与第一个问题有关,当我试图做相反的事情时,Doctrine不会在表单数组集合中设置departement.commercial。因此,与表中的数据没有区别,它不会进行任何查询。

最后,Doctrine不会检测到该格式中的任何未经检查的条目(因此将departement.commercial设置为NULL)。

在我想到的解决方案下面。它适用于OneToMany / ManyToOne关系的反面。并进行一些调整也可以用于ManyToMany关系。

src / AppBundle / Controller / UtilisateurController.php

public function editAction(Request $request, Utilisateur $utilisateur) {
    //Save pre submit data
    $preSubmitDepartement=$utilisateur->getDepartements()->toArray();

    $form=$this->createForm(UtilisateurType::class, $utilisateur, array(
        'action'=>$this->generateUrl('security_edit_user', array('id'=>$utilisateur->getId())),
        'method'=>'POST',
        'utilisateur'=>$utilisateur,
    ));

    $form->handleRequest($request);
    if($form->isSubmitted() && $form->isValid()) {
        $em=$this->getDoctrine()->getManager();

        //Get post submit data
        $postSubmitDepartement=$utilisateur->getDepartements()->toArray();

        //Keep only IDs for pre and post submit data
        /** @var Departement $v */
        foreach($preSubmitDepartement as $k=>$v) {
            $preSubmitDepartement[$k]=$v->getId();
        }
        /** @var Departement $v */
        foreach($postSubmitDepartement as $k=>$v) {
            $postSubmitDepartement[$k]=$v->getId();
        }

        //Find removed IDs
        $prePostSubmitDifference=array_map('unserialize', array_diff(array_map('serialize',$preSubmitDepartement), array_map('serialize',$postSubmitDepartement)));

        //Fetch related Departement entries
        $departements=$em->getRepository(Departement::class)->findBy(array('id'=>array_merge($postSubmitDepartement, $prePostSubmitDifference)));

        //setCommercial to $utilisateur or null
        /** @var Departement $departement */
        foreach($departements as $departement) {
            if(in_array($departement->getId(), $postSubmitDepartement)) {
                $departement->setCommercial($utilisateur);
            } else if(in_array($departement->getId(), $prePostSubmitDifference)) {
                $departement->setCommercial(null);
            }
        }

        $em->flush();
    }

    return $this->render('security/edit.html.twig', array(
        'user'=>$user,
        'utilisateur'=>$utilisateur,
        'form'=>$form->createView(),
    ));
}

现在我可以从反面设置departement.conseiller