核心思路
无论采用哪种方法,核心思路都是一致的:

(图片来源网络,侵删)
- 获取一级栏目:首先调用所有顶级(一级)栏目。
- 循环一级栏目:对每一个一级栏目,获取其下的所有二级栏目。
- 循环二级栏目:对每一个二级栏目,获取其下的所有三级栏目。
- 循环三级栏目:对每一个三级栏目,调用该栏目下的文章列表。
最常用嵌套 {dede:channel} 标签法
这是最直观、最容易理解的方法,通过 {dede:channel} 标签的嵌套来实现。
适用场景
适用于栏目层级固定、且不需要在首页等地方进行复杂循环调用的场景。
代码示例
{dede:channel type='top' row='8'}
<h2>{dede:field.typename/}</h2>
<ul>
{dede:channel type='son' row='10' currentid=''}
<li>
<!-- 二级栏目名称 -->
<h3>{dede:field.typename/}</h3>
<ul>
{dede:channel type='son' row='15' currentid=''}
<li>
<!-- 三级栏目名称 -->
<h4>{dede:field.typename/}</h4>
<ul>
<!-- 调用当前三级栏目下的文章列表 -->
{dede:arclist titlelen='30' row='5' orderby='pubdate'}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<span>[field:pubdate function="MyDate('Y-m-d', @me)"/]</span>
</li>
{/dede:arclist}
</ul>
</li>
{/dede:channel}
</ul>
</li>
{/dede:channel}
</ul>
{/dede:channel}
代码详解
-
{dede:channel type='top' row='8'}type='top':调用顶级栏目(一级栏目)。row='8':调用8个一级栏目。- 这个标签会循环输出所有一级栏目。
-
{dede:channel type='son' row='10' currentid=''}
(图片来源网络,侵删)- 这个标签嵌套在一级栏目循环内部。
type='son':调用当前一级栏目下的所有子栏目(即二级栏目)。currentid='':留空,表示不进行高亮判断,这样可以获取到所有二级栏目。
-
{dede:channel type='son' row='15' currentid=''}- 这个标签嵌套在二级栏目循环内部。
- 同样使用
type='son',它会调用当前二级栏目下的所有子栏目(即三级栏目)。
-
{dede:arclist titlelen='30' row='5' orderby='pubdate'}- 这个标签嵌套在最内层的三级栏目循环内部。
titlelen='30'长度限制为30个字符。row='5':每篇栏目调用5篇文章。orderby='pubdate':按发布时间降序排列。- 关键点:
{dede:arclist}会自动获取其所在{dede:channel}循环中的当前栏目的ID,从而调用该三级栏目下的文章。
{dede:sql} 自定义查询法
如果栏目非常多,或者逻辑更复杂,使用原生SQL查询可能会更高效和灵活。
适用场景
栏目层级不固定、需要复杂筛选条件,或者对性能有较高要求的场景。
代码示例
{dede:sql sql="
SELECT
c1.id as topid, c1.typename as topname,
c2.id as secid, c2.typename as secname,
c3.id as thid, c3.typename as thname
FROM
`#@__arctype` c1
LEFT JOIN
`#@__arctype` c2 ON c2.reid = c1.id
LEFT JOIN
`#@__arctype` c3 ON c3.reid = c2.id
WHERE
c1.reid = 0 AND c1.ispart <> 2
ORDER BY
c1.sortrank ASC, c2.sortrank ASC, c3.sortrank ASC
"}
<!-- 一级栏目 -->
<h2>[field:topname/]</h2>
<ul>
<!-- 二级栏目 -->
{dede:sql sql="SELECT id, typename FROM `#@__arctype` WHERE reid = ~topid~ ORDER BY sortrank ASC"}
<li>
<h3>[field:typename/]</h3>
<ul>
<!-- 三级栏目及其文章 -->
{dede:sql sql="
SELECT a.id, a.title, a.pubdate, a.arcrank
FROM `#@__arctype` t
LEFT JOIN `#@__archives` a ON a.typeid = t.id
WHERE t.reid = ~id~ AND a.arcrank >= 0
ORDER BY a.pubdate DESC
LIMIT 0, 5
"}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<span>[field:pubdate function="MyDate('Y-m-d', @me)"/]</span>
</li>
{/dede:sql}
</ul>
</li>
{/dede:sql}
</ul>
{/dede:sql}
代码详解
- 最外层查询:查询所有一级栏目及其关联的二级、三级栏目信息。
~topid~:这是织梦SQL标签中的一个变量替换语法,它会自动替换为外层循环中topid字段的值,它被传递给二级栏目的查询,作为reid条件。- 内层查询(二级栏目):根据一级栏目的ID (
~topid~) 查询出所有二级栏目。 - 最内层查询(三级栏目文章):这里我们直接通过
JOIN查询三级栏目及其下的文章。WHERE t.reid = ~id~:这里的~id~是二级栏目的ID,用于找到三级栏目。LEFT JOIN ... ON a.typeid = t.id:将三级栏目表和文章表关联起来。a.arcrank >= 0:这是一个重要的过滤条件,确保只调用已审核通过的文章。
PHP代码调用法(终极灵活)
如果你对PHP比较熟悉,或者需要在特定PHP文件(如 index.php)中实现这个功能,直接使用PHP代码是最高效、最可控的方式。
适用场景
需要在PHP逻辑中处理数据、或者与其他功能结合使用的场景。
代码示例
在模板文件中,你可以这样写:
<?php
// 1. 获取所有顶级栏目
$dsql->SetQuery("SELECT id, typename, sortrank FROM `#@__arctype` WHERE reid=0 AND ispart<>2 ORDER BY sortrank ASC");
$dsql->Execute('top');
while($toprow = $dsql->GetArray('top'))
{
echo "<h2>".$toprow['typename']."</h2>";
echo "<ul>";
// 2. 获取当前顶级栏目下的所有二级栏目
$dsql->SetQuery("SELECT id, typename, sortrank FROM `#@__arctype` WHERE reid=".$toprow['id']." ORDER BY sortrank ASC");
$dsql->Execute('sec_'.$toprow['id']);
while($secrow = $dsql->GetArray('sec_'.$toprow['id']))
{
echo "<li><h3>".$secrow['typename']."</h3><ul>";
// 3. 获取当前二级栏目下的所有三级栏目及其文章
$dsql->SetQuery("
SELECT t.id, t.typename, a.id as aid, a.title, a.pubdate
FROM `#@__arctype` t
LEFT JOIN `#@__archives` a ON a.typeid = t.id AND a.arcrank >= 0
WHERE t.reid=".$secrow['id']."
ORDER BY a.pubdate DESC, t.sortrank ASC
");
$dsql->Execute('thi_'.$secrow['id']);
while($thirow = $dsql->GetArray('thi_'.$secrow['id']))
{
// 注意:这里一个三级栏目下可能有多篇文章,需要判断
if(isset($thirow['aid']) && $thirow['aid'] != '')
{
$arcurl = GetOneArchive($thirow['aid']);
echo "<li><a href='".$arcurl['arcurl']."'>".$thirow['title']."</a></li>";
}
}
echo "</ul></li>";
}
echo "</ul>";
}
?>
代码详解
$dsql->SetQuery(...):设置SQL查询语句。$dsql->Execute(...):执行查询,为了避免资源名冲突,建议使用不同的后缀,如top,sec_1,thi_1等。$dsql->GetArray(...):获取查询结果的一行数据,并将其转为数组。GetOneArchive($aid):这是一个织梦内置函数,根据文章ID获取文章的详细信息,包括URL,比手动拼接URL更可靠。
总结与建议
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| 嵌套标签 | 简单直观,模板化,适合前端开发者。 | 标签嵌套过深时,模板可读性差,性能略低。 | 首页、列表页等常规页面,栏目层级固定。 |
| SQL查询 | 灵活,可以写复杂查询,性能较好。 | SQL语句相对复杂,需要一定的SQL基础。 | 需要复杂筛选、排序或性能要求高的地方。 |
| PHP代码 | 性能最高,逻辑最灵活,可控性最强。 | 需要PHP编程基础,不符合模板分离的思想。 | 在PHP文件中开发复杂功能,或对性能有极致要求。 |
对于绝大多数用户来说,方法一(嵌套标签) 是最常用且最容易上手的,如果发现标签嵌套导致页面加载变慢,再考虑方法二(SQL查询),只有在特殊需求下,才需要使用方法三(PHP代码)。
希望这些详细的解释和示例能帮助你解决问题!
