2013-01-07 41 views
0

基本上,我有两个表格(文章和标签),我想使多对多(一个文章可以有很多标签,一个标签可以分配给很多文章)与一些额外的属性。 我可以在Doctrine2中通过将它分成两个单独的关系(一对多,多对一)和一个关系表ArticleTag以及我的额外属性来编写它。Doctrine2 - 保存自定义连接关系表

我的问题是,我不知道我是否可以让Doctrine2为我创建连接表实体。我的意思是,当我打电话:

$article = /* create new article, etc... */ 
$tag = /* create new tag, etc... */ 
$article->addTag($tag); 

$em->persist($article); 
$em->flush(); 

DOES在数据库中创建两个条和标签实体,但它DOES NOT创建ArticleTag实体(换句话说,它不会创建之间的连接文章和标签)。我可以自己创建它,但我宁愿依靠Doctrine2。
当然,当我使用由Doctrine2生成的标准连接表时它工作正常,但我需要这些额外的属性。

有没有人有任何想法或我真的必须手动做?

编辑:源代码

/** 
* @ORM\Entity 
*/ 
class Article { 
    /** 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    * @ORM\Column(type="integer") 
    */ 
    protected $id; 

    /** 
    * @ORM\OneToMany(targetEntity="Tag", mappedBy="article", cascade={"persist"}) 
    * @ORM\JoinTable(name="ArticleTag", joinColumns={@ORM\JoinColumn(name="article_id", referencedColumnName="id")}) 
    *) 
    */ 
    protected $tags; 

    ... 
} 

/** 
* @ORM\Entity 
*/ 
class ArticleTag { 

    /** 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    * @ORM\Column(type="integer") 
    */ 
    protected $id; 

    /** 
    * @ORM\ManyToOne(targetEntity="Article") 
    */ 
    private $article; 

    /** 
    * @ORM\ManyToOne(targetEntity="Tag") 
    * @ORM\JoinColumn(name="tag_id", referencedColumnName="id") 
    */ 
    private $tag; 

    /** 
    * @ORM\Column(type="float") 
    */ 
    protected $priority = 0.5; 

} 

/** 
* @ORM\Entity 
*/ 
class Tag { 

    /** 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    * @ORM\Column(type="integer") 
    */ 
    protected $id; 

    /** 
    * @ORM\Column(type="string", length=32) 
    */ 
    protected $name; 

} 

回答

2

有没有一种方法来自动执行此操作本身,但是你可以在Article类改变addTag方法是这样的:

public function addTag(Tag $tag, $priority) 
{ 
    $articleTag = new ArticleTag(); 
    $articleTag->setTag($tag); 
    $articleTag->setArticle($this); 
    $articleTag->setPriority($priority); 
    $this->addArticleTag($articleTag); 
    return $this; 
} 

这样,你就可以保持你的代码集中和隐藏e创建ArticleTag条目。有关其他解释,请参阅此逻辑背后的推理:

Doctrine眼中的类表示实体源,并且该类的每个实例都代表一个实体。在简单的多对多表中,多对多表不是实体。实际上,它仅仅是两个实体之间的关系,这就是为什么教义允许你绕过这个逻辑,并且如果它只有外键则不需要一个ArticleTag实体。

但是,一旦将其他元数据添加到该表中,它就不再是关系表。我已经与许多看到不同事物的人讨论过这个问题,但事实并非如此。是的,它定义了实体1和实体2是相关的,但该附加列定义了关联所需的附加元数据。因此它是一个实体,必须如此反映。

我曾经为此付出了相当长的时间,直到我最终添加了上面显示的代码。

+0

我明白ArticleTag不是关系表,因为我的额外'优先'列,但我很惊讶,Doctrine2不能处理这个实体的方式与自动生成相同。 – martin

+0

我在回答同样的问题。然而,科林的解决方案并不完美,因为它只适用于单向关系。 如果您管理文章中的标签,但不管理标签中的文章,那没关系。只要您希望能够在标记实体中使用“addArticle()”方法,就必须处理两个相同的ArticleTag实体。的确,“addTag()”和“addArticle()”都会创建一个具有相同值的ArticleTag。 这也只是一个部分解决方案,因为它缺乏持久性和移除的管理。 – Ninj

+0

@Ninj我的解决方案可以相对容易地修改以支持删除以及双向关系。这个问题没有被要求。 –

0

我的实体可以加入他们的标签的文章(在我的架构科目)无任何加盟实体,你需要的出于任何特定原因加入实体?

使用

$article->getSubjects()->add($subject); 

实体

/** 
* @ORM\Entity(repositoryClass="\Fam\Article") 
* @ORM\Table(name="Article") 
*/ 
class Article{ 

    /*StartProtected*/ 

     protected $links = array(); 

     public function __construct() 
     { 
      $this->subjects = new \Doctrine\Common\Collections\ArrayCollection(); 
     } 

     /** 
     * @ORM\ManyToMany(targetEntity="Subject") 
     * @ORM\JoinTable(name="Article_to_Subject", 
     *  joinColumns={@ORM\JoinColumn(name="articleId", referencedColumnName="articleId")}, 
     *  inverseJoinColumns={@ORM\JoinColumn(name="subjectId", referencedColumnName="subjectId")} 
     *  ) 
     */ 
     protected $subjects; 

     /** 
     * 
     * @return \Doctrine\Common\Collections\ArrayCollection 
     */ 
     public function getSubjects() 
     { 
      return $this->subjects; 
     }   

     public function removeSubjects(){ $this->subjects = new \Doctrine\Common\Collections\ArrayCollection(); return $this; } 
+0

那么,我需要为每个关系分配一些属性,因此我需要连接实体。 – martin

+0

darn,我对找到一个没有答案的问题感到兴奋,我以为我知道得到了我的更好;) – CodeMonkey