【问题标题】:Laravel eloquent recursive childrenLaravel 雄辩的递归孩子
【发布时间】:2019-06-24 19:31:15
【问题描述】:

我得到的结构如下

用户表

id       PK
name
email
username
password

用户层次结构表

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');
}

为了得到所有的孩子,孙子等等,我开发了这个adicional关系,它使用了eager loading

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

到目前为止一切顺利,当我找到一个有 id 的用户时,我可以使用 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();

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

【问题讨论】:

  • 你在纠结什么?你有什么错误吗?有什么问题吗?
  • @Script47 没有错误,“我在实现向下树时遇到了麻烦”“我想要一个属于他的向下树(子递归)和他的第一个直接父母的用户集合”,我将用一个 adicional 示例更新问题

标签: php laravel eloquent


【解决方案1】:

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

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

因此,Laravel 只能天真地做这件事,如果你有一个复杂的树,这会导致很多查询。

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

如果您经常向数据库写入数据,即添加很多新子项但很少阅读结果,那么这可能是您的最佳解决方案。

(编辑:abr 下面的评论将我链接到 MySQL 8 的发行说明,确实具有此功能。我最初的回应是基于 MySQL 5.7。但是,我不知道 Laravel/ Eloquent 有一个使用这个的规范关系解决方案。此外,我以前在 MSSQL 中使用过这个功能,嵌套集是 IMO 更好的解决方案。

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


然而,大多数分层结构的读比写多,在这种情况下,这将开始给您的服务器带来很大压力。

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

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

我们使用https://github.com/lazychaser/laravel-nestedset,它是上述的一个实现,它对我们非常有效。

值得一提的是,当我们重新定义整个树(我们有大约 20,000 个父子关系)时,它可能会很慢并且会占用大量内存,但这只有在我们在层次结构中出现错误时才会发生'不要手动取消,这种情况很少见(我们在 6 个月内没有这样做)。同样,如果您认为您可能必须定期这样做,这可能不是您的最佳选择。

【讨论】:

  • 请允许我不同意 mysql 的递归查询,我可以通过 cte (dev.mysql.com/doc/refman/8.0/en/with.html) 实现这一点。但这并不是重点,如果有的话,这张表应该有 1k 条记录。我的问题的一个解决方案是我可以使用 childrenRecursive 来获取完整的树,使用 foreach 数组来获取每个用户的 id 并应用 whereIn('id', array) 但我希望能达到相同的结果查询。它可能是一棵复杂的树,现在我正在重新阅读这篇文章,我同意单个查询可能不是最好的方式
  • 谢谢!我没有意识到这一点。相应地编辑
  • 我创建了一个使用公用表表达式 (CTE) 实现递归关系的包:github.com/staudenmeir/laravel-adjacency-list
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-08
  • 2014-11-08
  • 1970-01-01
  • 2023-03-11
  • 2022-07-25
相关资源
最近更新 更多