3

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 设置器为例...

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

    return $this;
}

看起来方法名称的自动生成功能已关闭。它应该是“addCategory”并且应该传递一个“类别”。

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

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

4

2 回答 2

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();
于 2013-12-12T00:45:23.013 回答
0

你没有做错任何事。只是当集合属性有复数名称时,Doctrine 会自动尝试将方法存根的名称单数化。这是 Doctrine 在您运行命令 dictionary:generate:entities 时调用的函数:

$methodName = Inflector::singularize($methodName);

在您的情况下,Doctrine 尝试“单数化”单词categories但无法正确识别单数形式,因此它只是从返回的末尾删除了一个 's' categorie

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

如果你想避免这种情况,要么不使用复数的集合,要么使用复数,然后改变方法。正如@keyboardSmasher 对您的帖子的评论,当使用命令教义:生成:实体时,教义不会覆盖您已经拥有的方法,如果不理会错误的方法不会有太大的伤害。


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

$this->categories[] = $category;

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

于 2016-03-30T12:35:51.797 回答