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

htt*_*ete 15 entity symfony

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

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

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

就像是 :

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

        {{ carrot.price }}

    {% endfor %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

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

$ 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;
    }         
}
Run Code Online (Sandbox Code Playgroud)

我以为我也可以这样做:

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

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

            return $carrots;
        }         
    }
Run Code Online (Sandbox Code Playgroud)

这是我的控制器:

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

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

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

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

谢谢!

Ste*_*nte 9

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

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

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

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


Cer*_*rad 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;
}
}
Run Code Online (Sandbox Code Playgroud)

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

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

        {{ carrot.price }}

    {% endfor %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

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


Cer*_*rad 7

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

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

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

更新此内容以解决评论.

  1. Doctrine 2 Repository vs Symfony 2 Service

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

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

对于您当前的要求,任何一种方法都可行.

  1. 胡萝卜派拉姆问题

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

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

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