[TOC]
# 模型創建
假如現在需要開發一個CMS,文檔以創建一個叫Menu欄目的模型為例。

1、(必須)字段中勾選上parent_id字段,父級ID`parent_id`是**必須的,也只能叫它**;
2、(必須)無極限數字大于0,代表最多支持到的層級,確實要無限級就填寫大點
3、建議排序設置為正序,想這種模型一般都希望是按照添加的先后順序,先添加的優先顯示
4、建議勾選上`list_order`,用于模型可以自定義順序
無限級中,系統默認還可以幫你維護3個字段:
`family`:家族族譜,可有可無,如果需要字段名必須是它
`level`:當前行的級別,可有可無,如果需要字段名必須是它
`children_count`:當前行下級數量,可有可無,如果需要字段名必須是它
現在基本上一個無限級功能的模型都做好了(就多了一個parent_id字段,然后無極限數量>0,其他的交給系統了)...
為了演示后面的功能,讓欄目的功能更加合理,Menu我再自定義一個字段:

`is_nav`:表示欄目是否顯示(實際開發中有的欄目可能會不希望前臺顯示出來,到時候前臺的欄目查詢is_nav=1的數據即可;該字段和無級限功能無關,只是你自己的業務而已)
隨便添加一些數據,看下數據表結構:

# 列表改造
這個時候,其實無限極功能已經開發好了,但是列表頁并不太友好。可以簡單的改造下:
找到對應的后臺控制器,比如這里的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();
}
~~~

>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 獲取滿足條件的下級數據。
再看下數據表結構:

只有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
- 安全