简体   繁体   English

OneToMany不保存更改

[英]OneToMany doesn't save changes

I have a oneToMany - ManyToOne relation between two entities. 我在两个实体之间有一个oneToMany-ManyToOne关系。

When editing on the Departement edit page (owning side, ManyToOne), changes will be saved into the departement table, 在“部门”编辑页面(所有者,ManyToOne)上进行编辑时,所做的更改将保存到部门表中,
but editing from the Utilisateur edit page (inverse side, OneToMany), changes won't be saved into the Departement table. 但是从Utilisateur编辑页面(反面,OneToMany)进行编辑,所做的更改将不会保存到“部门”表中。

Can someone exmplain me why it's not working? 有人可以举例说明为什么它不起作用吗?

src/AppBundle/Entity/Utilisateur.php 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 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 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 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(),
    ));
}

You need to be sure that the entity is managed by Doctrine. 您需要确保该实体由Doctrine管理。 You can do this by calling the persist method with your object as the argument ( $em->persist($utilisateur) ). 您可以通过使用对象作为参数( $em->persist($utilisateur) )调用persist方法来实现。

While you retrieved the object via Doctrine, it is already managed, so Doctrine might be unaware of the fact that it should also persist object via the OneToMany relationship. 当您通过Doctrine检索对象时,该对象已经被管理,因此Doctrine可能没有意识到它也应该通过OneToMany关系持久化对象。 As @Dirk mentioned, setting the cascade operation would certainly make a difference. 如@Dirk所述,设置级联操作肯定会有所作为。 You can add that to the Utilisateur class like so: 您可以像这样将其添加到Utilisateur类中:

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

You should receive an exception when you're trying to persist an entity (via a relation) that is not managed by Doctrine. 当您尝试持久保存不是由Doctrine管理的实体时(通过关系),您应该收到一个异常。 So it's strange that you aren't getting one... 所以很奇怪你没有一个...

Check if the form is actually creating a Department instance and adding it to the Utilisateur class (you're not specifying a type when adding the "department" field to the form). 检查表单是否确实在创建Department实例并将其添加到Utilisateur类(在将“部门”字段添加到表单时未指定类型)。

You need to define your cascading operation with cascade={"persist"} like this: 您需要使用cascade={"persist"}来定义级联操作,如下所示:

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

In your FormType, you need to do the following. 在您的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
        ));}

For Info Read : https://symfony.com/doc/current/form/form_collections.html 有关信息,请阅读: https : //symfony.com/doc/current/form/form_collections.html

So... Thanks to TEDx who hinted me some docs, after hours of searchs and reading, tons of dumps and a small chat with a bear... I came to understand the following. 所以...感谢TEDx在经过数小时的搜索和阅读,大量的转储以及与熊的小聊天之后向我提示了一些文档,我终于明白了以下内容。

First, Doctrine seems to only check data on the owning side of the relation. 首先,Doctrine似乎只检查关系拥有方的数据。 Meaning that it won't handle things if done from the inverse side. 这意味着如果从反面完成,它将无法处理任何事情。

Second, related to the first problem, as I'm trying to do things on the inverse side, Doctrine won't set departement.commercial in the form array collection. 其次,与第一个问题有关,当我试图以相反的方式进行操作时,Doctrine不会在表单数组集合中设置departement.commercial Thus, finding no differences with the data in the table, it doesn't make any query. 因此,与表中的数据没有区别,它不会进行任何查询。

Last, Doctrine won't detect any unchecked entry in the form (so setting departement.commercial to NULL ). 最后,Doctrine将不会检测到该格式中的任何未经检查的条目(因此将departement.commercial设置为NULL )。

Below the solution I came up with. 在我想出的解决方案之下。 It apply for the inverse side of OneToMany/ManyToOne relations. 它适用于OneToMany / ManyToOne关系的反面。 And with some tweak can also be used for ManyToMany relations. 并进行一些调整也可以用于ManyToMany关系。

src/AppBundle/Controller/UtilisateurController.php 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(),
    ));
}

Now I can set departement.conseiller from the inverse side. 现在,我可以从反面设置departement.conseiller

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM