Tree.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. <?php
  2. namespace tree;
  3. /**
  4. * 通用的树型类,可以生成任何树型结构
  5. */
  6. class Tree
  7. {
  8. /**
  9. * 生成树型结构所需要的2维数组
  10. * @var array
  11. */
  12. public $arr = [];
  13. /**
  14. * 生成树型结构所需修饰符号,可以换成图片
  15. * @var array
  16. */
  17. public $icon = ['│', '├', '└'];
  18. public $nbsp = "&nbsp;";
  19. private $str = '';
  20. /**
  21. * @access private
  22. */
  23. public $ret = '';
  24. /**
  25. * 构造函数,初始化类
  26. * @param array 2维数组,例如:
  27. * array(
  28. * 1 => array('id'=>'1','parent_id'=>0,'name'=>'一级栏目一'),
  29. * 2 => array('id'=>'2','parent_id'=>0,'name'=>'一级栏目二'),
  30. * 3 => array('id'=>'3','parent_id'=>1,'name'=>'二级栏目一'),
  31. * 4 => array('id'=>'4','parent_id'=>1,'name'=>'二级栏目二'),
  32. * 5 => array('id'=>'5','parent_id'=>2,'name'=>'二级栏目三'),
  33. * 6 => array('id'=>'6','parent_id'=>3,'name'=>'三级栏目一'),
  34. * 7 => array('id'=>'7','parent_id'=>3,'name'=>'三级栏目二')
  35. * )
  36. * @return array
  37. */
  38. public function init($arr = [])
  39. {
  40. $this->arr = $arr;
  41. $this->ret = '';
  42. return is_array($arr);
  43. }
  44. /**
  45. * 得到父级数组
  46. * @param int
  47. * @return array
  48. */
  49. public function getParent($myId)
  50. {
  51. $newArr = [];
  52. if (!isset($this->arr[$myId]))
  53. return false;
  54. $pid = $this->arr[$myId]['parent_id'];
  55. $pid = $this->arr[$pid]['parent_id'];
  56. if (is_array($this->arr)) {
  57. foreach ($this->arr as $id => $a) {
  58. if ($a['parent_id'] == $pid)
  59. $newArr[$id] = $a;
  60. }
  61. }
  62. return $newArr;
  63. }
  64. /**
  65. * 得到子级数组
  66. * @param int
  67. * @return array
  68. */
  69. public function getChild($myId)
  70. {
  71. $newArr = [];
  72. if (is_array($this->arr)) {
  73. foreach ($this->arr as $id => $a) {
  74. if ($a['parent_id'] == $myId) {
  75. $newArr[$id] = $a;
  76. }
  77. }
  78. }
  79. return $newArr ? $newArr : false;
  80. }
  81. /**
  82. * 读取指定节点的所有孩子节点
  83. * @param int $myid 节点ID
  84. * @param boolean $withself 是否包含自身
  85. * @return array
  86. */
  87. public function getChildren($myId, $withself = FALSE)
  88. {
  89. $newArr = [];
  90. foreach ($this->arr as $value)
  91. {
  92. if (!isset($value['id']))
  93. continue;
  94. if ($value['parent_id'] == $myId)
  95. {
  96. $newArr[] = $value;
  97. $newArr = array_merge($newArr, $this->getChildren($value['id']));
  98. }
  99. else if ($withself && $value['id'] == $myId)
  100. {
  101. $newArr[] = $value;
  102. }
  103. }
  104. return $newArr;
  105. }
  106. /**
  107. * 读取指定节点的所有孩子节点ID
  108. * @param int $myid 节点ID
  109. * @param boolean $withself 是否包含自身
  110. * @return array
  111. */
  112. public function getChildrenIds($myId, $withself = FALSE)
  113. {
  114. $childrenlist = $this->getChildren($myId, $withself);
  115. $childrenids = [];
  116. foreach ($childrenlist as $k => $v)
  117. {
  118. $childrenids[] = $v['id'];
  119. }
  120. return $childrenids;
  121. }
  122. /**
  123. * 得到当前位置数组
  124. * @param int
  125. * @return array
  126. */
  127. public function getPosition($myId, &$newArr)
  128. {
  129. $a = [];
  130. if (!isset($this->arr[$myId]))
  131. return false;
  132. $newArr[] = $this->arr[$myId];
  133. $pid = $this->arr[$myId]['parent_id'];
  134. if (isset($this->arr[$pid])) {
  135. $this->getPosition($pid, $newArr);
  136. }
  137. if (is_array($newArr)) {
  138. krsort($newArr);
  139. foreach ($newArr as $v) {
  140. $a[$v['id']] = $v;
  141. }
  142. }
  143. return $a;
  144. }
  145. /**
  146. * 得到树型结构
  147. * @param int ID,表示获得这个ID下的所有子级
  148. * @param string 生成树型结构的基本代码,例如:"<option value=\$id \$selected>\$spacer\$name</option>"
  149. * @param int 被选中的ID,比如在做树型下拉框的时候需要用到
  150. * @return string
  151. */
  152. public function getTree($myId, $str, $sid = 0, $adds = '', $str_group = '')
  153. {
  154. $number = 1;
  155. //一级栏目
  156. $child = $this->getChild($myId);
  157. if (is_array($child)) {
  158. $total = count($child);
  159. foreach ($child as $key => $value) {
  160. $j = $k = '';
  161. if ($number == $total) {
  162. $j .= $this->icon[2];
  163. } else {
  164. $j .= $this->icon[1];
  165. $k = $adds ? $this->icon[0] : '';
  166. }
  167. $spacer = $adds ? $adds . $j : '';
  168. $selected = $value['id'] == $sid ? 'selected' : '';
  169. $id = 0;
  170. $nstr = '';
  171. @extract($value);
  172. $parentId = $value['parent_id'];
  173. $parentId == 0 && $str_group ? eval("\$nstr = \"$str_group\";") : eval("\$nstr = \"$str\";");
  174. $this->ret .= $nstr;
  175. $nbsp = $this->nbsp;
  176. $this->getTree($id, $str, $sid, $adds . $k . $nbsp, $str_group);
  177. $number++;
  178. }
  179. }
  180. return $this->ret;
  181. }
  182. /**
  183. * 生成树型结构数组
  184. * @param int myID,表示获得这个ID下的所有子级
  185. * @param int $maxLevel 最大获取层级,默认不限制
  186. * @param int $level 当前层级,只在递归调用时使用,真实使用时不传入此参数
  187. * @return array
  188. */
  189. public function getTreeArray($myId, $maxLevel = 0, $level = 1)
  190. {
  191. $returnArray = [];
  192. //一级数组
  193. $children = $this->getChild($myId);
  194. if (is_array($children)) {
  195. foreach ($children as $child) {
  196. $child['_level'] = $level;
  197. $returnArray[$child['id']] = $child;
  198. if ($maxLevel === 0 || ($maxLevel !== 0 && $maxLevel > $level)) {
  199. $mLevel = $level + 1;
  200. $returnArray[$child['id']]["children"] = $this->getTreeArray($child['id'], $maxLevel, $mLevel);
  201. }
  202. }
  203. }
  204. return $returnArray;
  205. }
  206. /**
  207. *
  208. * 获取树状数组
  209. * @param string $myid 要查询的ID
  210. * @param string $nametpl 名称条目模板
  211. * @param string $itemprefix 前缀
  212. * @return string
  213. */
  214. public function getTreeArrayO($myid, $itemprefix = '')
  215. {
  216. $childs = $this->getChild($myid);
  217. $n = 0;
  218. $data = [];
  219. $number = 1;
  220. if ($childs)
  221. {
  222. $total = count($childs);
  223. foreach ($childs as $id => $value)
  224. {
  225. $j = $k = '';
  226. if ($number == $total)
  227. {
  228. $j .= $this->icon[2];
  229. $k = $itemprefix ? $this->nbsp : '';
  230. }
  231. else
  232. {
  233. $j .= $this->icon[1];
  234. $k = $itemprefix ? $this->icon[0] : '';
  235. }
  236. $spacer = $itemprefix ? $itemprefix . $j : '';
  237. $value['spacer'] = $spacer;
  238. $data[$n] = $value;
  239. $data[$n]['childlist'] = $this->getTreeArrayO($value['id'], $itemprefix . $k . $this->nbsp);
  240. $n++;
  241. $number++;
  242. }
  243. }
  244. return $data;
  245. }
  246. /**
  247. * 将getTreeArray的结果返回为二维数组
  248. * @param array $data
  249. * @return array
  250. */
  251. public function getTreeList($data = [], $field = 'name')
  252. {
  253. $arr = [];
  254. foreach ($data as $k => $v)
  255. {
  256. $childlist = isset($v['childlist']) ? $v['childlist'] : [];
  257. unset($v['childlist']);
  258. $v[$field] = $v['spacer'] . ' ' . $v[$field];
  259. $v['haschild'] = $childlist ? 1 : 0;
  260. if ($v['id'])
  261. $arr[] = $v;
  262. if ($childlist)
  263. {
  264. $arr = array_merge($arr, $this->getTreeList($childlist, $field));
  265. }
  266. }
  267. return $arr;
  268. }
  269. /**
  270. * 同上一方法类似,但允许多选
  271. */
  272. public function getTreeMulti($myId, $str, $sid = 0, $adds = '')
  273. {
  274. $number = 1;
  275. $child = $this->getChild($myId);
  276. if (is_array($child)) {
  277. $total = count($child);
  278. foreach ($child as $id => $a) {
  279. $j = $k = '';
  280. if ($number == $total) {
  281. $j .= $this->icon[2];
  282. } else {
  283. $j .= $this->icon[1];
  284. $k = $adds ? $this->icon[0] : '';
  285. }
  286. $spacer = $adds ? $adds . $j : '';
  287. $selected = $this->have($sid, $id) ? 'selected' : '';
  288. @extract($a);
  289. eval("\$nstr = \"$str\";");
  290. $this->ret .= $nstr;
  291. $this->getTreeMulti($id, $str, $sid, $adds . $k . '&nbsp;');
  292. $number++;
  293. }
  294. }
  295. return $this->ret;
  296. }
  297. /**
  298. * @param integer $myId 要查询的ID
  299. * @param string $str 第一种HTML代码方式
  300. * @param string $str2 第二种HTML代码方式
  301. * @param integer $sid 默认选中
  302. * @param integer $adds 前缀
  303. */
  304. public function getTreeCategory($myId, $str, $str2, $sid = 0, $adds = '')
  305. {
  306. $number = 1;
  307. $child = $this->getChild($myId);
  308. if (is_array($child)) {
  309. $total = count($child);
  310. foreach ($child as $id => $a) {
  311. $j = $k = '';
  312. if ($number == $total) {
  313. $j .= $this->icon[2];
  314. } else {
  315. $j .= $this->icon[1];
  316. $k = $adds ? $this->icon[0] : '';
  317. }
  318. $spacer = $adds ? $adds . $j : '';
  319. $selected = $this->have($sid, $id) ? 'selected' : '';
  320. @extract($a);
  321. if (empty($html_disabled)) {
  322. eval("\$nstr = \"$str\";");
  323. } else {
  324. eval("\$nstr = \"$str2\";");
  325. }
  326. $this->ret .= $nstr;
  327. $this->getTreeCategory($id, $str, $str2, $sid, $adds . $k . '&nbsp;');
  328. $number++;
  329. }
  330. }
  331. return $this->ret;
  332. }
  333. /**
  334. * 同上一类方法,jquery treeview 风格,可伸缩样式(需要treeview插件支持)
  335. * @param $myId 表示获得这个ID下的所有子级
  336. * @param $effected_id 需要生成treeview目录数的id
  337. * @param $str 末级样式
  338. * @param $str2 目录级别样式
  339. * @param $showlevel 直接显示层级数,其余为异步显示,0为全部限制
  340. * @param $style 目录样式 默认 filetree 可增加其他样式如'filetree treeview-famfamfam'
  341. * @param $currentlevel 计算当前层级,递归使用 适用改函数时不需要用该参数
  342. * @param $recursion 递归使用 外部调用时为FALSE
  343. * @return string
  344. */
  345. function getTreeView($myId, $effected_id = 'example', $str = "<span class='file'>\$name</span>", $str2 = "<span class='folder'>\$name</span>", $showlevel = 0, $style = 'filetree ', $currentlevel = 1, $recursion = FALSE)
  346. {
  347. $child = $this->getChild($myId);
  348. if (!defined('EFFECTED_INIT')) {
  349. $effected = ' id="' . $effected_id . '"';
  350. define('EFFECTED_INIT', 1);
  351. } else {
  352. $effected = '';
  353. }
  354. $placeholder = '<ul><li><span class="placeholder"></span></li></ul>';
  355. if (!$recursion)
  356. $this->str .= '<ul' . $effected . ' class="' . $style . '">';
  357. foreach ($child as $id => $a) {
  358. @extract($a);
  359. if ($showlevel > 0 && $showlevel == $currentlevel && $this->getChild($id))
  360. $folder = 'hasChildren'; //如设置显示层级模式@2011.07.01
  361. $floder_status = isset($folder) ? ' class="' . $folder . '"' : '';
  362. $this->str .= $recursion ? '<ul><li' . $floder_status . ' id=\'' . $id . '\'>' : '<li' . $floder_status . ' id=\'' . $id . '\'>';
  363. $recursion = FALSE;
  364. //判断是否为终极栏目
  365. if ($child == 1) {
  366. eval("\$nstr = \"$str2\";");
  367. $this->str .= $nstr;
  368. if ($showlevel == 0 || ($showlevel > 0 && $showlevel > $currentlevel)) {
  369. $this->getTreeView($id, $effected_id, $str, $str2, $showlevel, $style, $currentlevel + 1, TRUE);
  370. } elseif ($showlevel > 0 && $showlevel == $currentlevel) {
  371. $this->str .= $placeholder;
  372. }
  373. } else {
  374. eval("\$nstr = \"$str\";");
  375. $this->str .= $nstr;
  376. }
  377. $this->str .= $recursion ? '</li></ul>' : '</li>';
  378. }
  379. if (!$recursion)
  380. $this->str .= '</ul>';
  381. return $this->str;
  382. }
  383. /**
  384. * 同上一类方法,jquery treeview 风格,可伸缩样式(需要treeview插件支持)
  385. * @param $myId 表示获得这个ID下的所有子级
  386. * @param $effected_id 需要生成treeview目录数的id
  387. * @param $str 末级样式
  388. * @param $str2 目录级别样式
  389. * @param $showlevel 直接显示层级数,其余为异步显示,0为全部限制
  390. * @param $style 目录样式 默认 filetree 可增加其他样式如'filetree treeview-famfamfam'
  391. * @param $currentlevel 计算当前层级,递归使用 适用改函数时不需要用该参数
  392. * @param $recursion 递归使用 外部调用时为FALSE
  393. * @param $dropdown 有子元素时li的class
  394. */
  395. function getTreeViewMenu($myId, $effected_id = 'example', $str = "<span class='file'>\$name</span>", $str2 = "<span class='folder'>\$name</span>", $showlevel = 0, $ul_class = "", $li_class = "", $style = 'filetree ', $currentlevel = 1, $recursion = FALSE, $dropdown = 'hasChild')
  396. {
  397. $child = $this->getChild($myId);
  398. if (!defined('EFFECTED_INIT')) {
  399. $effected = ' id="' . $effected_id . '"';
  400. define('EFFECTED_INIT', 1);
  401. } else {
  402. $effected = '';
  403. }
  404. $placeholder = '<ul><li><span class="placeholder"></span></li></ul>';
  405. if (!$recursion) {
  406. $this->str .= '<ul' . $effected . ' class="' . $style . '">';
  407. }
  408. foreach ($child as $id => $a) {
  409. @extract($a);
  410. if ($showlevel > 0 && is_array($this->getChild($a['id']))) {
  411. $floder_status = " class='$dropdown $li_class'";
  412. } else {
  413. $floder_status = " class='$li_class'";;
  414. }
  415. $this->str .= $recursion ? "<ul class='$ul_class'><li $floder_status id= 'menu-item-$id'>" : "<li $floder_status id= 'menu-item-$id'>";
  416. $recursion = FALSE;
  417. //判断是否为终极栏目
  418. if ($this->getChild($a['id'])) {
  419. eval("\$nstr = \"$str2\";");
  420. $this->str .= $nstr;
  421. if ($showlevel == 0 || ($showlevel > 0 && $showlevel > $currentlevel)) {
  422. $this->getTreeViewMenu($a['id'], $effected_id, $str, $str2, $showlevel, $ul_class, $li_class, $style, $currentlevel + 1, TRUE);
  423. } elseif ($showlevel > 0 && $showlevel == $currentlevel) {
  424. //$this->str .= $placeholder;
  425. }
  426. } else {
  427. eval("\$nstr = \"$str\";");
  428. $this->str .= $nstr;
  429. }
  430. $this->str .= $recursion ? '</li></ul>' : '</li>';
  431. }
  432. if (!$recursion)
  433. $this->str .= '</ul>';
  434. return $this->str;
  435. }
  436. /**
  437. * 获取子栏目json
  438. * Enter description here ...
  439. * @param unknown_type $myId
  440. */
  441. public function createSubJson($myId, $str = '')
  442. {
  443. $sub_cats = $this->getChild($myId);
  444. $n = 0;
  445. if (is_array($sub_cats))
  446. foreach ($sub_cats as $c) {
  447. $data[$n]['id'] = iconv(CHARSET, 'utf-8', $c['catid']);
  448. if ($this->getChild($c['catid'])) {
  449. $data[$n]['liclass'] = 'hasChildren';
  450. $data[$n]['children'] = [['text' => '&nbsp;', 'classes' => 'placeholder']];
  451. $data[$n]['classes'] = 'folder';
  452. $data[$n]['text'] = iconv(CHARSET, 'utf-8', $c['catname']);
  453. } else {
  454. if ($str) {
  455. @extract(array_iconv($c, CHARSET, 'utf-8'));
  456. eval("\$data[$n]['text'] = \"$str\";");
  457. } else {
  458. $data[$n]['text'] = iconv(CHARSET, 'utf-8', $c['catname']);
  459. }
  460. }
  461. $n++;
  462. }
  463. return json_encode($data);
  464. }
  465. private function have($list, $item)
  466. {
  467. return (strpos(',,' . $list . ',', ',' . $item . ','));
  468. }
  469. }