【问题标题】:Symfony 2 - ManyToOne Bidirectional relationship behaviourSymfony 2 - ManyToOne 双向关系行为
【发布时间】:2013-02-13 17:04:24
【问题描述】:

我花了很长时间试图弄清楚如何与 Doctrine 2 建立 ManyToOne -> OneToMany 关系,但它仍然无法正常工作......

这是应用程序的行为:

  1. 一个网站有Pages
  2. User 可以在Page 上写Comment

这是我的实体(简化版):

评论实体:

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

我希望一个双向关系能够从PageUser(使用getComments())获取Comments 的集合。

我的问题是,当我尝试保存新的 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 实体存在(我也检查了数据库)。

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

这是我收到的确切错误消息:

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

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

更新1:

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

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

更新 2:

我最终将 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());
}

【问题讨论】:

  • 您的Comment 实体中是否缺少page_id 属性?
  • 我不应该在Comment 实体中定义page_id 属性,因为已经定义了ManyToOne 关系(假设可以处理此问题)。在我的数据库架构中,Comment 表中有正确的字段:user_idpage_id
  • 但是你需要在你的@ORM\ManyToOne下添加@ORM\JoinColumn(name="page_id", referencedColumnName="id")。还是我错过了什么?
  • 好吧,我错了!
  • 是的,它已经默认了 ;)

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


【解决方案1】:

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

当你从不同的来源获取一个对象时,你必须使用merge 函数。 (来自会话、来自反序列化函数等...)

代替

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

你可以简单地使用:

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

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

更多关于合并实体here

【讨论】:

  • 谢谢,我明天去看看。但我不确定我想在会话中存储完整的Page 实体。没关系,我想我也可以使用注释?我见过cascade={"merge"},是一样的吗?
  • 所以您的解决方案有效,注释无效!这不是很方便:(
猜你喜欢
  • 1970-01-01
  • 2023-04-09
  • 2021-03-10
  • 2013-06-17
  • 1970-01-01
  • 2017-11-11
  • 2019-05-21
  • 1970-01-01
  • 2015-11-27
相关资源
最近更新 更多