[TOC]
# 模型创建
假如现在需要开发一个CMS,文档以创建一个叫Menu栏目的模型为例。
![](https://img.kancloud.cn/25/97/25979bca18557c41dc72a253afcc8007_803x454.png)
1、(必须)字段中勾选上parent_id字段,父级ID`parent_id`是**必须的,也只能叫它**;
2、(必须)无极限数字大于0,代表最多支持到的层级,确实要无限级就填写大点
3、建议排序设置为正序,想这种模型一般都希望是按照添加的先后顺序,先添加的优先显示
4、建议勾选上`list_order`,用于模型可以自定义顺序
无限级中,系统默认还可以帮你维护3个字段:
`family`:家族族谱,可有可无,如果需要字段名必须是它
`level`:当前行的级别,可有可无,如果需要字段名必须是它
`children_count`:当前行下级数量,可有可无,如果需要字段名必须是它
现在基本上一个无限级功能的模型都做好了(就多了一个parent_id字段,然后无极限数量>0,其他的交给系统了)...
为了演示后面的功能,让栏目的功能更加合理,Menu我再自定义一个字段:
![](https://img.kancloud.cn/e7/e1/e7e1a5723fc13f8c8d3b48f4252f25e7_448x203.png)
`is_nav`:表示栏目是否显示(实际开发中有的栏目可能会不希望前台显示出来,到时候前台的栏目查询is_nav=1的数据即可;该字段和无级限功能无关,只是你自己的业务而已)
随便添加一些数据,看下数据表结构:
![](https://img.kancloud.cn/de/31/de31c39bfb9aa405bc1cabd4041f874b_788x286.png)
# 列表改造
这个时候,其实无限极功能已经开发好了,但是列表页并不太友好。可以简单的改造下:
找到对应的后台控制器,比如这里的Menu:`app\admin\controller\Menu`
## 方式一:(不支持ajax)
~~~
// 引入一个trait
use \woo\common\controller\traits\Tree;
public function index()
{
// 列表页稍微改下
return $this->showTree();
}
~~~
还可以根据情况设置一些参数:
~~~
return $this->showTree('', 每页数量 默认5000, 是否显示翻页 默认 false, 是否自动折叠 默认true);
~~~
这样就默认全部展开,不折叠了:
~~~
return $this->showTree('', 5000, false, false);
~~~
自行测试显示效果...
*****
## 方式二:(支持ajax,数据多可以采用该方式)
~~~
// 引入trait
use \woo\common\controller\traits\Tree;
public function index()
{
// 开启ajax加载下级 如果数据量比较多 可以开启
// $this->assign->options['is_ajax'] = true;
// 关闭 添加一级分类 按钮
//$this->local['tool_bar']['create'] = false;
// 关闭 排序一级 分类 按钮
//$this->local['tool_bar']['sortable'] = false;
// 关闭 添加子分类 按钮
//$this->local['item_tool_bar']['create_child'] = false;
// 关闭 排序子分类 按钮
//$this->local['item_tool_bar']['sort_child'] = false;
// 关闭 编辑 按钮
//$this->local['item_tool_bar']['modify'] = false;
// 关闭删除 子分类 按钮
//$this->local['item_tool_bar']['delete'] = false;
// 添加一个字段显示 默认只显示 id和标题(主显字段)
$this->local['fields'] = [
'children_count' => [
'title' => '子' . $this->mdl->cname . '数',
'templet' => '{{# if (d.children_count> 0){ }}{{d.children_count}}{{#} }}',
'style' => 'color:#009688;'
]
];
return $this->showList();
}
~~~
![](https://img.kancloud.cn/63/e9/63e90dafbf8d15601ab21bcae9ee7795_1338x254.png)
>V2.0.2已经支持无限级模型列表自动生成"方式二"代码,但是有个“麻烦”就是需要你删除默认生成的控制器文件,然后在后台重新创建该控制器。
# 缓存结构
如果数据量大的表,不建议调用接下来会自动生成缓存的相关函数,列表使用默认的或第二种方式的ajax加载都不会大批量的生成缓存。
接下来,我们在任意可以执行到的地方,比如就在index方法测试代码:
~~~
pr(tree('Menu'));
~~~
`tree`函数本身的作用和用法,请查看后面的内容。通过tree函数可以获取到指定无限级模型的所有数据。数据结构打印出来大概如下:
~~~
Array
(
[threaded] => Array
(
[1] => Array
(
[4] => Array
(
)
[5] => Array
(
)
)
[2] => Array
(
[6] => Array
(
)
[7] => Array
(
)
)
[3] => Array
(
[8] => Array
(
)
[9] => Array
(
)
)
)
[children] => Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[1] => Array
(
[0] => 4
[1] => 5
)
[4] => Array
(
)
[5] => Array
(
)
[2] => Array
(
[0] => 6
[1] => 7
)
[6] => Array
(
)
[7] => Array
(
)
[3] => Array
(
[0] => 8
[1] => 9
)
[8] => Array
(
)
[9] => Array
(
)
)
[list] => Array
(
[1] => Array
(
[id] => 1
[parent_id] => 0
[title] => 关于我们
[image] =>
[list_order] => 1
[family] => ,1,
[level] => 1
[children_count] => 2
[create_time] => 2020-09-27 17:11:35
[update_time] => 2020-09-27 17:23:58
[delete_time] => 0
[is_nav] => 1
)
[2] => Array
(
[id] => 2
//...
[is_nav] => 1
)
//... 3 4 5 6... 结构一致的内容已经删除
[9] => Array
(
[id] => 9
[parent_id] => 3
[title] => 夏季产品
[image] =>
[list_order] => 9
[family] => ,3,9,
[level] => 2
[children_count] => 0
[create_time] => 2020-09-27 17:13:17
[update_time] => 2020-09-27 17:13:17
[delete_time] => 0
[is_nav] => 1
)
)
)
~~~
我们会发现有3个主要的键(多观察下数据结构):
`threaded`:整表数据螺旋结构;一个N维数组,里面只有主键id值,根据层级关系从一级分类开始一直包含下去(一般使用比较少)
`children`:一个二维数组,里面只有主键id值;给定任意一个id,包含它的子级分类id
`list`:一个二维数组,以主键id值为键,以整行数据为值;给定任意一个id,包含该id对应的所有字段数据
灵活的运用上面三大缓存键,就可以帮你解决很多场景的数据取值了,具体怎么取值查看后面章节。
虽然数据是存在缓存中,不需要每次读取数据库提升效率,但数据发送改变以后系统会自动清除对应缓存,下次获取的时候会再生成缓存,不用担心数据的实时问题。
# 自定义缓存
上面的缓存三大键是查询的所有数据,但我们做CMS应该只显示`is_nav=1`的数据吧,难道我前台遍历栏目的时候还需要自己判断?
如果我可以再缓存中只查询到`is_nav=1`的数据那不就可以了吗?
接下来,我们在`/app/commom/model/trait/MenuTrait.php`文件中(也就是模型中),加入以下方法和代码:
~~~
public function getCustomCache()
{
return [
// nav 是你自定义的缓存键,不要和默认的3大键一致
'nav' => [
// 通过where 自定义缓存的查询条件 order自定义缓存的排序方式
'where' => [
['is_nav', '=', 1]
]
]
];
}
~~~
每当生成缓存的时候,系统都会判断是否有`getCustomCache`方法,用于定义开发者自定义的缓存键 和 查询条件和排序方式等。
然后,随便找一条栏目数据编辑提交一次或点击首页的清除缓存,再刷新首页以后,查看下现在的缓存数据结构:
~~~
Array
(
[threaded] => Array
(
[1] => Array
(
[4] => Array
(
)
[5] => Array
(
)
)
[2] => Array
(
[6] => Array
(
)
[7] => Array
(
)
)
[3] => Array
(
[8] => Array
(
)
[9] => Array
(
)
)
)
[children] => Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[1] => Array
(
[0] => 4
[1] => 5
)
[4] => Array
(
)
[5] => Array
(
)
[2] => Array
(
[0] => 6
[1] => 7
)
[6] => Array
(
)
[7] => Array
(
)
[3] => Array
(
[0] => 8
[1] => 9
)
[8] => Array
(
)
[9] => Array
(
)
)
[nav] => Array
(
[1] => Array
(
[4] => Array
(
)
)
[2] => Array
(
[6] => Array
(
)
[7] => Array
(
)
)
)
[nav_children] => Array
(
[0] => Array
(
[0] => 1
[1] => 2
)
[1] => Array
(
[0] => 4
)
[4] => Array
(
)
[2] => Array
(
[0] => 6
[1] => 7
)
[6] => Array
(
)
[7] => Array
(
)
)
[list] => Array
(
[1] => Array
(
[id] => 1
[parent_id] => 0
[title] => 关于我们
[image] =>
[list_order] => 1
[family] => ,1,
[level] => 1
[children_count] => 2
[create_time] => 2020-09-27 17:11:35
[update_time] => 2020-09-28 17:10:43
[delete_time] => 0
[is_nav] => 1
)
[2] => Array
(
[id] => 2
//...
[is_nav] => 1
)
// 还有 3-8 假设在这里 ...
[9] => Array
(
[id] => 9
[parent_id] => 3
[title] => 夏季产品
[image] =>
[list_order] => 9
[family] => ,3,9,
[level] => 2
[children_count] => 0
[create_time] => 2020-09-27 17:13:17
[update_time] => 2020-09-27 17:13:17
[delete_time] => 0
[is_nav] => 1
)
)
)
~~~
你发发型多了2大键
`nav`: 满足你自定义条件的数据螺旋结构;一个N维数组,里面只有主键id值,根据层级关系从一级分类开始一直包含下去
`nav_children`:一个二维数组,里面只有主键id值;给定任意一个id,包含满足你自定义条件下的子级分类id
每个自定义的键最终都会生成2大缓存分别是N维满足你自定义条件和排序的螺旋数据结构 和_children 一个2维数组 用于存储满足你自定义条件下 给定一个父id 获取满足条件的下级数据。
再看下数据表结构:
![](https://img.kancloud.cn/3d/16/3d16b95229ae871a9d0983d43614ef21_796x292.png)
只有id:3和5栏目隐藏了,为什么nav中没有 8 9?因为他们的父级被隐藏了,子级自然不会有了
为什么nav_children中如果给定3,没有办法获取到8 9这2个可见的导航?还是因为3父级被隐藏了,如果确实需要前台获取3的下级 ,你可以通过children中去拿3的下级。
# 数据获取
为了方便各种情况下的数据获取,系统默认给大家准备了一个函数叫`tree`
~~~
tree('无限极模型名'); // 获取到整个缓存数据 接下来以Menu模型为例
tree('Menu');// 获取到所有缓存数据
tree('Menu', 'threaded');// 获取到整个数据表的 螺旋结构数据
tree('Menu', 'children');// 一个二维数字 获取到所有数据的父子关系
tree('Menu', 'list');// 获取到整个数据的详细字段数据
tree('Menu', 'nav');// 你自定义的键也可以
tree('Menu', 'nav_chidren');
tree('Menu', 'children', 2);// 获取到指定id的下级
tree('Menu', 'nav_children', 2);// 获取到你自定义条件下指定id的下级
tree('Menu', 1);// 获取到指定ID的整行数据 一位数组 字段 => 值
tree('Menu', 1, 'title');// 获取到指定ID的指定字段值
~~~
利用上面的数据获取方式,大致写了一个前台遍历一、二级主导航栏目的示例模板代码:
~~~
<ul>
{foreach :tree('Menu', 'nav') as $level1_id => $level1}
<div>{:tree('Menu', $level1_id, 'title')}</div>
{if $level1}
<ul>
{foreach $level1 as $level2_id => $level2}
<li>{:tree('Menu', $level2_id, 'title')}</li>
{/foreach}
</ul>
{/if}
{/foreach}
</ul>
~~~
如果你感觉还不爽,复制下面代码到你的`app/commom.php`函数库中:
~~~
if (!function_exists('menu')) {
function menu($key = null, $index = null, $default = [])
{
return tree('Menu', $key, $index, $default);
}
}
~~~
每个无限级模型,开发者都可以自行定义一个该函数,把函数名换下,把tree函数中的`模型名`换下,然后:
~~~
menu(); // 获取到整个缓存数据 接下来以Menu模型为例
menu();// 获取到所有缓存数据
menu('threaded');;// 获取到整个数据表的 螺旋结构数据
menu('children');// 一个二维数字 获取到所有数据的父子关系
menu('list');// 获取到整个数据的详细字段数据
menu('nav');// 你自定义的键也可以
menu('nav_chidren');
menu('children', 2);// 获取到指定id的下级
menu('nav_children', 2);// 获取到你自定义条件下指定id的下级
menu(1);// 获取到指定ID的整行数据 一维数组 字段 => 值
menu(1, 'title');// 获取到指定ID的指定字段值
~~~
前台模板示例代码:
~~~
<ul>
{foreach :menu('nav') as $level1_id => $level1}
<div>{:menu($level1_id, 'title')}</div>
{if $level1}
<ul>
{foreach $level1 as $level2_id => $level2}
<li>{:menu($level2_id, 'title')}</li>
{/foreach}
</ul>
{/if}
{/foreach}
</ul>
~~~
还有其他的一些可能会用上的获取数据的方法:
~~~
$tree = new \woo\common\helper\Tree(model('Menu'));// v2.0.3以后可以改为tree('Menu', true)
$data = $tree->getDeepLevel(4);//获取指定id是第多少代
$data = $tree->getDeepParents(4);//获取指定ID的所有 父辈ID 返回一个数组
$data = $tree->getDeepChildren(0);//获取指定ID下的 所有后代ID
$data = $tree->getOptions();// 获取树形选项 自己打印看下
$data = $tree->getXmOptions();// 获取xmselect结构树形选项 自己打印看下
$data = $tree->getLayuiTree();// 获取layui结构的树形选项
~~~
- 2.0开发手册
- 基础
- 简介
- 安装
- 目录
- 规范(必看)
- 快速开发
- 创建模型
- 字段管理
- 无限级开发
- 模型Model
- 定义
- 方法
- 事件
- 关联
- 关联查询
- 验证
- 后台控制器Controller
- 定义
- 列表【index】
- 新增【create】
- 修改【modify】
- 删除【delete】
- 详细【detail】
- 文本审核【antispam】
- 清空数据【clearData】
- 自定义页面
- 视图View
- 视图使用
- 引入CSS和JS
- 基础表单构建Form
- 布局表单构建FormPage
- 表单构建器的基础使用
- 表单项
- 表单分组
- 表单触发器
- 表单布局
- 数据提交验证和入库
- 集成tinymce编辑器
- 集成nkeditor编辑器
- 表格构建Table
- 表格构建器基础使用
- 表格构建器列表字段相关
- 自定义列表头部工具按钮
- 自定义列表项工具按钮
- 自定义搜索
- 定义列表侧边栏
- 静态数据
- 更多属性和回调
- 自定义模板V2.1.0
- 列表统计输出V2.1.2
- 常见问题
- 自定义应用
- 创建新应用
- 应用开发
- API应用
- 自定义插件
- 创建新插件
- 杂项
- 认证Auth
- 权限管理
- 上传Upload
- 批量导入
- 助手库
- 系统配置
- 字典
- 二维码生成
- 源码修改
- 常见问题
- 小技巧,小细节
- 插件
- oauth
- APP一键登录
- 微信小程序登录
- 2.0CMS建站
- 基础
- 安装
- 建站
- Callback
- 引入CSS和JS
- 模板
- 栏目数据
- 列表页数据
- 详细页数据
- 栏目封面
- 自定义表单
- 其他数据和自定义数据
- wap视图层
- 前台搜索
- API开发
- CMS应用日志
- CMS升级指导
- 中台-SAAS开发
- 安装
- 中台日志
- 中台升级指导
- ★★2.0视频教程★★
- 附录
- Admin核心更新日志
- Admin核心升级指导
- composer
- 安全