Tree.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php namespace App\Servers\Data;
  2. /**
  3. * 树形栏目
  4. *
  5. */
  6. class Tree {
  7. /**
  8. * 返回多层栏目
  9. *
  10. * @param $data 操作的数组
  11. * @param int $pid 一级PID的值
  12. * @param string $html 栏目名称前缀
  13. * @param string $fieldPri 唯一键名,如果是表则是表的主键
  14. * @param string $fieldPid 父ID键名
  15. * @param int $level 不需要传参数(执行时调用)
  16. *
  17. * @return array
  18. */
  19. public function channelLevel( $data, $pid = 0, $html = "&nbsp;", $fieldPri = 'cid', $fieldPid = 'pid', $level = 1 ) {
  20. if ( empty( $data ) ) {
  21. return [ ];
  22. }
  23. $arr = [ ];
  24. foreach ( $data as $v ) {
  25. if ( $v[ $fieldPid ] == $pid ) {
  26. $arr[ $v[ $fieldPri ] ] = $v;
  27. $arr[ $v[ $fieldPri ] ]['_level'] = $level;
  28. $arr[ $v[ $fieldPri ] ]['_html'] = str_repeat( $html, $level - 1 );
  29. $arr[ $v[ $fieldPri ] ]["_data"] = $this->channelLevel( $data, $v[ $fieldPri ], $html, $fieldPri, $fieldPid, $level + 1 );
  30. }
  31. }
  32. return $arr;
  33. }
  34. /**
  35. * 获得所有子栏目
  36. *
  37. * @param $data 栏目数据
  38. * @param int $pid 操作的栏目
  39. * @param string $html 栏目名前字符
  40. * @param string $fieldPri 表主键
  41. * @param string $fieldPid 父id
  42. * @param int $level 等级
  43. *
  44. * @return array
  45. */
  46. public static function channelList( $data, $pid = 0, $html = "&nbsp;", $fieldPri = 'cid', $fieldPid = 'pid', $level = 1 ) {
  47. $data = Tree::_channelList( $data, $pid, $html, $fieldPri, $fieldPid, $level );
  48. if ( empty( $data ) ) {
  49. return $data;
  50. }
  51. foreach ( $data as $n => $m ) {
  52. if ( $m['_level'] == 1 ) {
  53. continue;
  54. }
  55. $data[ $n ]['_first'] = FALSE;
  56. $data[ $n ]['_end'] = FALSE;
  57. if ( ! isset( $data[ $n - 1 ] ) || $data[ $n - 1 ]['_level'] != $m['_level'] ) {
  58. $data[ $n ]['_first'] = TRUE;
  59. }
  60. if ( isset( $data[ $n + 1 ] ) && $data[ $n ]['_level'] > $data[ $n + 1 ]['_level'] ) {
  61. $data[ $n ]['_end'] = TRUE;
  62. }
  63. }
  64. //更新key为栏目主键
  65. $category = [ ];
  66. foreach ( $data as $d ) {
  67. $category[ $d[ $fieldPri ] ] = $d;
  68. }
  69. return $category;
  70. }
  71. //只供channelList方法使用
  72. public static function _channelList( $data, $pid = 0, $html = "&nbsp;", $fieldPri = 'cid', $fieldPid = 'pid', $level = 1 ) {
  73. if ( empty( $data ) ) {
  74. return [ ];
  75. }
  76. $arr = [ ];
  77. foreach ( $data as $v ) {
  78. $id = $v[ $fieldPri ];
  79. if ( $v[ $fieldPid ] == $pid ) {
  80. $v['_level'] = $level;
  81. $v['_html'] = str_repeat( $html, $level - 1 );
  82. array_push( $arr, $v );
  83. $tmp = Tree::_channelList( $data, $id, $html, $fieldPri, $fieldPid, $level + 1 );
  84. $arr = array_merge( $arr, $tmp );
  85. }
  86. }
  87. return $arr;
  88. }
  89. /**
  90. * 获得树状数据
  91. *
  92. * @param $data 数据
  93. * @param $title 字段名
  94. * @param string $fieldPri 主键id
  95. * @param string $fieldPid 父id
  96. *
  97. * @return array
  98. */
  99. public static function tree( $data, $title, $fieldPri = 'cid', $fieldPid = 'pid' ) {
  100. if ( ! is_array( $data ) || empty( $data ) ) {
  101. return [ ];
  102. }
  103. $arr = Tree::channelList( $data, 0, '', $fieldPri, $fieldPid );
  104. foreach ( $arr as $k => $v ) {
  105. $str = "";
  106. if ( $v['_level'] > 2 ) {
  107. for ( $i = 1;$i < $v['_level'] - 1;$i ++ ) {
  108. $str .= "│&nbsp;&nbsp;&nbsp;&nbsp;";
  109. }
  110. }
  111. if ( $v['_level'] != 1 ) {
  112. $t = $title ? $v[ $title ] : '';
  113. if ( isset( $arr[ $k + 1 ] ) && $arr[ $k + 1 ]['_level'] >= $arr[ $k ]['_level'] ) {
  114. $arr[ $k ][ '_' . $title ] = $str . "&nbsp;&nbsp;├─ " . $v['_html'] . $t;
  115. } else {
  116. $arr[ $k ][ '_' . $title ] = $str . "&nbsp;&nbsp;└─ " . $v['_html'] . $t;
  117. }
  118. } else {
  119. $arr[ $k ][ '_' . $title ] = $v[ $title ];
  120. }
  121. }
  122. //设置主键为$fieldPri
  123. $data = [ ];
  124. foreach ( $arr as $d ) {
  125. // $data[$d[$fieldPri]] = $d;
  126. $data[] = $d;
  127. }
  128. return $data;
  129. }
  130. /**
  131. * 获得所有父级栏目
  132. *
  133. * @param $data 栏目数据
  134. * @param $sid 子栏目
  135. * @param string $fieldPri 唯一键名,如果是表则是表的主键
  136. * @param string $fieldPid 父ID键名
  137. *
  138. * @return array
  139. */
  140. public function parentChannel( $data, $sid, $fieldPri = 'cid', $fieldPid = 'pid' ) {
  141. if ( empty( $data ) ) {
  142. return $data;
  143. } else {
  144. $arr = [ ];
  145. foreach ( $data as $v ) {
  146. if ( $v[ $fieldPri ] == $sid ) {
  147. $arr[] = $v;
  148. $_n = $this->parentChannel( $data, $v[ $fieldPid ], $fieldPri, $fieldPid );
  149. if ( ! empty( $_n ) ) {
  150. $arr = array_merge( $arr, $_n );
  151. }
  152. }
  153. }
  154. return $arr;
  155. }
  156. }
  157. /**
  158. * 判断$s_cid是否是$d_cid的子栏目
  159. *
  160. * @param $data 栏目数据
  161. * @param $sid 子栏目id
  162. * @param $pid 父栏目id
  163. * @param string $fieldPri 主键
  164. * @param string $fieldPid 父id字段
  165. *
  166. * @return bool
  167. */
  168. private function isChild( $data, $sid, $pid, $fieldPri = 'cid', $fieldPid = 'pid' ) {
  169. $_data = $this->channelList( $data, $pid, '', $fieldPri, $fieldPid );
  170. foreach ( $_data as $c ) {
  171. //目标栏目为源栏目的子栏目
  172. if ( $c[ $fieldPri ] == $sid ) {
  173. return TRUE;
  174. }
  175. }
  176. return FALSE;
  177. }
  178. /**
  179. * 检测是不否有子栏目
  180. *
  181. * @param $data 栏目数据
  182. * @param $cid 要判断的栏目cid
  183. * @param string $fieldPid 父id表字段名
  184. *
  185. * @return bool
  186. */
  187. private function hasChild( $data, $cid, $fieldPid = 'pid' ) {
  188. foreach ( $data as $d ) {
  189. if ( $d[ $fieldPid ] == $cid ) {
  190. return TRUE;
  191. }
  192. }
  193. return FALSE;
  194. }
  195. /**
  196. * 递归实现迪卡尔乘积
  197. *
  198. * @param $arr 操作的数组
  199. * @param array $tmp
  200. *
  201. * @return array
  202. */
  203. private function descarte( $arr, $tmp = [ ] ) {
  204. $n_arr = [ ];
  205. foreach ( array_shift( $arr ) as $v ) {
  206. $tmp[] = $v;
  207. if ( $arr ) {
  208. $this->descarte( $arr, $tmp );
  209. } else {
  210. $n_arr[] = $tmp;
  211. }
  212. array_pop( $tmp );
  213. }
  214. return $n_arr;
  215. }
  216. }