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

时间:2015-04-08 14:30:33

标签: php mysql laravel hierarchy hierarchical-data

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

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

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

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

我正在使用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方法回答了它,这有助于我解决问题,所以我将把它留在这里以备将来参考。

1 个答案:

答案 0 :(得分:4)

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

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

背后的理论,TL; DR版本

可视化嵌套集如何工作的简单方法是考虑围绕所有嵌套集的父实体 它的孩子,它的父母围绕着它等等。所以这棵树:

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
|   |___________________________|   |___________________________|   |
|___________________________________________________________________|

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

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

要获取节点的所有子节点,您

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

要获得孩子的数量,这是

(right - left - 1)/2

要让节点及其所有祖先返回根目录,

SELECT * WHERE node.lft IS BETWEEN lft AND rgt

正如您所看到的那样,查询将是递归的并且过于缓慢 普通的树突然很快。漂亮,不是吗?