1. 第一部分:基础准备 - 理解科汛自定义SQL标签的工作模式,并准备好数据库和页面。
  2. 第二部分:核心教程 - 编写并配置三级菜单的自定义SQL标签,从单表查询到多表关联。
  3. 第三部分:前端渲染 - 在模板页面中使用循环标签,将查询出的数据渲染成完整的三级菜单。

第一部分:基础准备

在开始之前,我们需要明确几个关键点并做好准备工作。

科汛自定义sql标签教程 三级菜单
(图片来源网络,侵删)

1 理解科汛自定义SQL标签

科汛的自定义SQL标签允许你直接在模板页面中执行SQL查询,并将查询结果以变量形式传递给模板引擎进行渲染,其基本语法结构如下:

<ks:sql sql="你的SQL语句" var="自定义变量名">
  • sql: 你要执行的SQL查询语句。
  • var: 一个变量名,用于存储查询结果,查询结果通常是一个二维数组(列表),即使只查询到一条数据,也会被包装成一个包含一个元素的数组。

2 准备数据库表结构

为了演示,我们假设你的数据库中有两个表:ks_class (栏目表) 和 ks_content (内容表),一个典型的三级菜单结构是:一级栏目 -> 二级栏目 -> 三级栏目

这里我们简化模型,只用一个 ks_class 表来存储所有层级的栏目,表中需要有一个关键的字段来表示层级关系。

ks_class 表示例:

科汛自定义sql标签教程 三级菜单
(图片来源网络,侵删)
classid (ID) classname (名称) parentid (父级ID) classurl (链接)
1 首页 0 /
2 关于我们 0 /about/
3 产品中心 0 /products/
4 公司简介 2 /about/company/
5 发展历程 2 /about/history/
6 手机系列 3 /products/mobile/
7 电脑系列 3 /products/computer/
8 旗舰手机 6 /products/mobile/flagship/
9 入门手机 6 /products/mobile/entry/

关系说明:

  • parentid = 0 的栏目是一级菜单(如“关于我们”、“产品中心”)。
  • parentid 等于某个一级栏目 classid 的,是二级菜单(如“公司简介”的 parentid 是 2)。
  • parentid 等于某个二级栏目 classid 的,是三级菜单(如“旗舰手机”的 parentid 是 6)。

3 准备模板页面

在你的模板文件中(index.htm),找到你希望显示菜单的位置,我们将在这里编写后续的代码。


第二部分:核心教程 - 编写三级菜单SQL

实现三级菜单的关键在于如何一次性查询出所有需要的数据,并在前端进行逻辑分组,我们提供两种SQL方案:单表查询多表关联查询,对于三级菜单,单表查询通常更简单高效。

单表查询(推荐)

这种方法只查询 ks_class 表,逻辑清晰,性能较好。

科汛自定义sql标签教程 三级菜单
(图片来源网络,侵删)

目标: 查询出所有一级菜单,并同时关联出每个一级菜单下的二级和三级菜单。

SQL语句:

SELECT
    c1.classid AS id1,
    c1.classname AS name1,
    c1.classurl AS url1,
    c2.classid AS id2,
    c2.classname AS name2,
    c2.classurl AS url2,
    c3.classid AS id3,
    c3.classname AS name3,
    c3.classurl AS url3
FROM
    ks_class c1
LEFT JOIN
    ks_class c2 ON c1.classid = c2.parentid
LEFT JOIN
    ks_class c3 ON c2.classid = c3.parentid
WHERE
    c1.parentid = 0  -- 条件:只查询顶级菜单
ORDER BY
    c1.orderby, c1.classid, c2.orderby, c2.classid, c3.orderby, c3.classid;

SQL语句解析:

  1. FROM ks_class c1: 主表是 ks_class,并给它一个别名 c1,代表所有的一级菜单。
  2. LEFT JOIN ks_class c2 ON c1.classid = c2.parentid: 将表自身作为第二张表 c2(二级菜单)进行左连接,连接条件是“一级菜单的ID等于二级菜单的父ID”,这样,每个一级菜单就会关联出它下属的所有二级菜单。
  3. LEFT JOIN ks_class c3 ON c2.classid = c3.parentid: 同理,再将表自身作为第三张表 c3(三级菜单)进行左连接,连接条件是“二级菜单的ID等于三级菜单的父ID”,这样,每个二级菜单就会关联出它下属的所有三级菜单。
  4. WHERE c1.parentid = 0: 这是关键的过滤条件,确保我们只从顶级菜单开始查询,避免数据混乱。
  5. ORDER BY ...: 排序,确保菜单的顺序符合预期,先按一级菜单排序,再按二级,最后按三级。

在科汛标签中使用SQL

我们将上面的SQL语句嵌入到科汛标签中。

<ks:sql sql="SELECT c1.classid AS id1, c1.classname AS name1, c1.classurl AS url1, c2.classid AS id2, c2.classname AS name2, c2.classurl AS url2, c3.classid AS id3, c3.classname AS name3, c3.classurl AS url3 FROM ks_class c1 LEFT JOIN ks_class c2 ON c1.classid = c2.parentid LEFT JOIN ks_class c3 ON c2.classid = c3.parentid WHERE c1.parentid = 0 ORDER BY c1.orderby, c1.classid, c2.orderby, c2.classid, c3.orderby, c3.classid" var="menu_list">

执行后,变量 menu_list 中将存储一个二维数组,数组中的每个元素都代表一个“一级菜单及其下属所有二、三级菜单”的组合,如果一个一级菜单没有二级菜单,id2, name2 等字段将为空。


第三部分:前端渲染 - 循环与嵌套

现在最关键的一步来了:如何将 menu_list 这个扁平化的数组,渲染成我们想要的树形(嵌套)结构。

我们将使用三层嵌套的 <ks:foreach> 循环标签来实现。

完整模板代码示例:

<!-- 在你的模板文件中,<div class="nav">...</div> 内 -->
<style>
    /* 简单的样式,让菜单更清晰 */
    .nav { font-family: Arial, sans-serif; }
    .nav ul { list-style: none; padding: 0; margin: 0; }
    .nav li { position: relative; }
    .nav a { display: block; padding: 8px 15px; text-decoration: none; color: #333; border-bottom: 1px solid #eee; }
    .nav a:hover { background-color: #f0f0f0; }
    .nav .sub-menu { display: none; } /* 默认隐藏二级菜单 */
    .nav .sub-menu .sub-menu { display: none; } /* 默认隐藏三级菜单 */
    /* 鼠标悬停显示子菜单 (可选) */
    .nav > li:hover > .sub-menu { display: block; }
    .nav .sub-menu > li:hover > .sub-menu { display: block; }
</style>
<ul class="nav">
    <!-- 
      第一层循环:遍历所有一级菜单
      item 是循环变量,代表数组中的每一个元素,即一个一级菜单及其关联数据
      key 是索引,这里用不到
    -->
    <ks:foreach name="menu_list" item="item1" key="key1">
        <li>
            <!-- 
              输出一级菜单的名称和链接
              注意:这里要判断 name1 是否存在,因为 LEFT JOIN 可能为空
            -->
            <a href="{if:$item1['url1']}{fun:geturl($item1['url1'])}{else:/}{/if}">
                {$item1['name1']}
            </a>
            <!-- 
              第二层循环:遍历当前一级菜单下的所有二级菜单
              item2 代表每一个二级菜单项
              注意:这里我们只处理 name2 不为空的情况,即确实存在二级菜单
            -->
            <ks:foreach name="item1['name2']" item="item2" key="key2">
                <ul class="sub-menu">
                    <li>
                        <!-- 输出二级菜单的名称和链接 -->
                        <a href="{if:$item1['url2']}{fun:geturl($item1['url2'])}{else:/}{/if}">
                            {$item1['name2']}
                        </a>
                        <!-- 
                          第三层循环:遍历当前二级菜单下的所有三级菜单
                          item3 代表每一个三级菜单项
                          注意:这里我们只处理 name3 不为空的情况
                        -->
                        <ks:foreach name="item1['name3']" item="item3" key="key3">
                            <ul class="sub-menu">
                                <li>
                                    <!-- 输出三级菜单的名称和链接 -->
                                    <a href="{if:$item1['url3']}{fun:geturl($item1['url3'])}{else:/}{/if}">
                                        {$item1['name3']}
                                    </a>
                                </li>
                            </ul>
                        </ks:foreach>
                    </li>
                </ul>
            </ks:foreach>
        </li>
    </ks:foreach>
</ul>

代码解析与关键点

  1. 三层嵌套 <ks:foreach>

    • 第一层ks:foreach name="menu_list",这是我们的主循环,变量 item1 包含了一级菜单及其所有关联的二、三级菜单数据。
    • 第二层ks:foreach name="item1['name2']",这是最巧妙的地方,科汛的foreach标签可以直接对一个数组的某个(可能是重复的)字段进行循环。item1['name2'] 会收集当前一级菜单下所有二级菜单的 name2 值,形成一个临时数组,foreach就会遍历这个数组,每次循环时,item2 会是当前这个二级菜单的所有字段(id2, name2, url2)。
    • 第三层ks:foreach name="item1['name3']",同理,item1['name3'] 会收集当前一级菜单下所有三级菜单的 name3 值,item3 会是当前这个三级菜单的所有字段。
  2. 数据访问

    • 一级菜单数据通过 item1['name1'], item1['url1'] 访问。
    • 二级菜单数据通过 item1['name2'], item1['url2'] 访问。
    • 三级菜单数据通过 item1['name3'], item1['url3'] 访问。
  3. 链接处理

    • {fun:geturl($item1['url1'])} 是科汛的内置函数,用于处理URL,确保链接正确,如果URL为空,则默认跳到首页 。
    • 注意:在 foreach 内部,循环变量(如 item1)在整个循环过程中是同一个引用,所以内部的 foreach 访问的 item1['url2'] 实际上是它父级循环中 item1 的对应字段,这在科汛模板引擎中是可行的。
  4. CSS样式

    • 我添加了一些简单的CSS,用于隐藏子菜单(.sub-menu { display: none; })。
    • 你可以根据需要修改,例如使用 hover 来触发子菜单的显示,或者使用JavaScript实现更复杂的交互(如下拉、滑动等)。

总结与进阶

通过以上三个步骤,你就成功地使用科汛自定义SQL标签实现了一个动态、可配置的三级菜单。

  • 优点

    • 数据与表现分离:菜单数据完全由数据库控制,修改菜单只需在后台管理栏目,无需改动代码。
    • 高效:一次数据库查询获取所有数据,减少了页面加载时的请求次数。
    • 灵活:SQL和模板都可以根据你的实际需求进行修改,例如增加菜单图标、描述等字段。
  • 进阶

    • 高亮当前栏目:可以通过 {ks:get} 标签获取当前页面的栏目ID,然后在循环中判断 item1['id1'], item2['id2'], item3['id3'] 是否与之相等,如果相等则给 <a> 标签添加一个 class="active",并定义其高亮样式。
    • 多表关联:如果你的菜单项需要关联内容表(点击“产品中心”显示最新的产品列表),你可以在SQL中再 JOIN ks_content 表,并获取相关内容信息。
    • 缓存:对于不经常变化的菜单,可以开启科汛的缓存功能,进一步提升网站性能。

希望这份详细的教程能帮助你顺利掌握科汛自定义SQL标签的使用!