Laravel雄辩的递归孩子

时间:2019-01-31 10:50:04

标签: php laravel eloquent

我得到的结构如下

用户表

id       PK
name
email
username
password

UserHierarchy表

user_parent_id FK of User
user_child_id  FK of User 
(Composite primary key)

我已经写了这两个关系,以便检索谁是用户的父亲以及谁是用户的孩子

public function parent()
{
    return $this->hasManyThrough(\App\Models\User::class, \App\Models\UserHierarchy::class, 'user_child_id', 'id', 'id', 'user_parent_id');
}

public function children()
{
    return $this->hasManyThrough(\App\Models\User::class, \App\Models\UserHierarchy::class, 'user_parent_id', 'id', 'id', 'user_child_id');
}

为了让所有的孩子,孙子孙女等等,我已经建立了这种特殊的关系,它利用了渴望的负荷

public function childrenRecursive()
{
        return $this->children()->with('childrenRecursive.children');
}

到目前为止,当我找到一个具有id的User时,可以使用childrenRecursive获得所有向下的树。我现在想要实现的是重新使用这些关系来过滤一组特定的结果,我的意思是:当它是某个用户(例如id 1)时,我想要一个属于的用户集合他的倒下的树(递归的孩子)和他的第一个直接父母。

$model->where(function ($advancedWhere) use ($id) {
    $advancedWhere->whereHas('parent', function ($advancedWhereHas) use ($filterValue) {
        $advancedWhereHas->orWhere('user_child_id', $id);
        //I want all users that are recorded as his parents
 })->whereHas('childrenRecursive', function ($advancedWhereHas) use ($id) {
        // Missing code, I want all users that are recorded as his children and downwards
 })->get();

enter image description here

这是我正在测试的完整树,上面产生的结果(如果我在childrenRecursive上添加类似的orWhere)是它返回具有父子关系的每个User。例如,用户2应该返回除11和12之外的每个数字,并且它返回除11之外的每个数字(因为11不是任何人的孩子)

1 个答案:

答案 0 :(得分:2)

我将首先回答您的问题,但是在答案的后半部分,我提出了一个强烈建议采用的替代方法。

MySQL(与Microsoft SQL不同)没有选项来编写递归查询。因此,没有很好的Laravel关系可以对此建模。

因此,Laravel除了天真之外没有其他方法,如果您有复杂的树,它将导致许多查询。

基本上,当您加载父级时,您只能访问其子级(作为关系集合)。然后,您将foreach遍历其子级(然后递归地遍历其子级,等等)以生成整个树。每次执行此操作时,它都会对该子代及其子代执行新查询。这实际上是您当前正在执行的操作,并且您会发现,随着数据集的增长,它将开始变得非常缓慢。最后,这为您提供了一个数据结构,您可以在该数据结构上应用代码中的过滤器和条件。 您将无法在单个查询中实现这一目标。

如果您正在向数据库中写入大量数据,即添加许多新子级但很少读取结果,那么这可能是您的最佳解决方案。

(编辑:下面的abr注释将我链接到具有功能的MySQL 8发行说明。我最初的响应基于MySQL 5.7。但是,我不知道Laravel /雄辩地有一个规范的关系解决方案正在使用它,此外,我以前在MSSQL中使用了此功能,而嵌套集是一个更好的IMO解决方案。

此外,Laravel不一定与MySQL耦合-它通常是首选的数据库。因此,它可能永远不会使用这样的特定解决方案来避免这种紧密的耦合。)


但是,大多数分层结构读取的内容多于写入的内容,在这种情况下,这将开始使您的服务器承受很大的压力。

如果是这种情况,我建议您调查一下:

https://en.wikipedia.org/wiki/Nested_set_model

我们使用https://github.com/lazychaser/laravel-nestedset,它是上述的实现,对我们来说很好用。

值得一提的是,当我们重新定义整棵树(大约有20,000个父子关系)时,它可能会很慢并且占用大量内存,但是只有当我们在可以不会被手动取消,这很罕见(我们已经有6个月没有这样做了)。同样,如果您认为您可能必须定期执行此操作,那么这对您来说可能不是最佳选择。