I received an excellent comment by ‘Chris Rogers’ on my earlier post Implementation of a Tree Structure in PHP. He commented to the fact that the iterator does not work if the child was created prior to the parent. Excellent catch!
To that end I have modified the class, specifically the iterator’s ‘createNode’ method which takes care of this fact. The final code can be found on Github: tree-php
/** * JTreeRecursiveIterator * * To use a recursive iterator you have to extend of the RecursiveIteratorIterator * As an example I have built an unordered list * For detailed information on please see RecursiveIteratorIterator * http://us.php.net/manual/en/class.recursiveiteratoriterator.php * * @package JTree * @author Jayesh Wadhwani * @copyright Jayesh Wadhwani * @license GNU GENERAL PUBLIC LICENSE 3.0 * @version 1.0 2011 */ class JTreeRecursiveIterator extends RecursiveIteratorIterator { /** * @var _jTree the JTree object */ private $_jTree; /** * @var _str string with ul/li string */ private $_str; /** * JTreeRecursiveIterator::__construct() * * @param mixed $jt - the tree object * @param mixed $iterator - the tree iterator * @param mixed $mode * @param integer $flags * @return */ public function __construct(JTree $jt, $iterator, $mode = RecursiveIteratorIterator::LEAVES_ONLY, $flags = 0) { parent::__construct($iterator, $mode, $flags); $this->_jTree = $jt; $this->_str = "<ul>\n"; } /** * JTreeRecursiveIterator::endChildren() * Called when end recursing one level.(See manual) * @return void */ public function endChildren() { parent::endChildren(); $this->_str .= "</ul></li>\n"; } /** * JTreeRecursiveIterator::callHasChildren() * Called for each element to test whether it has children. (See Manual) * * @return mixed */ public function callHasChildren() { $ret = parent::callHasChildren(); $value = $this->current()->getValue(); if($ret === true) { $this->_str .= "<li>{$value}<ul>\n"; } else { $this->_str .= "<li>{$value}</li>\n"; } return $ret; } /** * JTreeRecursiveIterator::__destruct() * On destruction end the list and display. * @return void */ public function __destruct() { $this->_str .= "</ul>\n"; echo $this->_str; } }
Now if you were to re-arrange the array like so where I have made ‘Fires'(id=5) as a child of Hurricanes(id=9)
$categories = array(); $categories[] = array('id' => 1, 'weather_condition' => 'weather', 'parent_id' => 9999); $categories[] = array('id' => 2, 'weather_condition' => 'Earthquakes', 'parent_id' => 1); $categories[] = array('id' => 3, 'weather_condition' => 'Major', 'parent_id' => 2); $categories[] = array('id' => 4, 'weather_condition' => 'Minor', 'parent_id' => 2); $categories[] = array('id' => 5, 'weather_condition' => 'Fires', 'parent_id' => 9); $categories[] = array('id' => 6, 'weather_condition' => 'Rain', 'parent_id' => 1); $categories[] = array('id' => 7, 'weather_condition' => 'Flooding', 'parent_id' => 6); $categories[] = array('id' => 8, 'weather_condition' => 'Washout', 'parent_id' => 6); $categories[] = array('id' => 9, 'weather_condition' => 'Hurricanes', 'parent_id' => 1);
And run it
//create a new tree object $jt = new JTree(); //iterate building the tree foreach($categories as $category) { $uid = $jt->createNode($category['weather_condition'],$category['id'], $category['parent_id']); } //update: removed third variable. Use defaults $it = new JTreeRecursiveIterator($jt, new JTreeIterator($jt->getTree())); //iterate to create the ul list foreach($it as $k => $v) {}
Note that I have removed addChild. It is included in createNode.
The result being:
- weather
- Earthquakes
- Major
- Minor
- Rain
- Flooding
- Washout
- Hurricanes
- Fires
- Earthquakes
Many thanks to Chris for pointing this out.
Happy computing!