OneToMany 不保存更改

OneToMany doesn't save changes

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

在部门编辑页面(拥有方,ManyToOne)上编辑时,更改将保存到部门table,
但是从 Utilisateur 编辑页面(反面,OneToMany)进行编辑,更改不会保存到部门 table。

有人可以解释一下为什么它不起作用吗?

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(),
    ));
}

您需要确保该实体是由 Doctrine 管理的。您可以通过将您的对象作为参数 ($em->persist($utilisateur)).

调用 persist 方法来完成此操作

当您通过 Doctrine 检索对象时,它已经被管理,因此 Doctrine 可能不知道它也应该通过 OneToMany 关系持久化对象这一事实。正如@Dirk 提到的,设置级联操作肯定会有所作为。您可以像这样将其添加到 Utilisateur class:

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

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

检查表单是否实际创建了一个 Department 实例并将其添加到 Utilisateur class(添加 "department" 时未指定类型表单字段)。

您需要像这样使用 cascade={"persist"} 定义级联操作:

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

在您的 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

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

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

其次,与第一个问题相关,因为我正在尝试做相反的事情,Doctrine 不会在表单数组集合中设置 departement.commercial。因此,发现与table中的数据没有差异,因此不进行任何查询。

最后,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