从 Laravel 上的数据库中检索所有 parent/child 记录(分层数据)

Retrieve all parent/child records from database on Laravel (hierarchical data)

我有以下简化的数据库 table 结构,用于类似遗留票证的系统。

messages
  id         INT
  parent_id  INT
  content    TEXT
  answer     TEXT
  ...

在列表中,我显示了所有消息。单击一条消息时,我会显示其答案等。

问题是,现在我需要制作一个列表结构,包含与此消息相关的所有 parentschildren,以及此消息在树中的位置。 如何从数据库中检索这些信息?

我正在使用 Laravel,但原始 SQL 也可以帮助我找到方向。


示例:

╔════╦═══════════╦════════════════════════╦═════════════════╗
║ id ║ parent_id ║        content         ║     answer      ║
╠════╬═══════════╬════════════════════════╬═════════════════╣
║  1 ║ NULL      ║ Hi, I have a problem   ║ I can't help    ║
║  2 ║ 1         ║ The problem persists   ║ Ok, what is it? ║
║  3 ║ 2         ║ Nevermind, I got this  ║ Oh, well.       ║
║  4 ║ 3         ║ Problem is back        ║ Which problem?  ║
║  5 ║ 4         ║ The same problem again ║ ...             ║
╚════╩═══════════╩════════════════════════╩═════════════════╝

当使用 id = 4 显示消息时,我应该能够显示如下列表:

留言历史:
- 你好,我有一个问题
- 问题仍然存在
- 没关系,我明白了
- 问题又来了
- 又是同样的问题

我只能想到一个循环和几个 SQL 查询执行,对于每个父子,这看起来像代码味道。


更新

正如 Daan 所说,这个问题似乎与 How to create a MySQL hierarchical recursive query 重复。

但是我决定不删除它,因为 Ravan 刚刚用 Laravel 方法回答了它,帮助我解决了问题,所以我将把它留在这里以供将来参考。

由于您正在进行分层操作,因此您应该使用一种策略来保存和从数据库中检索这些数据。

一种方法是使用 Nested Set Model,这样会更容易。 Laravel 有一个很好的包来处理它,叫做 etrepat/baum,它也解释了它是如何工作的,我引用:

背后的理论,一个 TL;DR 版本

可视化嵌套集如何工作的一种简单方法是想象一个 parent 实体包围所有 它的 children,以及它周围的 parent,等等。所以这棵树:

root
  |_ Child 1
    |_ Child 1.1
    |_ Child 1.2
  |_ Child 2
    |_ Child 2.1
    |_ Child 2.2

可以这样形象化:

 ___________________________________________________________________
|  Root                                                             |
|    ____________________________    ____________________________   |
|   |  Child 1                  |   |  Child 2                  |   |
|   |   __________   _________  |   |   __________   _________  |   |
|   |  |  C 1.1  |  |  C 1.2 |  |   |  |  C 2.1  |  |  C 2.2 |  |   |
1   2  3_________4  5________6  7   8  9_________10 11_______12 13  14
|   |___________________________|   |___________________________|   |
|___________________________________________________________________|

数字代表左右边界。 table 然后可能 看起来像这样:

id | parent_id | lft  | rgt  | depth | data
 1 |           |    1 |   14 |     0 | root
 2 |         1 |    2 |    7 |     1 | Child 1
 3 |         2 |    3 |    4 |     2 | Child 1.1
 4 |         2 |    5 |    6 |     2 | Child 1.2
 5 |         1 |    8 |   13 |     1 | Child 2
 6 |         5 |    9 |   10 |     2 | Child 2.1
 7 |         5 |   11 |   12 |     2 | Child 2.2

要获取 parent 节点的所有 children,你

SELECT * WHERE lft IS BETWEEN parent.lft AND parent.rgt

要得到children的个数,是

(right - left - 1)/2

要让一个节点及其所有祖先回到根,您

SELECT * WHERE node.lft IS BETWEEN lft AND rgt

如您所见,递归的查询速度非常慢 普通的树木突然变得相当快。很漂亮,不是吗?