使用递归构建 N 级类别(category)树

建立多级嵌套分类树或菜单总是个问题。许多内容管理系统和网站只允许一定程度的嵌套。为了避免多重连接带来的性能问题,有些网站最多只允许 3-4 级嵌套。现在,我们将探讨如何在不影响性能的情况下,借助递归创建 N 级嵌套分类树或菜单。以下是我们的解决方案:

  1. 我们将为数据库中的类别定义表结构。

  2. 我们将在不使用任何连接或多重查询的情况下获取表中的所有类别。这将是一个使用简单选择语句的单一数据库查询。

  3. 我们将建立一个类别数组,以便利用递归来显示嵌套类别或菜单。

假设我们的数据库中有一个简单的表结构来存储类别,它看起来是这样的:

CREATE TABLE `categories` (
    `id` int(11) NOT NULL,
    `categoryName` varchar(100) NOT NULL,
    `parentCategory` int(11) DEFAULT 0,
    `sortInd` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

为简单起见,我们假设表中不需要其他字段。此外,我们还在表中添加了一些数据,如下所示:

Id categoryName parentCategory sortInd

1

First

0

0

2

Second

1

0

3

Third

1

1

4

Fourth

3

0

5

Fifth

4

0

6

Sixth

5

0

7

Seventh

6

0

8

Eight

7

0

9

Ninth

1

0

10

Tenth

2

1

现在,我们已经为数据库创建了一个表结构,并假设输入了一些示例数据。让我们建立一个查询来检索这些数据,这样我们就可以进入递归解决方案了:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-03.adoc - include::example$Chapter05/4.php[]

前面代码的核心部分是我们如何在数组中存储类别。我们根据父类别来存储结果。这将帮助我们以递归方式显示类别的子类别。这看起来非常简单。现在,基于类别数组,让我们编写递归函数来分层显示类别:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-03.adoc - include::example$Chapter05/4.php[]

前面的代码实际上是递归显示所有类别及其子类别。我们选取一个层级,首先打印该层级上的类别。紧接着,我们将使用代码 showCategoryTree($categories, $category->id) 检查它是否有任何子级类别。现在,如果我们使用根层级(层级 0)调用递归函数,那么输出结果如下:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-03.adoc - include::example$Chapter05/4.php[]

其输出如下:

First
-Second
--Tenth
-Third
---Fourth
----fifth
-----Sixth
------seventh
-------Eighth
-Nineth

正如我们所看到的,不考虑类别级别或多个查询的深度,我们只需一个简单的查询和递归函数即可构建嵌套类别或菜单。如果我们想要具有动态显示和隐藏功能,我们可以使用 <ul><li> 来创建嵌套菜单。这对于在不涉及实现块的情况下高效解决问题至关重要,例如具有固定级别的连接或固定级别的类别。前面的例子完美地展示了尾递归,我们不需要等待递归返回任何东西,随着我们的前进,结果已经显示出来了。

构建嵌套评论回复系统

很多时候,我们都面临着以适当方式显示评论回复的挑战。按时间顺序显示有时并不符合我们的需要。我们可能需要将每条评论的回复显示在实际评论的下方。换句话说,我们需要嵌套评论回复系统或线程评论。

我们想要构建类似于以下屏幕截图的东西:

image 2023 11 07 21 27 50 758

我们可以沿用嵌套类别部分的相同步骤。不过,这次我们将使用一些用户界面元素,使其看起来更真实。假设我们有一个名为 comments 的表,其中包含以下数据和列。为简单起见,我们不讨论多表关系。我们假设用户名与评论存储在同一个表中:

Id comments username Datetime parentID postID

1

First comment

Mizan

2016-10-01 15:10:20

0

1

2

First reply

Adiyan

2016-10-02 04:09:10

1

1

3

Reply of first reply

Mikhael

2016-10-03 11:10:47

2

1

4

Reply of reply of first reply

Arshad

2016-10-04 21:22:45

3

1

5

Reply of reply of reply of first reply

Anam

2016-10-05 12:01:29

4

1

6

Second comment

Keith

2016-10-01 15:10:20

0

1

7

First comment of second post

Milon

2016-10-02 04:09:10

0

2

8

Third comment

Ikrum

2016-10-03 11:10:47

0

1

9

Second comment of second post

Ahmed

2016-10-04 21:22:45

0

2

10

Reply of second comment of second post

Afsar

2016-10-18 05:18:24

9

2

现在,让我们编写一条准备语句来获取一篇文章中的所有评论。然后,我们可以构建一个与嵌套类别类似的数组:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-03.adoc - include::example$Chapter05/5.php[]

现在,我们有了数组和其中所需的所有数据;我们现在可以编写一个函数,递归调用该函数以适当的缩进显示评论:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-03.adoc - include::example$Chapter05/5.php[]

由于我们在 PHP 代码中添加了一些 HTML 元素,因此我们需要一些基本的 CSS 来使其工作。以下是我们为使设计简洁而编写的 CSS 代码。没有什么花哨的东西,只是用纯 CSS 来创建层叠效果,并为注释的每个部分创建一些基本样式:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-03.adoc - include::example$Chapter05/5.php[]

如前所述,我们在此并不试图制作复杂的东西,只是为了响应速度快、设备友好等。我们的假设是,您可以将逻辑集成到应用程序的不同部分,而不会出现任何问题。

以下是数据和前面代码的输出结果:

image 2023 11 07 21 53 29 163

从前面两个示例中,我们可以看出,创建嵌套内容非常简单,无需多个查询,也不受嵌套连接语句的限制。我们甚至不需要自连接来生成嵌套数据。