{dede:loop}标签的核心功能是直接执行一个SQL查询,并将查询结果循环输出,实现多表关联的关键在于编写一个正确的JOIN SQL查询语句。

(图片来源网络,侵删)
下面我将通过一个详细的例子,为你讲解如何实现。
场景示例
假设我们有两个表:
dede_archives:文章主表,存储文章的ID、标题、发布时间等。dede_arctype:文章栏目表,存储栏目的ID、名称、别名等。
我们的目标是:循环输出最新的5篇文章,并在每篇文章标题后面显示它所属的栏目名称。
实现步骤
第1步:理解{dede:loop}标签的基本语法
{dede:loop}标签的基本格式如下:

(图片来源网络,侵删)
{dede:loop table='数据表名' sort='排序字段' row='记录条数' if='查询条件'}
<!-- 循环体内可使用的字段 -->
[field:字段名/]
{/dede:loop}
table: 要查询的数据表名(可以是一个表,也可以是JOIN后的多表)。sort: 排序方式,id DESC(按ID降序)。row: 循环输出的记录数量。if: 查询条件(WHERE子句)。[field:字段名/]: 在循环体内,使用[field:]来访问SQL查询结果中的字段。
第2步:编写关联查询的SQL语句
这是最关键的一步,我们需要将dede_archives和dede_arctype表通过它们的关联字段连接起来。
dede_archives表中有typeid字段,表示文章所属的栏目ID。dede_arctype表中有id字段,表示栏目的ID。
连接条件是 dede_archives.typeid = dede_arctype.id。
完整的SQL查询语句如下:
SELECT
a.id,
a.title,
a.pubdate,
t.typename
FROM
dede_archives AS a
LEFT JOIN
dede_arctype AS t ON a.typeid = t.id
ORDER BY
a.id DESC
LIMIT 0, 5
SQL语句解析:

(图片来源网络,侵删)
SELECT a.id, a.title, a.pubdate, t.typename: 我们需要查询文章的ID、标题、发布时间,以及栏目的名称(typename)。FROM dede_archives AS a: 将dede_archives表起一个别名为a,方便书写。LEFT JOIN dede_arctype AS t ON a.typeid = t.id: 将dede_arctype表(别名为t)通过typeid和id字段进行左连接,使用LEFT JOIN可以确保即使某篇文章没有对应的栏目(数据不完整),文章本身依然会显示出来,只是栏目名称为空。ORDER BY a.id DESC: 按文章ID降序排列,即最新的文章排在前面。LIMIT 0, 5: 限制只查询5条记录。
第3步:在模板文件中使用{dede:loop}
我们将上面编写的SQL语句整合到{dede:loop}标签中,并在模板文件中使用它。
<ul>
{dede:loop table='dede_archives AS a LEFT JOIN dede_arctype AS t ON a.typeid = t.id' sort='a.id DESC' row='5'}
<li>
<!-- 文章标题 -->
<a href="[field:arcurl/]" title="[field:title/]">
[field:title/]
</a>
<!-- 所属栏目名称 -->
([field:typename/])
<!-- 发布时间,这里需要转换一下时间戳格式 -->
([field:pubdate function="MyDate('Y-m-d', @me)"/])
</li>
{/dede:loop}
</ul>
代码解析:
table 属性: 我们直接将完整的JOIN SQL语句的FROM部分(包括表别名和JOIN条件)填入,DedeCMS会自动处理它。
sort 属性: 使用a.id DESC来指定排序,由于我们在table中使用了表别名,这里也需要使用别名a来指代dede_archives表。
row 属性: 设置为5,表示循环5次。
[field:title/]: 输出文章标题,这里的title字段来自dede_archives表(因为我们查询的是a.title)。
[field:typename/]: 输出栏目名称,这里的typename字段来自dede_arctype表(因为我们查询的是t.typename)。{dede:loop}会自动根据查询结果的列名来匹配[field:]
[field:pubdate function="..."/]: pubdate是时间戳,直接显示看不懂,这里使用function属性调用DedeCMS自带的MyDate函数来格式化时间,使其变为年-月-日的格式。
重要注意事项
-
字段别名:
如果你的SQL查询中为字段指定了别名,SELECT a.title AS post_title, ...,那么在模板中就需要使用别名来调用:[field:post_title/],如果不指定别名,则使用原始字段名。
-
性能考虑:
- 尽量只查询你需要的字段,避免使用
SELECT *。
JOIN 的表不宜过多,通常2-3个表是比较合适的,过多的表连接会影响查询性能。
- 确保
JOIN和ORDER BY的字段(尤其是主键ID)已经建立了索引,可以大大提高查询速度。
-
LEFT JOIN vs INNER JOIN:
LEFT JOIN (左连接): 以左边的表(FROM后面的表)为基准,返回所有记录,即使右边没有匹配项,右边没有匹配的字段会显示为NULL,适用于需要保留主表所有数据的情况。
INNER JOIN (内连接): 只返回两个表中字段匹配的记录,如果你只想显示“有对应分类的文章”,可以使用INNER JOIN。
-
安全性:
如果SQL语句中的条件(if属性)或排序(sort属性)可能来自用户输入(如URL参数),请务必进行过滤或转义,以防SQL注入,但对于固定的后台模板,风险较低。
通过以上步骤,你就可以在DedeCMS中灵活地使用{dede:loop}标签来循环输出两个甚至多个关联表的数据了。
我们将上面编写的SQL语句整合到{dede:loop}标签中,并在模板文件中使用它。
<ul>
{dede:loop table='dede_archives AS a LEFT JOIN dede_arctype AS t ON a.typeid = t.id' sort='a.id DESC' row='5'}
<li>
<!-- 文章标题 -->
<a href="[field:arcurl/]" title="[field:title/]">
[field:title/]
</a>
<!-- 所属栏目名称 -->
([field:typename/])
<!-- 发布时间,这里需要转换一下时间戳格式 -->
([field:pubdate function="MyDate('Y-m-d', @me)"/])
</li>
{/dede:loop}
</ul>
代码解析:
table属性: 我们直接将完整的JOINSQL语句的FROM部分(包括表别名和JOIN条件)填入,DedeCMS会自动处理它。sort属性: 使用a.id DESC来指定排序,由于我们在table中使用了表别名,这里也需要使用别名a来指代dede_archives表。row属性: 设置为5,表示循环5次。[field:title/]: 输出文章标题,这里的title字段来自dede_archives表(因为我们查询的是a.title)。[field:typename/]: 输出栏目名称,这里的typename字段来自dede_arctype表(因为我们查询的是t.typename)。{dede:loop}会自动根据查询结果的列名来匹配[field:][field:pubdate function="..."/]:pubdate是时间戳,直接显示看不懂,这里使用function属性调用DedeCMS自带的MyDate函数来格式化时间,使其变为年-月-日的格式。
重要注意事项
-
字段别名: 如果你的SQL查询中为字段指定了别名,
SELECT a.title AS post_title, ...,那么在模板中就需要使用别名来调用:[field:post_title/],如果不指定别名,则使用原始字段名。 -
性能考虑:
- 尽量只查询你需要的字段,避免使用
SELECT *。 JOIN的表不宜过多,通常2-3个表是比较合适的,过多的表连接会影响查询性能。- 确保
JOIN和ORDER BY的字段(尤其是主键ID)已经建立了索引,可以大大提高查询速度。
- 尽量只查询你需要的字段,避免使用
-
LEFT JOINvsINNER JOIN:LEFT JOIN(左连接): 以左边的表(FROM后面的表)为基准,返回所有记录,即使右边没有匹配项,右边没有匹配的字段会显示为NULL,适用于需要保留主表所有数据的情况。INNER JOIN(内连接): 只返回两个表中字段匹配的记录,如果你只想显示“有对应分类的文章”,可以使用INNER JOIN。
-
安全性: 如果SQL语句中的条件(
if属性)或排序(sort属性)可能来自用户输入(如URL参数),请务必进行过滤或转义,以防SQL注入,但对于固定的后台模板,风险较低。
通过以上步骤,你就可以在DedeCMS中灵活地使用{dede:loop}标签来循环输出两个甚至多个关联表的数据了。
