Walker

A class for displaying various tree-like structures.

Defined (1)

The class is defined in the following location(s).

/wp-includes/class-wp-walker.php  
  1. class Walker { 
  2. /** 
  3. * What the class handles. 
  4. * @since 2.1.0 
  5. * @access public 
  6. * @var string 
  7. */ 
  8. public $tree_type; 
  9.  
  10. /** 
  11. * DB fields to use. 
  12. * @since 2.1.0 
  13. * @var array 
  14. */ 
  15. public $db_fields; 
  16.  
  17. /** 
  18. * Max number of pages walked by the paged walker 
  19. * @since 2.7.0 
  20. * @var int 
  21. */ 
  22. public $max_pages = 1; 
  23.  
  24. /** 
  25. * Whether the current element has children or not. 
  26. * To be used in start_el(). 
  27. * @since 4.0.0 
  28. * @var bool 
  29. */ 
  30. public $has_children; 
  31.  
  32. /** 
  33. * Starts the list before the elements are added. 
  34. * The $args parameter holds additional values that may be used with the child 
  35. * class methods. This method is called at the start of the output list. 
  36. * @since 2.1.0 
  37. * @abstract 
  38. * @param string $output Passed by reference. Used to append additional content. 
  39. * @param int $depth Depth of the item. 
  40. * @param array $args An array of additional arguments. 
  41. */ 
  42. public function start_lvl( &$output, $depth = 0, $args = array() ) {} 
  43.  
  44. /** 
  45. * Ends the list of after the elements are added. 
  46. * The $args parameter holds additional values that may be used with the child 
  47. * class methods. This method finishes the list at the end of output of the elements. 
  48. * @since 2.1.0 
  49. * @abstract 
  50. * @param string $output Passed by reference. Used to append additional content. 
  51. * @param int $depth Depth of the item. 
  52. * @param array $args An array of additional arguments. 
  53. */ 
  54. public function end_lvl( &$output, $depth = 0, $args = array() ) {} 
  55.  
  56. /** 
  57. * Start the element output. 
  58. * The $args parameter holds additional values that may be used with the child 
  59. * class methods. Includes the element output also. 
  60. * @since 2.1.0 
  61. * @abstract 
  62. * @param string $output Passed by reference. Used to append additional content. 
  63. * @param object $object The data object. 
  64. * @param int $depth Depth of the item. 
  65. * @param array $args An array of additional arguments. 
  66. * @param int $current_object_id ID of the current item. 
  67. */ 
  68. public function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {} 
  69.  
  70. /** 
  71. * Ends the element output, if needed. 
  72. * The $args parameter holds additional values that may be used with the child class methods. 
  73. * @since 2.1.0 
  74. * @abstract 
  75. * @param string $output Passed by reference. Used to append additional content. 
  76. * @param object $object The data object. 
  77. * @param int $depth Depth of the item. 
  78. * @param array $args An array of additional arguments. 
  79. */ 
  80. public function end_el( &$output, $object, $depth = 0, $args = array() ) {} 
  81.  
  82. /** 
  83. * Traverse elements to create list from elements. 
  84. * Display one element if the element doesn't have any children otherwise,  
  85. * display the element and its children. Will only traverse up to the max 
  86. * depth and no ignore elements under that depth. It is possible to set the 
  87. * max depth to include all depths, see walk() method. 
  88. * This method should not be called directly, use the walk() method instead. 
  89. * @since 2.5.0 
  90. * @param object $element Data object. 
  91. * @param array $children_elements List of elements to continue traversing. 
  92. * @param int $max_depth Max depth to traverse. 
  93. * @param int $depth Depth of current element. 
  94. * @param array $args An array of arguments. 
  95. * @param string $output Passed by reference. Used to append additional content. 
  96. */ 
  97. public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) { 
  98. if ( ! $element ) { 
  99. return; 
  100.  
  101. $id_field = $this->db_fields['id']; 
  102. $id = $element->$id_field; 
  103.  
  104. //display this element 
  105. $this->has_children = ! empty( $children_elements[ $id ] ); 
  106. if ( isset( $args[0] ) && is_array( $args[0] ) ) { 
  107. $args[0]['has_children'] = $this->has_children; // Back-compat. 
  108.  
  109. $cb_args = array_merge( array(&$output, $element, $depth), $args); 
  110. call_user_func_array(array($this, 'start_el'), $cb_args); 
  111.  
  112. // descend only when the depth is right and there are childrens for this element 
  113. if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) { 
  114.  
  115. foreach ( $children_elements[ $id ] as $child ) { 
  116.  
  117. if ( !isset($newlevel) ) { 
  118. $newlevel = true; 
  119. //start the child delimiter 
  120. $cb_args = array_merge( array(&$output, $depth), $args); 
  121. call_user_func_array(array($this, 'start_lvl'), $cb_args); 
  122. $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output ); 
  123. unset( $children_elements[ $id ] ); 
  124.  
  125. if ( isset($newlevel) && $newlevel ) { 
  126. //end the child delimiter 
  127. $cb_args = array_merge( array(&$output, $depth), $args); 
  128. call_user_func_array(array($this, 'end_lvl'), $cb_args); 
  129.  
  130. //end this element 
  131. $cb_args = array_merge( array(&$output, $element, $depth), $args); 
  132. call_user_func_array(array($this, 'end_el'), $cb_args); 
  133.  
  134. /** 
  135. * Display array of elements hierarchically. 
  136. * Does not assume any existing order of elements. 
  137. * $max_depth = -1 means flatly display every element. 
  138. * $max_depth = 0 means display all levels. 
  139. * $max_depth > 0 specifies the number of display levels. 
  140. * @since 2.1.0 
  141. * @param array $elements An array of elements. 
  142. * @param int $max_depth The maximum hierarchical depth. 
  143. * @return string The hierarchical item output. 
  144. */ 
  145. public function walk( $elements, $max_depth ) { 
  146. $args = array_slice(func_get_args(), 2); 
  147. $output = ''; 
  148.  
  149. //invalid parameter or nothing to walk 
  150. if ( $max_depth < -1 || empty( $elements ) ) { 
  151. return $output; 
  152.  
  153. $parent_field = $this->db_fields['parent']; 
  154.  
  155. // flat display 
  156. if ( -1 == $max_depth ) { 
  157. $empty_array = array(); 
  158. foreach ( $elements as $e ) 
  159. $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 
  160. return $output; 
  161.  
  162. /** 
  163. * Need to display in hierarchical order. 
  164. * Separate elements into two buckets: top level and children elements. 
  165. * Children_elements is two dimensional array, eg. 
  166. * Children_elements[10][] contains all sub-elements whose parent is 10. 
  167. */ 
  168. $top_level_elements = array(); 
  169. $children_elements = array(); 
  170. foreach ( $elements as $e) { 
  171. if ( empty( $e->$parent_field ) ) 
  172. $top_level_elements[] = $e; 
  173. else 
  174. $children_elements[ $e->$parent_field ][] = $e; 
  175.  
  176. /** 
  177. * When none of the elements is top level. 
  178. * Assume the first one must be root of the sub elements. 
  179. */ 
  180. if ( empty($top_level_elements) ) { 
  181.  
  182. $first = array_slice( $elements, 0, 1 ); 
  183. $root = $first[0]; 
  184.  
  185. $top_level_elements = array(); 
  186. $children_elements = array(); 
  187. foreach ( $elements as $e) { 
  188. if ( $root->$parent_field == $e->$parent_field ) 
  189. $top_level_elements[] = $e; 
  190. else 
  191. $children_elements[ $e->$parent_field ][] = $e; 
  192.  
  193. foreach ( $top_level_elements as $e ) 
  194. $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 
  195.  
  196. /** 
  197. * If we are displaying all levels, and remaining children_elements is not empty,  
  198. * then we got orphans, which should be displayed regardless. 
  199. */ 
  200. if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) { 
  201. $empty_array = array(); 
  202. foreach ( $children_elements as $orphans ) 
  203. foreach ( $orphans as $op ) 
  204. $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 
  205.  
  206. return $output; 
  207.  
  208. /** 
  209. * paged_walk() - produce a page of nested elements 
  210. * Given an array of hierarchical elements, the maximum depth, a specific page number,  
  211. * and number of elements per page, this function first determines all top level root elements 
  212. * belonging to that page, then lists them and all of their children in hierarchical order. 
  213. * $max_depth = 0 means display all levels. 
  214. * $max_depth > 0 specifies the number of display levels. 
  215. * @since 2.7.0 
  216. * @param array $elements 
  217. * @param int $max_depth The maximum hierarchical depth. 
  218. * @param int $page_num The specific page number, beginning with 1. 
  219. * @param int $per_page 
  220. * @return string XHTML of the specified page of elements 
  221. */ 
  222. public function paged_walk( $elements, $max_depth, $page_num, $per_page ) { 
  223. if ( empty( $elements ) || $max_depth < -1 ) { 
  224. return ''; 
  225.  
  226. $args = array_slice( func_get_args(), 4 ); 
  227. $output = ''; 
  228.  
  229. $parent_field = $this->db_fields['parent']; 
  230.  
  231. $count = -1; 
  232. if ( -1 == $max_depth ) 
  233. $total_top = count( $elements ); 
  234. if ( $page_num < 1 || $per_page < 0 ) { 
  235. // No paging 
  236. $paging = false; 
  237. $start = 0; 
  238. if ( -1 == $max_depth ) 
  239. $end = $total_top; 
  240. $this->max_pages = 1; 
  241. } else { 
  242. $paging = true; 
  243. $start = ( (int)$page_num - 1 ) * (int)$per_page; 
  244. $end = $start + $per_page; 
  245. if ( -1 == $max_depth ) 
  246. $this->max_pages = ceil($total_top / $per_page); 
  247.  
  248. // flat display 
  249. if ( -1 == $max_depth ) { 
  250. if ( !empty($args[0]['reverse_top_level']) ) { 
  251. $elements = array_reverse( $elements ); 
  252. $oldstart = $start; 
  253. $start = $total_top - $end; 
  254. $end = $total_top - $oldstart; 
  255.  
  256. $empty_array = array(); 
  257. foreach ( $elements as $e ) { 
  258. $count++; 
  259. if ( $count < $start ) 
  260. continue; 
  261. if ( $count >= $end ) 
  262. break; 
  263. $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 
  264. return $output; 
  265.  
  266. /** 
  267. * Separate elements into two buckets: top level and children elements. 
  268. * Children_elements is two dimensional array, e.g. 
  269. * $children_elements[10][] contains all sub-elements whose parent is 10. 
  270. */ 
  271. $top_level_elements = array(); 
  272. $children_elements = array(); 
  273. foreach ( $elements as $e) { 
  274. if ( 0 == $e->$parent_field ) 
  275. $top_level_elements[] = $e; 
  276. else 
  277. $children_elements[ $e->$parent_field ][] = $e; 
  278.  
  279. $total_top = count( $top_level_elements ); 
  280. if ( $paging ) 
  281. $this->max_pages = ceil($total_top / $per_page); 
  282. else 
  283. $end = $total_top; 
  284.  
  285. if ( !empty($args[0]['reverse_top_level']) ) { 
  286. $top_level_elements = array_reverse( $top_level_elements ); 
  287. $oldstart = $start; 
  288. $start = $total_top - $end; 
  289. $end = $total_top - $oldstart; 
  290. if ( !empty($args[0]['reverse_children']) ) { 
  291. foreach ( $children_elements as $parent => $children ) 
  292. $children_elements[$parent] = array_reverse( $children ); 
  293.  
  294. foreach ( $top_level_elements as $e ) { 
  295. $count++; 
  296.  
  297. // For the last page, need to unset earlier children in order to keep track of orphans. 
  298. if ( $end >= $total_top && $count < $start ) 
  299. $this->unset_children( $e, $children_elements ); 
  300.  
  301. if ( $count < $start ) 
  302. continue; 
  303.  
  304. if ( $count >= $end ) 
  305. break; 
  306.  
  307. $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 
  308.  
  309. if ( $end >= $total_top && count( $children_elements ) > 0 ) { 
  310. $empty_array = array(); 
  311. foreach ( $children_elements as $orphans ) 
  312. foreach ( $orphans as $op ) 
  313. $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 
  314.  
  315. return $output; 
  316.  
  317. /** 
  318. * Calculates the total number of root elements. 
  319. * @since 2.7.0 
  320. * @access public 
  321. * @param array $elements Elements to list. 
  322. * @return int Number of root elements. 
  323. */ 
  324. public function get_number_of_root_elements( $elements ) { 
  325. $num = 0; 
  326. $parent_field = $this->db_fields['parent']; 
  327.  
  328. foreach ( $elements as $e) { 
  329. if ( 0 == $e->$parent_field ) 
  330. $num++; 
  331. return $num; 
  332.  
  333. /** 
  334. * Unset all the children for a given top level element. 
  335. * @param object $e 
  336. * @param array $children_elements 
  337. */ 
  338. public function unset_children( $e, &$children_elements ) { 
  339. if ( ! $e || ! $children_elements ) { 
  340. return; 
  341.  
  342. $id_field = $this->db_fields['id']; 
  343. $id = $e->$id_field; 
  344.  
  345. if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) ) 
  346. foreach ( (array) $children_elements[$id] as $child ) 
  347. $this->unset_children( $child, $children_elements ); 
  348.  
  349. unset( $children_elements[ $id ] ); 
  350.  
  351. } // Walker