多对多,Doctrine的实体生成器和多元化

时间:2013-03-06 19:20:48

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

Doctrine的多对多逻辑令我感到困惑。我有一个非常简单的多对多关系的食谱与类别的关系。我的基本实体类同样简单。

食谱实体类......

class Recipe
{
    /**
     * @ORM\ManyToMany(targetEntity="Category", inversedBy="categories")
     * @ORM\JoinTable(name="recipe_category")
     **/
    private $categories;

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;


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

分类实体类......

class Category
{
    /**
     * @ORM\ManyToMany(targetEntity="Recipe")
     **/
    private $recipes;

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;


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

看起来很漂亮,并且与Doctrine(和Symfony2)的文档示例相匹配。当我尝试通过Symfony控制台应用程序为这些类生成getter和setter时,会出现奇怪的行为。

关系设置者/获取者不正确。例如,在Recipe类中生成的Category setter ......

/**
 * Add categories
 *
 * @param \Namespace\CookbookBundle\Entity\Category $categories
 * @return Recipe
 */
public function addCategorie(\Namespace\CookbookBundle\Entity\Category $categories)
{
    $this->categories[] = $categories;

    return $this;
}

看起来方法名称的自动生成已关闭。它应该是“addCategory”,应该传递给“类别”。

虽然我可以手动纠正,但如果我重新运行实体生成器,它只会再次添加它们。

我这样做不正确还是这只是实体生成器的怪癖?我可以通过注释指定覆盖吗?

2 个答案:

答案 0 :(得分:0)

你没有做任何错误,因为symfony是如何产生它们的。我通常不使用应用程序/控制台生成它们,因为目前它们没有做好。一个例子就像你提到的那样,你已经提到过多元化的单词。另一个显而易见的事实是,它使用的是[]符号,它将ArrayCollection对象作为PHP数组。您永远不应该将ArrayCollections视为数组。

这就是我自己实现它的方式:

public function addCategory(Category $category)
{
    if (!$this->categories->contains($category)
        $this->categories->add($category);

    return $this;
}

如果已经添加了重复项,则不会向Array集合添加重复项。同样的事情与删除:

public function removeCategory(Category $category)
{
    if ($this->categories->contains($category)
        $this->categories->remove($category);
}

我遇到的很多次是让你说你有4个类别,然后添加和删除它们

$r = new Recipe();
$c1 = new Category();
$c2 = new Category();


$r->addCategory($c1);
$r->addCategory($c2);
// at this point $r->getCategories()->toArray()[0] contains $c1
// and $r->getCategories()->toArray()[1] contains $c2

$r->removeCategory($c1);
// now $r->getCategories()->toArray()[0] is empty and
// $r->getCategories()->toArray()[1] contains $c2 still
// so in order to get the first category you need to:

$r->getCategories()->first();

答案 1 :(得分:0)

你没有做错任何事。只是当集合属性有多个名称时,Doctrine会自动尝试单独化方法存根的名称。这是Doctrine在运行命令doctrine:generate:entities:

时调用的函数
$methodName = Inflector::singularize($methodName);

在你的情况下,学说试图单一化。单词 categories 但无法正确识别单数形式,所以它只是删除了一个&#39> s'从最后返回类别

此外,如您所见,Doctrine并未将传递给方法存根的参数单一化,而是保留$categories而不是一致并将其修改为$categorie

如果你想避免这种情况,要么你不使用复数词来收集,要么使用复数词然后改变方法。正如@keyboardSmasher对你的帖子所做的评论,当使用命令doctrine:generate:entities时,doctrine不会覆盖你已经拥有的方法,如果只留在那里,错误的方法会受到很大的伤害。

最后一点:使用 ArrayCollections 作为数组非常好,所以这段代码是正确的:

$this->categories[] = $category;

ArrayCollection 对象实现 Collection ,后者又实现 ArrayAccess 。这样做是为了能够将 ArrayCollections 用作数组。