Symfony2 - 在Entity中访问存储库功能

时间:2012-02-08 02:22:00

标签: symfony entity

假设我的数据库中有两个表:兔子和胡萝卜。 兔子可以有0或多个胡萝卜,胡萝卜属于一只兔子。这是这两个表之间的1,n关系。

我有两个实体,兔子和胡萝卜。

我在模板中传递了一系列兔子,我想从每只兔子那里得到特定的胡萝卜,然后展示它们:假设我想得到10个更贵的胡萝卜(胡萝卜价格会存放在胡萝卜表中)来自阵列中的每只$ rabbit。

类似的东西:

{% for rabbit in rabbits %}
    {% for carrot in rabbit.getMoreExpensiveCarrots %}

        {{ carrot.price }}

    {% endfor %}
{% endfor %}

我正在使用存储库类,但如果我在Rabbit存储库类中创建函数getMoreExpensiveCarrots($ rabbit),我将无法从类似的实体类访问该函数,这就是我想要的: / p>

$ rabbit-> getMoreExpensiveCarrots()

我认为这样做的方法是在Rabbit实体中创建一个getMoreExpensiveCarrots():

// Entity rabbit
class Rabbit
{
    public function getMoreExpensiveCarrots()
    {
        // Access repository functions like getMoreExpensiveCarrots( $rabbit )
        // But how can I do such thing ? Isn't that bad practise ?
        return $carrots;
    }         
}

我以为我也可以这样做:

    // Entity rabbit
    class Rabbit
    {
        public function getMoreExpensiveCarrots()
        {
            $this->getCarrots();

            // Then try here to sort the carrots by their price, using php            

            return $carrots;
        }         
    }

这是我的控制器:

    public function indexAction()
    {
        $em = $this->getDoctrine()->getEntityManager();

        $rabbits = $em->getRepository('AppNameBundle:Rabbit')->getSomeRabbits();

        return $this->render('AppNameBundle:Home:index.html.twig', 
                array(
                    "rabbits"=>$rabbits
        ));
    }

从模板中的每只兔子调用getMoreExpensiveCarrots函数的最佳做法是什么?

谢谢!

3 个答案:

答案 0 :(得分:8)

您的实体类应该只关心它们所代表的对象,并且完全不了解实体管理器或存储库。

此处可能的解决方案是使用包含RabbitService方法的服务对象(getMoreExpensiveCarrots)。允许该服务知道实体管理器和存储库,因此您可以执行任何复杂的操作。

通过使用服务对象,您可以保持关注点的分离,并确保您的实体类完成他们想要的工作,仅此而已。

假设胡萝卜存储在ArrayCollection中,您也可以使用第二个选项。您只需在方法中执行所需的任何排序逻辑。这样就可以了,因为您要对实体可用的数据进行操作。

答案 1 :(得分:8)

回到基础。忘记存储库与服务,只关注兔子和胡萝卜。

class Rabbit
{
/** @ORM\OneToMany(targetEntity="Carrot", mappedBy="rabbit" */
protected $carrots;

public function getCarrots() { return $this->carrots; }

public function getMoreExpensiveCarrots()
{
    // Get all carrots
    $carrots = $this->getCarrots()->toArray();

    // Sort by price
    // http://php.net/manual/en/function.usort.php
    usort(&$carrots,array($this,'compareTwoCarrotsByPrice'));

    // Now slice off the top 10 carrots (slice - carrots, hah, kindo of funny
    $carrots = array_slice($carrots, 0, 10);

    // And return the sorted carrots
    return $carrots;
}
public function compareTwoCarrotsByPrice($carrot1,$carrot2)
{
    if ($carrot1->getPrice() > $carrot2->getPrice()) return -1;
    if ($carrot1->getPrice() < $carrot2->getPrice()) return  1;
    return 0;
}
}

从查询中删除所有胡萝卜的东西,然后获取兔子列表 您的原始模板现在将完全按预期工作:

{% for rabbit in rabbits %}
    {% for carrot in rabbit.getMoreExpensiveCarrots %}

        {{ carrot.price }}

    {% endfor %}
{% endfor %}

这里唯一的缺点是,对于每只兔子,Doctrine会自动生成对所有胡萝卜的单独查询。在某些时候,性能将受到阻碍。当您到达那一点时,您可以回到原始查询,看看如何更有效地带来胡萝卜。但是让我上面的课程先行,因为我认为这可能是你的阻碍点。

答案 2 :(得分:7)

让我来解释一下。 Doctrine 2 ORM确实需要重新思考。您目前的心态是兔子应该能够在需要时查询它的胡萝卜。你需要改变这种心态。

对于学说2,我们的想法是在你创造兔子后立即给兔子胡萝卜。换句话说,它是在查询时完成的。假设无论什么过程都在查询兔子的数据库,知道你也想要一套特定的胡萝卜。

在这种情况下,你想要10个最昂贵的胡萝卜,所以你的过程需要知道。所以现在回到@Arms回答并使用一个名为loadRabbitsWithExpensiveCarrots的方法自己做一个RabbitManager服务。返回值将是一群兔子已经填满了胡萝卜。

更新此内容以解决评论。

  1. Doctrine 2 Repository vs Symfony 2 Service
  2. 存储库倾向于关注一种类型的实体,即只在处理兔子时最好使用Rabbit存储库。当您开始遇到需要多种类型实体的更复杂的需求时,开始变得难以确定给定函数应该在哪个存储库中。您的应用程序(控制器)将必须知道要引入哪个存储库,也许更多关于内部结构的信息,而不是它真正需要知道的内容。

    Symfony 2服务隐藏了有关兔子和胡萝卜组织方式的所有细节。它是一个更通用的概念,可以处理涉及多个实体的更复杂的查询。它是S2的基本构建块,你确实需要对它感到满意。

    根据您当前的要求,任何一种方法都可行。

    1. 胡萝卜问题
    2. 仍然不确定你的意思。也许您可以发布示例查询?请记住,您当然可以向您的兔子对象添加getExpensiveCarrots()方法。该方法将开始调用getCarrots()。返回的胡萝卜已经被初始查询加载了。您的方法将进行过滤和排序。

      请记住,我们正在尝试处理兔子可能附有数百或数千个胡萝卜的情况。所以我们试图避免加载所有胡萝卜只是为了性能考虑。在初始查询中根本不加载胡萝卜可能更容易。相反,第一次调用rabbit-gt; getCarrots()时,Doctrine 2会自动发出查询并加载该兔子的所有胡萝卜。然后再次使用getExpensiveCarrots方法根据需要进行过滤和排序。

      希望这有帮助。可能只是让你更加困惑。