Symfony 2 - ManyToOne双向关系行为

时间:2013-02-13 01:15:00

标签: symfony doctrine-orm one-to-many many-to-one

我有一段时间试图找出如何设置ManyToOne - > OneToMany与Doctrine 2的关系,但仍无法正常工作......

以下是应用程序行为:

  1. 网站有Page s
  2. User可以在Comment
  3. 上写Page

    以下是我的实体(简化):

    评论实体:

    **
     * @ORM\Entity
     * @ORM\Table(name="comment")
     */
    class Comment {
        /**
         * @ORM\Id
         * @ORM\Column(type="integer")
         * @ORM\GeneratedValue(strategy="AUTO")
         */
         protected $id;
    
        /**
         * Many Comments have One User
         *
         * @ORM\ManyToOne(targetEntity="\Acme\UserBundle\Entity\User", inversedBy="comments")
         */
        protected $user;
    
        /**
         * Many Comments have One Page
         *
         * @ORM\ManyToOne(targetEntity="\Acme\PageBundle\Entity\Page", inversedBy="comments")
         */
        protected $page;
    
        ...
    
        /**
         * Set user
         *
         * @param \Acme\UserBundle\Entity\User $user
         * @return Comment
         */
        public function setUser(\Acme\UserBundle\Entity\User $user)
        {
            $this->user = $user;
            return $this;
        }
    
        /**
         * Set page
         *
         * @param \Acme\PageBundle\Entity\Page $page
         * @return Comment
         */
        public function setPage(\Acme\PageBundle\Entity\Page $page)
        {
            $this->page = $page;
            return $this;
        }
    

    用户实体:

    /**
     * @ORM\Entity
     * @ORM\Table(name="fos_user")
     */
    class User extends BaseUser
    {
        /**
         * @ORM\Id
         * @ORM\Column(type="integer")
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;
    
        /**
         * The User create the Comment so he's supposed to be the owner of this relationship
         * However, Doctrine doc says: "The many side of OneToMany/ManyToOne bidirectional relationships must be the owning
         * side", so Comment is the owner
         *
         * One User can write Many Comments
         *
         * @ORM\OneToMany(targetEntity="Acme\CommentBundle\Entity\Comment", mappedBy="user")
         */
        protected $comments;
    
        ...
    
        /**
         * Get Comments
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getComments() {
            return $this->comments ?: $this->comments = new ArrayCollection();
        } 
    

    页面实体:

    /**
     * @ORM\Entity
     * @ORM\Table(name="page")
     */
    class Page
    {
        /**
         * @ORM\Id
         * @ORM\Column(type="integer")
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;
    
        /**
         * One Page can have Many Comments
         * Owner is Comment
         *
         * @ORM\OneToMany(targetEntity="\Acme\CommentBundle\Entity\Comment", mappedBy="page")
         */
        protected $comments;
    
        ...
    
        /**
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getComments(){
            return $this->comments ?: $this->comments = new ArrayCollection();
        }
    

    我希望双向关系能够从CommentPage(使用User)获取getComments()的集合。

    我的问题,当我尝试保存新的Comment时,我收到一条错误消息,指出该教条无法创建Page实体。我想这种情况正在发生,因为它没有找到Page(但它应该),所以它试图创建一个新的Page实体,以便稍后将它链接到我正在尝试的Comment实体创造。

    以下是我的控制器创建Comment

    的方法
    public function createAction()
        {
            $user = $this->getUser();
            $page = $this->getPage();
    
            $comment = new EntityComment();
            $form = $this->createForm(new CommentType(), $comment);
    
            if ($this->getRequest()->getMethod() === 'POST') {
                $form->bind($this->getRequest());
                if ($form->isValid()) {
                    $em = $this->getDoctrine()->getManager();
    
                    $comment->setPage($page);
                    $comment->setUser($user);
    
                    $em->persist($comment);
                    $em->flush();
    
                    return $this->redirect($this->generateUrl('acme_comment_listing'));
                }
            }
    
            return $this->render('AcmeCommentBundle:Default:create.html.twig', array(
                'form' => $form->createView()
            ));
        }
    

    我不明白为什么会这样。我已经检查了这个控制器中的Page对象(由$this->getPage()返回 - 它返回存储在会话中的对象)并且它是一个有效的Page实体(我已经在DB也是。)

    我现在不知道该怎么做,我找不到有同样问题的人:(

    这是我的确切错误消息:

      

    通过这种关系找到了一个新的实体   'Acme \ CommentBundle \ Entity \ Comment#page'未配置为   实体的级联持久化操作:   ACME \ PageBundle \实体\页@ 000000005d8a1f2000000000753399d4。要解决   此问题:在此显式调用EntityManager#persist()   未知实体或配置级联持久化此关联   映射例如@ManyToOne(..,cascade = {“persist”})。如果你不能   找出导致问题实施的实体   'Acme \ PageBundle \ Entity \ Page #__ toString()'以获得线索。

    但我不想添加cascade={"persist"}因为我不想在级联上创建页面,而只是链接现有页面。

    UPDATE1:

    如果我在设置之前获取页面,它就可以了。但我仍然不知道为什么要这样做。

    public function createAction()
            {
                $user = $this->getUser();
                $page = $this->getPage();
    
                // Fetch the page from the repository
                $page = $this->getDoctrine()->getRepository('AcmePageBundle:page')->findOneBy(array(
                   'id' => $page->getId()
                ));
    
                $comment = new EntityComment();
    
                // Set the relation ManyToOne
                $comment->setPage($page);
                $comment->setUser($user);
    
                $form = $this->createForm(new CommentType(), $comment);
    
                if ($this->getRequest()->getMethod() === 'POST') {
                    $form->bind($this->getRequest());
                    if ($form->isValid()) {
                        $em = $this->getDoctrine()->getManager();
    
                        $em->persist($comment);
                        $em->flush();
    
                        return $this->redirect($this->generateUrl('acme_comment_listing'));
                    }
                }
    
                return $this->render('AcmeCommentBundle:Default:create.html.twig', array(
                    'form' => $form->createView()
                ));
            }
    

    UPDATE2:

    我最终在会话中存储了page_id(而不是完整的对象),我认为这是一个更好的主意,考虑到我不会有一个使用会话来存储而只是id。我还期望Doctrine在检索Page实体时缓存查询。

    但有人可以解释为什么我不能在会话中使用Page实体吗?这就是我设置会话的方式:

    $pages = $site->getPages(); // return doctrine collection
    if (!$pages->isEmpty()) {         
        // Set the first page of the collection in session
        $session = $request->getSession();
        $session->set('page', $pages->first());
    }
    

1 个答案:

答案 0 :(得分:0)

实际上,实体管理器不知道您的Page对象,该对象来自会话。 (正确的术语与实体经理“分离”。) 这就是为什么它试图创建一个新的。

当您从不同来源获取对象时,您必须使用merge函数。 (来自会话,来自unserialize函数等...)

而不是

// Fetch the page from the repository
            $page = $this->getDoctrine()->getRepository('AcmePageBundle:page')->findOneBy(array(
               'id' => $page->getId()
            ));

您可以简单地使用:

$page = $em->merge($page);

如果您想在会话中使用对象,它会对您有所帮助。

有关合并实体here

的更多信息