核心思路
核心思路是利用 DedeCMS 的 SQL 查询功能,通过 dede_arctype 表和 dede_archives 表的关联,精准地定位到三级栏目及其所有子栏目(即三级栏目本身)的文章。

(图片来源网络,侵删)
关键数据表:
{dede_arctype}: 存储所有栏目信息。{dede_archives}: 存储所有文章的基本信息(标题、ID、发布时间等)。{dede_addonarticle}: 存储文章正文内容(如果使用的是文章模型)。
关键字段:
typeid: 栏目的ID。reid: 栏目的父级栏目ID。topid: 栏目的顶级栏目ID。typedir: 栏目的目录。
使用 {dede:sql} 标签(最灵活、最推荐)
这是最直接、最灵活的方法,可以直接在模板中写 SQL 语句来获取数据。
场景1:调用某个特定三级栏目内的文章
假设您已经知道这个三级栏目的ID是 15。

(图片来源网络,侵删)
模板代码:
{dede:sql sql="SELECT a.*,b.body FROM `dede_archives` a LEFT JOIN `dede_addonarticle` b ON a.id = b.aid WHERE a.typeid = 15 ORDER BY a.pubdate DESC LIMIT 10"}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<span>[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:sql}
代码解释:
SELECT a.*,b.body: 从dede_archives(别名为 a) 表选择所有字段,并从dede_addonarticle(别名为 b) 表中选择body字段(文章内容)。LEFT JOIN ... ON a.id = b.aid: 将文章表和文章内容表通过文章ID (aid) 关联起来。WHERE a.typeid = 15: 这是关键,指定只查询栏目ID为15的文章。ORDER BY a.pubdate DESC: 按发布时间降序排列,最新的在前。LIMIT 10: 限制只调用10篇文章。[field:arcurl/]: 获取文章链接。[field:title/]: 获取文章标题。[field:pubdate ...]: 对发布时间进行格式化。
场景2:调用某个二级栏目下所有三级栏目的文章
假设您要调用二级栏目ID为 5 下的所有三级栏目(比如ID为 15, 16, 17)的文章。
模板代码:

(图片来源网络,侵删)
{dede:sql sql="SELECT a.*,b.body FROM `dede_archives` a LEFT JOIN `dede_addonarticle` b ON a.id = b.aid WHERE a.typeid IN (15, 16, 17) ORDER BY a.pubdate DESC LIMIT 10"}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<span>[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:sql}
代码解释:
WHERE a.typeid IN (15, 16, 17): 使用IN关键字,指定查询多个栏目ID的文章。
如何动态获取二级栏目下的所有三级栏目ID? 您不能在模板中直接写死ID,需要先通过PHP获取,但Dede模板的灵活性有限,一个更优雅的解决方案是使用自定义函数。
(进阶)使用自定义函数动态获取ID
-
在
/include/extend.func.php文件中添加以下函数:/** * 获取指定父栏目下的所有子栏目ID * @param int $pid 父栏目ID * @return string 子栏目ID,用逗号隔开,如 '15,16,17' */ function GetSonTypeId($pid) { global $dsql; $typeid = ''; $query = "SELECT id FROM `dede_arctype` WHERE reid = $pid"; $dsql->Execute('me', $query); while ($row = $dsql->GetArray('me')) { $typeid .= $row['id'] . ','; } // 去掉最后一个逗号 return trim($typeid, ','); } -
在模板中调用:
{dede:sql sql="SELECT a.*,b.body FROM `dede_archives` a LEFT JOIN `dede_addonarticle` b ON a.id = b.aid WHERE a.typeid IN (~GetSonTypeId(5)~) ORDER BY a.pubdate DESC LIMIT 10"} <li> <a href="[field:arcurl/]">[field:title/]</a> <span>[field:pubdate function="MyDate('Y-m-d',@me)"/]</span> </li> {/dede:sql}~GetSonTypeId(5)~是Dede调用自定义函数的语法,它会执行GetSonTypeId(5)并将返回值(如'15,16,17')替换到SQL语句中。
使用嵌套的 {dede:channel} 和 {dede:arclist}
这种方法比较传统,通过循环调用实现,逻辑清晰但代码量稍多。
模板代码:
{dede:channel type='son' typeid='5'} <!-- 假设5是二级栏目的ID -->
{dede:arclist titlelen='30' row='10' orderby='pubdate'}
<li>
<!-- [field:typename/] 可以显示当前是哪个三级栏目 -->
<span>[field:typename/]:</span>
<a href="[field:arcurl/]">[field:title/]</a>
</li>
{/dede:arclist}
{/dede:channel}
代码解释:
{dede:channel type='son' typeid='5'}:
typeid='5': 指定父栏目ID为 5 的二级栏目。
type='son': 获取它的所有直接子栏目(即三级栏目)。
{dede:arclist ...}: 在 {dede:channel} 循环内部,{dede:arclist} 会自动循环遍历每一个子栏目(三级栏目),并调用该栏目下的文章。
优点: 代码直观,不需要写SQL。
缺点: 如果三级栏目很多,会产生多次数据库查询,性能上不如 {dede:sql} 方法,且无法一次性混合所有三级栏目的文章进行排序(比如按时间倒序)。
修改PHP源码(不推荐,有风险)
这是最根本的解决方法,通过修改DedeCMS的核心文件,让 arclist 标签支持 typeid 为三级栏目ID,但强烈不建议普通用户操作,因为升级系统后修改会丢失,且容易出错。
修改步骤(仅作了解):
- 找到文件
/include/taglib/arclist.lib.php。
- 找到
$typeid = trim(preg_replace('/[^0-9,]/', '', $typeid)); 这类代码。
- 在它后面添加逻辑,判断
$typeid 是否是三级栏目,如果是,则获取其所有同级栏目的ID,再进行后续查询。
- 这个逻辑比较复杂,需要理解Dede的栏目结构查询。
除非您是开发者,否则请优先选择 方法一。
总结与建议
方法
优点
缺点
推荐场景
{dede:sql}
性能最好,最灵活,可精确控制查询逻辑
需要编写SQL语句,对新手有一定门槛
强烈推荐,适用于绝大多数三级栏目调用场景,尤其是需要复杂查询或排序时。
嵌套标签
代码直观,易于理解
性能稍差,代码量大,排序不灵活
栏目数量极少,且不需要复杂排序,且不想写SQL时使用。
修改源码
一劳永逸,从根本上解决问题
风险极高,升级会失效,易出错
仅适用于高级开发者,有特殊需求且无法通过模板实现时。
对于绝大多数用户来说,使用 {dede:sql} 标签是最佳选择,特别是结合自定义函数 GetSonTypeId(),可以非常优雅和高效地实现“调用某个二级栏目下所有三级栏目”的需求。
这种方法比较传统,通过循环调用实现,逻辑清晰但代码量稍多。
模板代码:
{dede:channel type='son' typeid='5'} <!-- 假设5是二级栏目的ID -->
{dede:arclist titlelen='30' row='10' orderby='pubdate'}
<li>
<!-- [field:typename/] 可以显示当前是哪个三级栏目 -->
<span>[field:typename/]:</span>
<a href="[field:arcurl/]">[field:title/]</a>
</li>
{/dede:arclist}
{/dede:channel}
代码解释:
{dede:channel type='son' typeid='5'}:typeid='5': 指定父栏目ID为5的二级栏目。type='son': 获取它的所有直接子栏目(即三级栏目)。
{dede:arclist ...}: 在{dede:channel}循环内部,{dede:arclist}会自动循环遍历每一个子栏目(三级栏目),并调用该栏目下的文章。
优点: 代码直观,不需要写SQL。
缺点: 如果三级栏目很多,会产生多次数据库查询,性能上不如 {dede:sql} 方法,且无法一次性混合所有三级栏目的文章进行排序(比如按时间倒序)。
修改PHP源码(不推荐,有风险)
这是最根本的解决方法,通过修改DedeCMS的核心文件,让 arclist 标签支持 typeid 为三级栏目ID,但强烈不建议普通用户操作,因为升级系统后修改会丢失,且容易出错。
修改步骤(仅作了解):
- 找到文件
/include/taglib/arclist.lib.php。 - 找到
$typeid = trim(preg_replace('/[^0-9,]/', '', $typeid));这类代码。 - 在它后面添加逻辑,判断
$typeid是否是三级栏目,如果是,则获取其所有同级栏目的ID,再进行后续查询。 - 这个逻辑比较复杂,需要理解Dede的栏目结构查询。
除非您是开发者,否则请优先选择 方法一。
总结与建议
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
{dede:sql} |
性能最好,最灵活,可精确控制查询逻辑 | 需要编写SQL语句,对新手有一定门槛 | 强烈推荐,适用于绝大多数三级栏目调用场景,尤其是需要复杂查询或排序时。 |
| 嵌套标签 | 代码直观,易于理解 | 性能稍差,代码量大,排序不灵活 | 栏目数量极少,且不需要复杂排序,且不想写SQL时使用。 |
| 修改源码 | 一劳永逸,从根本上解决问题 | 风险极高,升级会失效,易出错 | 仅适用于高级开发者,有特殊需求且无法通过模板实现时。 |
对于绝大多数用户来说,使用 {dede:sql} 标签是最佳选择,特别是结合自定义函数 GetSonTypeId(),可以非常优雅和高效地实现“调用某个二级栏目下所有三级栏目”的需求。
