在Symfony中让实体的子女成为孩子的正确方法是什么?

时间:2019-03-18 20:48:08

标签: symfony doctrine-orm symfony4

我正在使用Symfony4。我有一个包含三个表的数据库结构,其中有两个一对多的关联关系。例如:

房子->人->狗

每个房子可以容纳许多人,每个人可以拥有许多狗。 (我的数据库实际上不是关于房子和狗的,实际的表名已更改为保护无辜的人。)使用Doctrine实体,我当然具有类似$house->getPersons()$person->getDogs()的功能。但是,实现诸如$house->getDogs()之类的功能的正确方法是什么?我可以提出几种解决方案,但是对我来说,这些解决方案似乎都不是“好的”解决方案:

  • 我可以简单地添加一个将House与Dog关联的一对多关系。但是,这违反了常规数据库结构的基本规则之一:无冗余数据。房子->人->狗之间的关系已经在数据库中表示出来,因此直接从房子->狗中添加另一个关系不仅是多余的,而且还可能导致狗的人住在一所房子中,但狗的情况生活在另一个(我不想要的东西)中。

  • 我可以做类似的事情

    foreach($house->getPersons() as $person){
        $person->getDogs();
        // Do something with each dog
        ...
    }
    

    这没有得到很好的优化,因为当我可以轻松地通过几个联接运行一个查询时,我将为每个人运行一个单独的查询-可能是很多查询。

  • 我可以使用HouseRepository或House实体中的查询构建器来运行自定义查询。据我了解,这在Symfony中是不赞成的。我们通常不希望在实体类中使用任何DQL或使用存储库,对吗?

  • 我可以在服务/控制器中使用HouseRepository来运行自定义查询。这将是一个简单的方法,但是感觉有点不雅致。不过,我感觉这可能是“正确”的方式。

总结:我有种感觉应该能够以某种方式将这种相当简单的双重联接放入我的实体中,而不必进入存储库级别来获取此数据。明确地说,我不是在问如何编写DQL连接,我不是在问放置它的正确位置,或者是否存在使用Criteria或类似方法进行此操作的Symfony聪明方法。

2 个答案:

答案 0 :(得分:1)

如果要调用$house->getDogs(),可以在House实体中添加一个方法。

public function getDogs()
{
    /** @var Doctrine\Common\Collections\ArrayCollection */
    $dogs = new ArrayCollection();

    if ($persons = $this->getPersons()) {
        foreach($persons as $person) {
            if ($d = $person->getDogs()) {
                $dogs->add($d);
            }
        }
    }

    return $dogs;
}

答案 1 :(得分:0)

以您完成的方式遍历每个人都是完全有效的。如果希望最大程度地减少对数据库的影响,则可以使用一些连接语句在一次访问数据库的过程中“预选择”所有需要的内容。使用此处显示的技术,请尝试以下代码。

假设一对多的关系狗->人,第二个一对多的关系人->狗,则在HouseRepository中使用一种方法将单个查询中的所有相关数据提取出来,包括所有相关的Person和Dog (s)。

class HouseRepository extends ServiceEntityRepository
{
    $qb = $this->createQueryBuilder('house')
        ->leftJoin('house.persons', 'persons')
        ->addSelect('persons')
        ->leftJoin('persons.dogs', 'dogs')
        ->addSelect('dogs')
        ->getQuery()->getResult();
}

这是未经测试的,当然,我怀疑返回的数据结构会很混乱,以至于即使工作正常,您的代码也很难处理。