本文实例讲述了tp5框架前台无限极导航菜单类实现方法。分享给大家供大家参考,具体如下:

适用于 id name pid sort 类似结构的表结构

使用方法:(tp5)

1、将最下面的代码保存到“前台”控制器目录下(名为 frontnav.php),比如(路径): application/index/controll(应用/模块/控制器)

2、在控制器中使用:(application/index/controll/index)(应用/模块/控制器/方法)

也可以放到基础类的初始化方法中,如:base.php 的 _initialize() 方法(不用多解释,这个是 tp5 的初始化方法 貌似 init() 也行?可以自己试试)

使用:

1)、第一步:先实例化本类, 5 个参数。

参数说明:

  • param 1:必填 字符串类型 数据表名称(也是模型名称),不用其实字母大写也行。例如: category
  • param 2:选填 字符串类型 模型所在的路径(默认是:admin模块下的model目录)。如果你不叫 admin,那么书写格式如下:houtai/model
  • param 3:必填 字符串类型 父级栏目字段名称,例如:pid(parent id)
  • param 4:选填 数组类型 默认是按 id 正序排序的,如果有排序字段 sortfield 的值为 字段名称 如 sort 或者 listorder 等…,sortorder 的值为 asc(正序) 或 desc (倒序),建议按这个排序,要不然会显示有点乱,因为权重的关系需要手动排序显示的位置。
  • param 5:必填 二维数组 替换关键词,该参数的第一个数组为顶部导航所需要替换的关键词(必填),linkurl(url 链接)是固定模式,必须这么写,它的值是:模块/控制器/方法,其他的键为要替换的关键词值为字段名称。第二个数组(选填)为二级菜单,第三个数组(选填)为n级菜单,此三个数组个数要对应 $this->createnavhtml() 方法中模版参数的个数,详见 createnavhtml() 方法解释。
$frontnav = new frontnav('category', '', 'pid', array(
'sortfield' => 'sort',
'sortorder' => 'asc'
), array(
array(
'linkurl' => 'index/artlist/index',
'catname' => 'name',
'catdesc' => 'desc'
),
array(
'linkurl' => 'index/artlist/index',
'catname' => 'name',
'catdesc' => 'desc'
)
));

2)、第二步:生成 导航的 html 结构,4个参数

  1. param 1:选填 字符串类型 首页的 html 模版,例如 ‘<li><a class=”navi_home” href=”/”>首页</a></li>’
  2. param 2:必填 数组类型 顶部导航的 html 模版,注意下面实例的格式写法
  3. param 3:选填 数组类型 二级菜单的 html 模版,同上
  4. param 4:选填 数组类型 n级菜单的 html 模版,同上
$navhtml = $frontnav->createnavhtml('<li><a class="navi_home" href="/" rel="external nofollow" rel="external nofollow" >首页</a></li>', array(
'<ul id="jsddm" class="topnav">',
'<li><a href="linkurl" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="catdesc">catname</a>',
'</li>',
'</ul>'
), array(
'<ul class="twolevel">',
'<li><a href="linkurl" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="catdesc">catname</a>',
'</li>',
'</ul>'
), '');

3)、第三步:向模版输出

$this->assign(array(
'navhtml' => $navhtml
));

4)、第四步:模版调用(多余??)

<div id="navi">
{$navhtml}
</div>

提示:

1、替换关键词参数个数与模版(除了首页外)参数个数一定要对应,打字解释的可能有点不明白,详细的对照 实例化 和 创键方法 的代码看几遍就明白了,实在不行可以看源程序,都有较详细的注释。

2、本类默认模型优先,如果没有模型就会查表返回数据库实例。

3、还有一点要注意就是你的替换关键词尽量要跟模版里的字符串不要重复,比如说,你的替换关键词叫 ‘id’ => catename,而模版里 <li id=”xixixi”><a href=”###”>哎呀?</a></li>,要是这样就坏了…

求高手改成php原生的,可联系qq发给我吗?嘿嘿…

具体哪有不清楚的可以联系我qq

效果图:(好像也支持无限极菜单)

<?php
/**
* created by phpstorm.
* user: chao chao
* date: 2017/9/23
* time: 10:18
* versions: 1.0.0
* url: null
* email: 2776332953@qq.com
* phone: ***
*/
namespace app\index\controller;
use think\db;    // 引用 db (数据库链接) 类
use think\url;   // 引用 url ( 创建 url) 类
use think\loader;  // 引用 loader ( 加载 ) 类
class frontnav {
// 数据库实例
protected $db;
// 无限极字段名称
protected $pidname = '';
// 排序设置
protected $sort = array();
// 一级导航html模版
protected $levelone = array();
// 二级导航html模版
protected $leveltwo = array();
// n级导航html模版
protected $leveln = array();
// nav html
protected $navhtml = '';
// 替换关键词
protected $replacekeywords = array();
/**
* frontnav constructor.  构造方法用于生成数据实例与配置参数
* @param string $name 数据表名称或模型名称
* @param string $modelpath 模型所在路径,默认为 admin/model (admin模块下的model目录)
* @param string $pidname 无限极分类的字段(如:pid 或 parentid 等)
* @param string $sort 要排序的字段名称
* @param array $replacekeywords 定义的替换关键词
*/
public function __construct($name, $modelpath, $pidname, $sort, $replacekeywords) {
// $name 为必填参数
if (empty($name) || !is_string($name)) {
throw new \think\exception('参数错误 $name(表名称或模型名称),实例化时该参数必须为字符串类型且不能为空!');
}
// 模型优先考虑 如果 模型类先存在 就返回 模型实例,否则返回 db 类实例。
// 防止大小写错误,先都转换成小写在将第一个字母大写 如:category,因为 linux 区分大小写
$filename = ucwords(strtolower($name));
// 一般栏目的模型都在后台,所以这里就写死了地址 '/admin/model/',也可以传参制定位置
$modelpath = !empty($modelpath) ? strtolower($modelpath) : 'admin/model';
if (class_exists('app\\' . str_replace('/', '\\', $modelpath) . '\\' . $filename)) {
$this->db = loader::model($filename, 'model', false, 'admin');
} else {
// 不确定在 linux 下数据库名称是否区分大小写,所以都转换成小写。
$this->db = db::name(strtolower($filename));
}
// 无限极父类字段不能为空
if (!empty($pidname)) {
$this->pidname = $pidname;
} else {
throw new \think\exception('参数错误 $pidname(父栏目id),实例化时字段名称不能为空!');
}
// 替换关键词
if (empty($replacekeywords) || !is_array($replacekeywords)) {
throw new \think\exception('参数错误 $replacekeywords(替换关键词),实例化时该参数必须是而为数组类型且不能为空!');;
} else {
$this->replacekeywords = $replacekeywords;
}
$this->sort = $sort;
}
/**
* 控制器调用,生成导航菜单。顶层导航的样式( 参数2 $levelonetemplate )为必填项,也就是说最基本的是一层导航,二级和多级是选填项( 参数3: $leveltwotemplate 与 参数4 $levelntemplate 非必填项 )
* @param string $homepagehml 首页 标签的html样式,如: <li><a class="navi_home" href="/" rel="external nofollow" rel="external nofollow" >首页</a></li>
* @param array $levelonetemplate 必填 顶部导航的html样式,如: array(
* '<ul id="jsddm" class="topnav">',  最外层 ul
* '<li><a href="linkurl" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="catdesc">catname</a>',  li标签
* '</li>',  li 结束
* '</ul>' ul 结束
* )
* @param array $leveltwotemplate 选填 二级菜单的html样式,如: array(
* '<ul class="twolevel">',  二级菜单的 ul
* '<li><a href="linkurl" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="catdesc">catname</a>',  li标签
* '</li>',li 结束
* '</ul>'ul 结束
* )
* @param array $levelntemplate 选填 多级菜单的html样式,如: array(
* '<ul class="nlevel">',  n级菜单的 ul
* '<li><a href="linkurl" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="catdesc">catname</a>',  li标签
* '</li>',li 结束
* '</ul>'ul 结束
* @return string
*/
public function createnavhtml($homepagehml, $levelonetemplate, $leveltwotemplate, $levelntemplate) {
// 第一层导航不能为空且必须是数组
if (empty($levelonetemplate) || !is_array($levelonetemplate)) {
throw new \think\exception('参数错误 $levelonetemplate(一级导航模版),该参数必须是数组类型且不能为空!');
}
$this->levelone = $levelonetemplate;
// 二级导航
if (!empty($leveltwotemplate) && !is_array($leveltwotemplate)) {
throw new \think\exception('参数错误 $leveltwotemplate(二级导航模版),该参数可以为空 \'\' 或 array(),否则必须是数组类型!');
}
$this->leveltwo = $leveltwotemplate;
// n级导航
if (!empty($levelntemplate) && !is_array($levelntemplate)) {
throw new \think\exception('参数错误 $levelntemplate(n级导航模版),该参数可以为空 \'\' 或 array(),否则必须是数组类型!');
}
$this->leveln = $levelntemplate;
$treedata = $this->gettreedata($this->getalldata(), 0);
//print_r($treedata);
$this->createhtml($treedata);
return $this->levelone[0] . (!empty($homepagehml) ? $homepagehml : '') . $this->navhtml .
$this->levelone[3] . "\n";
}
/**
* 获取所有数据
* @return array
*/
private function getalldata() {
if (empty($this->sort) || empty($this->sort['sortfield']) || empty($this->sort['sortorder'])) {
return collection($this->db->where(1)
->select())->toarray();
} else {
return collection($this->db->where(1)
->order($this->sort['sortfield'] . ' ' . $this->sort['sortorder'])
->select())->toarray();
}
}
/**
* 将所有数据攒成树状结构的数组
* 增加 levels (层级) children (子数组)
* @param $alldata   传递过来的所有非树状结构的数组
* @param $parentid   初始化时的父栏目id
* @return array    树状结构的数组
*/
private function gettreedata($alldata, $parentid) {
$tree = array();
// 层级计数
static $number = 1;
foreach ($alldata as $v) {
if ($v[$this->pidname] == $parentid) {
if ($v[$this->pidname] == 0) {
$v['levels'] = 0;
} else {
$v['levels'] = $number;
++$number;
}
$v['children'] = $this->gettreedata($alldata, $v['id']);
$tree[] = $v;
} else {
$number = 1;
}
}
return $tree;
}
/**
* 递归生成树状结构的html
* @param $alldata array  由 createnavhtml() 方法传递过来的 树形结构 数据(数组)
* @return     string 返回(最外层ul内部的html)树状结构的html
*/
private function createhtml($alldata) {
foreach ($alldata as $v) {
// 顶部导航
if ($v['levels'] == 0) {
$tempstr0 = $this->levelone[1];
foreach ($this->replacekeywords[0] as $k1 => $v1) {
if ($k1 == 'linkurl') {
$tempstr0 = str_replace($k1, url::build($v1, 'id=' . $v['id']), "\n" . $tempstr0);
} else {
$tempstr0 = str_replace($k1, $v[$v1], $tempstr0);
}
}
$this->navhtml .= $tempstr0;
if (empty($v['children'])) {
$this->navhtml .= $this->levelone[2] . "\n";
} else if (!empty($v['children']) && !empty($this->leveltwo)) {
$this->navhtml .= "\n" . $this->leveltwo[0] . "\n";
$this->createhtml($v['children']);
$this->navhtml .= $this->leveltwo[3] . $this->levelone[2];
}
}
// 二级菜单
if ($v['levels'] == 1) {
$tempstr2 = $this->leveltwo[1];
foreach ($this->replacekeywords[1] as $k1 => $v1) {
if ($k1 == 'linkurl') {
$tempstr2 = str_replace($k1, url::build($v1, 'id=' . $v['id']),       $tempstr2);
} else {
$tempstr2 = str_replace($k1, $v[$v1], $tempstr2);
}
}
$this->navhtml .= $tempstr2;
if (empty($v['children'])) {
$this->navhtml .= $this->leveltwo[2] . "\n";
} else if (!empty($v['children']) && !empty($this->leveln)) {
// 是否多级导航,有 children ,还必须有3级 html 模版
$this->navhtml .= "\n" . $this->leveln[0] . "\n";
$this->createhtml($v['children']);
$this->navhtml .= $this->leveln[3] . $this->leveltwo[2] . "\n";
}
}
// 多级菜单
if (!empty($this->leveln) && $v['levels'] > 1) {
$tempstrn = $this->leveln[1];
foreach ($this->replacekeywords[2] as $k1 => $v1) {
if ($k1 == 'linkurl') {
$tempstrn = str_replace($k1, url::build($v1, 'id=' . $v['id']), $tempstrn);
} else {
$tempstrn = str_replace($k1, $v[$v1], $tempstrn);
}
}
$this->navhtml .= $tempstrn;
if (empty($v['children'])) {
$this->navhtml .= $this->leveln[2] . "\n";
} else {
$this->navhtml .= $this->leveln[0];
$this->createhtml($v['children']);
$this->navhtml .= $this->leveln[3] . $this->leveln[2];
}
}
}
return $this->navhtml;
}
}