Tree Helper 1
A helper to generate nested ULs OLs, DIVs or whatever from tree data.
Works best with the TreeBehavior, but not a requirement.
Works best with the TreeBehavior, but not a requirement.
Example usage 1:
Download code
<?php
//controller code:
$stuff = $this->MyTreeBehaviorModel->find('all',
array('fields' => array('whatever', 'lft', 'rght'), 'order' => 'lft ASC'));
$this->set('stuff', $stuff);
//view code
echo $tree->generate($stuff);
Example usage 2:
Download code
<?php
//controller code:
$stuff = $this->NoTreeBehaviorModel->findAllThreaded();
$this->set('stuff', $stuff);
//view code
echo $tree->generate($stuff);
Example usage 3:
Download code
<?php
//controller code:
$id = 1;
$showMeChildren = true;
$stuff = $this->MyTreeBehaviorModel->children($id, $showMeChildren);
$this->set('stuff', $stuff);
//view code
echo $tree->generate($stuff);
Example usage 4:
Download code
<?php
//controller code:
$stuff = $this->MainModel->MyTreeBehaviorModel->find('all',
array('fields' => array('whatever', 'lft', 'rght'), 'order' => 'lft ASC'));
$this->set('stuff', $stuff);
//view code
echo $tree->generate($stuff, array('model' => 'MyTreeBehaviorModel'));
Example usage 5:
Download code
<?php
//controller code:
$stuff = $this->MyTreeBehaviorModel->find('all',
array('fields' => array('whatever', 'lft', 'rght'), 'order' => 'lft ASC'));
$this->set('stuff', $stuff);
//view code
echo $tree->generate($stuff, array('type' => 'ol')); // generate an ol not a ul
Example usage 6:
Download code
<?php
//controller code:
$stuff = $this->MyTreeBehaviorModel->find('all',
array('fields' => array('whatever', 'lft', 'rght'), 'order' => 'lft ASC'));
//view code
// pass the data for each node to /views/elements/thisone.ctp and put the results in the rendered UL
echo $tree->generate($stuff, array('element' => 'thisone'));
The contents of "thisone.ctp" can be as simple or as complex as you like. e.g.:Download code
<?php
extract ($data);
echo $html->link($myModel['name'], array('action' => 'view', $myModel['id']));
or *new* as of revision 84 you can also do the following:Download code
<?php
extract ($data);
if (some logic) {
$tree->addItemAttribute('class', 'highlight'); // highlight this li
} else {
$tree->addTypeAttribute('style', 'display', 'none'); // hide this ul completely
}
echo $html->link($myModel['name'], array('action' => 'view', $myModel['id']));
Helper Class:
Download code
<?php
/* SVN FILE: $Id: tree.php 205 2008-08-13 14:13:32Z ad7six $ */
/**
* Tree Helper.
*
* Used the generate nested representations of hierarchial data
*
* PHP versions 4 and 5
*
* Copyright (c) 2008, Andy Dawson
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2008, Andy Dawson
* @link www.ad7six.com
* @package cake-base
* @subpackage cake-base.app.views.helpers
* @since v 1.0
* @version $Revision: 205 $
* @modifiedBy $LastChangedBy: ad7six $
* @lastModified $Date: 2008-08-13 16:13:32 +0200 (Wed, 13 Aug 2008) $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Tree helper
*
* Helper to generate tree representations of MPTT or recursively nested data
*/
class TreeHelper extends AppHelper {
/**
* name property
*
* @var string 'Tree'
* @access public
*/
var $name = 'Tree';
/**
* settings property
*
* @var array
* @access private
*/
var $__settings = array();
/**
* typeAttributes property
*
* @var array
* @access private
*/
var $__typeAttributes = array();
/**
* typeAttributesNext property
*
* @var array
* @access private
*/
var $__typeAttributesNext = array();
/**
* itemAttributes property
*
* @var array
* @access private
*/
var $__itemAttributes = array();
/**
* helpers variable
*
* @var array
* @access public
*/
var $helpers = array ('Html');
/**
* Tree generation method.
*
* Accepts the results of
* find('all', array('fields' => array('lft', 'rght', 'whatever'), 'order' => 'lft ASC'));
* children(); // if you have the tree behavior of course!
* or findAllThreaded(); and generates a tree structure of the data.
*
* Settings (2nd parameter):
* 'model' => name of the model (key) to look for in the data array. defaults to the first model for the current
* controller. If set to false 2d arrays will be allowed/expected.
* 'alias' => the array key to output for a simple ul (not used if element or callback is specified)
* 'type' => type of output defaults to ul
* 'itemType => type of item output default to li
* 'id' => id for top level 'type'
* 'class' => class for top level 'type'
* 'element' => path to an element to render to get node contents.
* 'callback' => callback to use to get node contents. e.g. array(&$anObject, 'methodName') or 'floatingMethod'
* 'autoPath' => array($left, $right [$classToAdd = 'active']) if set any item in the path will have the class $classToAdd added. MPTT only.
* 'left' => name of the 'lft' field if not lft. only applies to MPTT data
* 'right' => name of the 'rght' field if not lft. only applies to MPTT data
* 'depth' => used internally when running recursively, can be used to override the depth in either mode.
* 'firstChild' => used internally when running recursively.
* 'splitDepth' => if multiple "parallel" types are required, instead of one big type, nominate the depth to do so here
* example: useful if you have 30 items to display, and you'd prefer they appeared in the source as 3 lists of 10 to be able to
* style/float them.
* 'splitCount' => the number of "parallel" types. defaults to 3
*
* @param array $data data to loop on
* @param array $settings
* @return string html representation of the passed data
* @access public
*/
function generate ($data, $settings = array ()) {
$this->__settings = array_merge(array(
'model' => null,
'alias' => 'name',
'type' => 'ul',
'itemType' => 'li',
'id' => false,
'class' => false,
'element' => false,
'callback' => false,
'autoPath' => false,
'left' => 'lft',
'right' => 'rght',
'depth' => 0,
'firstChild' => true,
'indent' => null,
'splitDepth' => false,
'splitCount' => 3,
), (array)$settings);
if ($this->__settings['autoPath'] && !isset($this->__settings['autoPath'][2])) {
$this->__settings['autoPath'][2] = 'active';
}
extract($this->__settings);
if ($indent === null && Configure::read()) {
$indent = true;
}
$view =& ClassRegistry:: getObject('view');
if ($model === null) {
$model = Inflector::classify($view->params['models'][0]);
}
if (!$model) {
$model = '_NULL_';
}
$stack = array();
if ($depth == 0) {
if ($class) {
$this->addTypeAttribute('class', $class, null, 'previous');
}
if ($id) {
$this->addTypeAttribute('id', $id, null, 'previous');
}
}
$return = '';
if ($indent) {
$return = "\r\n";
}
$__addType = true;
foreach ($data as $i => $result) {
/* Allow 2d data arrays */
if ($model == '_NULL_') {
$_result = $result;
$result[$model] = $_result;
}
/* BulletProof */
if (!isset($result[$model][$left]) && !isset($result['children'])) {
$result['children'] = array();
}
/* Close open items as appropriate */
while ($stack && ($stack[count($stack)-1] < $result[$model][$right])) {
array_pop($stack);
if ($indent) {
$whiteSpace = str_repeat("\t",count($stack));
$return .= "\r\n" . $whiteSpace . "\t";
}
if ($type) {
$return .= '</' . $type . '>';
}
if ($itemType) {
$return .= '</' . $itemType . '>';
}
}
/* Some useful vars */
$hasChildren = $firstChild = $lastChild = $hasVisibleChildren = false;
$numberOfDirectChildren = $numberOfTotalChildren = null;
if (isset($result['children'])) {
if ($result['children']) {
$hasChildren = $hasVisibleChildren = true;
$numberOfDirectChildren = count($result['children']);
}
$prevRow = prev($data);
if (!$prevRow) {
$firstChild = true;
}
next($data);
$nextRow = next($data);
if (!$nextRow) {
$lastChild = true;
}
prev($data);
} elseif (isset($result[$model][$left])) {
if ($result[$model][$left] != ($result[$model][$right] - 1)) {
$hasChildren = true;
$numberOfTotalChildren = ($result[$model][$right] - $result[$model][$left] - 1) / 2;
if (isset($data[$i + 1]) && $data[$i + 1][$model][$right] < $result[$model][$right]) {
$hasVisibleChildren = true;
}
}
if (!isset($data[$i - 1]) || ($data[$i - 1][$model][$left] == ($result[$model][$left] - 1))) {
$firstChild = true;
}
if (!isset($data[$i + 1]) || ($stack && $stack[count($stack) - 1] == ($result[$model][$right] + 1))) {
$lastChild = true;
}
}
$elementData = array(
'data' => $result,
'depth' => $depth?$depth:count($stack),
'hasChildren' => $hasChildren,
'numberOfDirectChildren' => $numberOfDirectChildren,
'numberOfTotalChildren' => $numberOfTotalChildren,
'firstChild' => $firstChild,
'lastChild' => $lastChild,
'hasVisibleChildren' => $hasVisibleChildren
);
$this->__settings = array_merge($this->__settings, $elementData);
/* Main Content */
if ($element) {
$content = $view->element($element,$elementData);
} elseif ($callback) {
list($content) = array_map($callback, array($elementData));
} else {
$content = $result[$model][$alias];
}
if (!$content) {
continue;
}
$whiteSpace = str_repeat("\t", $depth);
if ($indent && strpos($content, "\r\n", 1)) {
$content = str_replace("\r\n", "\n" . $whiteSpace . "\t", $content);
}
/* Prefix */
if ($__addType) {
if ($indent) {
$return .= "\r\n" . $whiteSpace;
}
if ($type) {
$typeAttributes = $this->__attributes($type, array('data' => $elementData));
$return .= '<' . $type . $typeAttributes . '>';
}
}
if ($indent) {
$return .= "\r\n" . $whiteSpace . "\t";
}
if ($itemType) {
$itemAttributes = $this->__attributes($itemType, $elementData);
$return .= '<' . $itemType . $itemAttributes . '>';
}
$return .= $content;
/* Suffix */
$__addType = false;
if ($hasVisibleChildren) {
if ($numberOfDirectChildren) {
$settings['depth'] = $depth + 1;
$return .= $this->__suffix();
$return .= $this->generate($result['children'], $settings);
if ($itemType) {
$return .= '</' . $itemType . '>';
}
} elseif ($numberOfTotalChildren) {
$__addType = true;
$stack[] = $result[$model][$right];
}
} else {
if ($itemType) {
$return .= '</' . $itemType . '>';
}
$return .= $this->__suffix();
}
}
/* Cleanup */
while ($stack) {
array_pop($stack);
if ($indent) {
$whiteSpace = str_repeat("\t",count($stack));
$return .= "\r\n" . $whiteSpace . "\t";
}
if ($type) {
$return .= '</' . $type . '>';
}
if ($itemType) {
$return .= '</' . $itemType . '>';
}
}
if ($indent) {
$return .= "\r\n";
}
if ($type) {
$return .= '</' . $type . '>';
if ($indent) {
$return .= "\r\n";
}
}
return $return;
}
/**
* addItemAttribute function
*
* Called to modify the attributes of the next <item> to be processed
* Note that the content of a 'node' is processed before generating its wrapping <item> tag
*
* @param string $id
* @param string $key
* @param mixed $value
* @access public
* @return void
*/
function addItemAttribute($id = '', $key = '', $value = null) {
if (!is_null($value)) {
$this->__itemAttributes[$id][$key] = $value;
} elseif (!(isset($this->__itemAttributes[$id]) && in_array($key, $this->__itemAttributes[$id]))) {
$this->__itemAttributes[$id][] = $key;
}
}
/**
* addTypeAttribute function
*
* Called to modify the attributes of the next <type> to be processed
* Note that the content of a 'node' is processed before generating its wrapping <type> tag (if appropriate)
* An 'interesting' case is that of a first child with children. To generate the output
* <ul> (1)
* <li>XYZ (3)
* <ul> (2)
* <li>ABC...
* ...
* </ul>
* ...
* The processing order is indicated by the numbers in brackets.
* attributes are allways applied to the next type (2) to be generated
* to set properties of the holding type - pass 'previous' for the 4th param
* i.e.
* // Hide children (2)
* $tree->addTypeAttribute('style', 'display', 'hidden');
* // give top level type (1) a class
* $tree->addTypeAttribute('class', 'hasHiddenGrandChildren', null, 'previous');
*
* @param string $id
* @param string $key
* @param mixed $value
* @access public
* @return void
*/
function addTypeAttribute($id = '', $key = '', $value = null, $previousOrNext = 'next') {
$var = '__typeAttributes';
$firstChild = isset($this->__settings['firstChild'])?$this->__settings['firstChild']:true;
if ($previousOrNext == 'next' && $firstChild) {
$var = '__typeAttributesNext';
}
if (!is_null($value)) {
$this->{$var}[$id][$key] = $value;
} elseif (!(isset($this->{$var}[$id]) && in_array($key, $this->{$var}[$id]))) {
$this->{$var}[$id][] = $key;
}
}
/**
* supressChildren method
*
* @return void
* @access public
*/
function supressChildren() {
}
/**
* suffix method
*
* Used to close and reopen a ul/ol to allow easier listings
*
* @access private
* @return void
*/
function __suffix() {
static $__splitCount = 0;
static $__splitCounter = 0;
extract($this->__settings);
if ($splitDepth) {
if ($depth == $splitDepth -1) {
$total = $numberOfDirectChildren?$numberOfDirectChildren:$numberOfTotalChildren;
if ($total) {
$__splitCounter = 0;
$__splitCount = $total / $splitCount;
$rounded = (int)$__splitCount;
if ($rounded < $__splitCount) {
$__splitCount = $rounded + 1;
}
}
}
if ($depth == $splitDepth) {
$__splitCounter++;
if ($type && ($__splitCounter % $__splitCount) == 0) {
return '</' . $type . '><' . $type . '>';
}
}
}
return;
}
/**
* attributes function
*
* Logic to apply styles to tags.
*
* @param mixed $rType
* @param array $elementData
* @access private
* @return void
*/
function __attributes($rType, $elementData = array(), $clear = true) {
extract($this->__settings);
if ($rType == $type) {
$attributes = $this->__typeAttributes;
if ($clear) {
$this->__typeAttributes = $this->__typeAttributesNext;
$this->__typeAttributesNext = array();
}
} else {
$attributes = $this->__itemAttributes;
$this->__itemAttributes = array();
if ($clear) {
$this->__itemAttributes = array();
}
}
if ($autoPath && $depth) {
if ($this->__settings['data'][$model][$left] < $autoPath[0] && $this->__settings['data'][$model][$right] > $autoPath[1]) {
$attributes['class'][] = $autoPath[2];
} elseif (isset($autoPath[3]) && $this->__settings['data'][$model][$left] == $autoPath[0]) {
$attributes['class'][] = $autoPath[3];
}
}
if ($attributes) {
foreach ($attributes as $type => $values) {
foreach ($values as $key => $val) {
if (is_array($val)) {
$attributes[$type][$key] = '';
foreach ($val as $vKey => $v) {
$attributes[$type][$key][$vKey] .= $vKey . ':' . $v;
}
$attributes[$type][$key] = implode(';', $attributes[$type][$key]);
}
if (is_string($key)) {
$attributes[$type][$key] = $key . ':' . $val . ';';
}
}
$attributes[$type] = $type . '="' . implode(' ', $attributes[$type]) . '"';
}
return ' ' . implode(' ', $attributes);
}
return '';
}
}
?>
Variables available in your element
If you choose to nominate an element to be used to render the contents of each node, the following variables are automatically available inside the element:Download code
<?php
$data // the row of data passed to the helper
$depth // depth in the current tree 1 = first item
$hasChildren // whether the current row has children or not
$hasVisibleChildren // whether the current row has Visible children or not. Only relavent for MPTT tree data
$numberOfDirectChildren // only avaliable with recursive data
$numberOfTotalChildren // only available with MPTT tree data
$firstChild // whether the current row is the first of it's siblings or not
$lastChild // whether the current row is the last of it's siblings or not
Comments
Bug
1 Incorrect variable name line 91
Line 91 :
Controller Class:
<?php$numberOfChildren = $numberOfTotalChildren = 0;
?>
Should be :
Controller Class:
<?php$numberOfDirectChildren = $numberOfTotalChildren = 0;
?>
Also, don't forget to pass the name of the model if you use this helper globally (like a navigation menu in the default layout). I failed to remember that and made my Apache crash !
Comment
2 Corrected
Thanks for pointing that out - now corrected in the article.
Question
3 Remove empty UL elements
eg)
View Template:
<ul id="nestList" >
<li><a href="somelink">Windows</A>
<ul>
<li><a href="someOtherLink">File System</A>
<ul>
</ul>
</li>
</ul>
</li>
</ul>
I'm very new to Cake and not a very experienced programmer, having looked at the helper code i cannot see how to remove the empty UL's. I ask as i have some collapsible menu javascript code which does not work properly with the empty UL elements after all childless nodes. Im currently passing in data using findAllThreaded(), Any help would be greatly appreciated.
Question
4 urls
Comment
5 re urls
You need to create an element and place it in views/elements.
your tree generation in the view should look similar to this.
View Template:
echo $tree->generate($threadedlist, array('element'=>'elementName', 'model'=>'Category'));Then in the element, you simply echo whatever you would like to appear in the node, the $tree-generate makes an array variable called '$data' available to the element which the node element it is about to print to the screen. Check the helper code above, there is also a lot of other useful information in the $data array which can be used to make your output conditional or to tailor it to your needs. So my element looks similar to this
View Template:
$return = '<A HREF="/categories/view_cat/'.$data['Category']['id'].'">';
$return .= $data['Category'][$lang.'_description']."</A>";
I think you can use the $html->link method in the element also but i chose this way. remember, the element is called for every node in the list so you only need to echo the url or whatever information for that single line in the list.
Hope this helps
Question
6 first child and last child
Comment
7 Found a solution
I added this to add firstChild and lastChild classes to html elements for all depths/levels allowing for greater CSS styling flexibility.
if (!isset($data[$i - 1]) || ($data[$i - 1][$model][$left] == ($result[$model][$right] - 1)) || $data[$i - 1][$model][$left] + 1 == $result[$model][$left]) {
$firstChild = true;
}
if ($stack && $stack[count($stack) - 1] == ($result[$model][$right] + 1) || !isset($data[$i + 1])) {
$lastChild = true;
}
Question
8 Possible Enhancement and Question
An enhancement (that I don't have the skills to implement myself and I'm sure could be added easily by those with skills):
1) User selectable class name for each li ... maybe one of the vars in $data. User provides $liclass = 'Model.field' as a setting. Can be usefull for styling/css control purposes.
Also a question:
I might be reading the code wrong but I can't find out how to 'turn off' the output of the ul/ol and li's ie to control the output manually using the element setting. Any hints?
Bug
9 A small typo
Am I wrong or there is a typo (a $lft instead of $left) ?
if (!isset($data[$i - 1]) || ($data[$i - 1][$model][$left] == ($result[$model][$lft] - 1))) {
Comment
10 Tree Helper updates v1 70
Re Empty ULs
That shouldn't be a problem now
Re Urls
Use an element - it's the easiest solution if you are not just wanting an inanimate list of names. I wouldn't advocate hand writing your html links though ;)
Re First child and last child
The internal logic of the behavior now relies on the 'helpful vars' so these variables should always be accurate now (I await being proven wrong).
Re Possible enhancments
This still isn't possible but please take a look at the _attributes method. At the moment it is necessary to edit the helper to put your logic in there to set attribute values - your patches on how to structure the code so it's not necessary are welcome.
Re turning off uls/lis
Assuming the purpose of doing so is to be able to put the ul/li inside your element - you can't. if you tried to do so you'd end up with an invalid tree as soon as you rendered an element that had a child - your patches on how to prove me wrong are welcome.
Re typos
probably ;)
Comment
11 Updated. Rev 89
Bug
12 Trees that dont have name fields
For me I had a title instead of name. When I used this:
<?php echo $tree->generate($pages); ?>
I get Notice (8): Undefined index: name [APP/views/helpers/tree.php, line 210]
But after you pass an alias in the options array you get lovely output. I am now using
<?php echo $tree->generate($pages, array('alias' => 'title')); ?>
Great helper, keep up the good work!
Sincerely,
~Andrew Allen
Comment
13 A little change
Your check of last/first child don't work. I don't know why :(
Here is some changes:
/*$prevRow = prev($data);
if (!$prevRow) {
$firstChild = true;
}
next($data);
$nextRow = next($data);
if (!$nextRow) {
$lastChild = true;
}
prev($data);*/
if (!isset($data[$i - 1])) {
$firstChild = true;
}
if (!isset($data[$i + 1])) {
$lastChild = true;
}
Comment
14 Bug Example usage 4
echo $tree->generate($stuff, array('Model' => 'MyTreeBehaviorModel'));Should be:echo $tree->generate($stuff, array('model' => 'MyTreeBehaviorModel'));Notice the options-array-key (model) should lowercase! Toke me a while before noticing :). (In the future I should read the code (comments) before using a helper/component...)Bug
15 a possible bug when using in error templates
I have a layout, that includes $tree->generate() function. But when I try to use this layout for error messages I cannot access any helpers in my menu element (the one that is rendered for each menu item).
I guess it must be something with variable context because in the template called on error "$this" in the element refers to a "View" class and not to the Tree helper itself.
Let me know if you need more info on this.
regards
danielz
Comment
16 methods in element (example) have no effect
I am using the helper and everything looks great. I use the tree-behavior to get childs and generate the tree. Until here everything works!
When I try to use the methods: addTypeAttribute and/or addItemAttribute in the element (exactly like example 6). Nothing happens...
It's not really clear why it won't work. Could use some help please, been pulling my last hairs for the last couple of hours.
I am using: 1.2.0.7692 RC3
Thanks in advanced!
Adriaan
Question
17 Element requests action, view requests element (for each node)
In the view
echo $this->element('user_menu');
In the element (user_menu)
echo $this->requestAction('users/user_menu', array('return'));
In the view (users/user_menu)
echo $tree->generate($data, array('model' => 'UserMenu', 'element' => 'user_menu_node'));
And, finally!, in the element (user_menu_node)
... some logic ...
$tree->addItemAttribute('class', $data['UserMenu']['class']);
... some more ...
This setup generates an error (undefined variable tree), have i inadvertently skipped a step?
Comment
18 Making tree available to the node element
(tree.php line ~211)
$elementData = array(
'tree' => $this,
'data' => $result,
'depth' => $depth?$depth:count($stack),
...
);
Comment
19 methods in element (example) have no effect
-> problem solved, i've used this wrong code in my view:
App::import('Helper', 'Tree');
$tree = new TreeHelper();
Comment
20 Generic Element View
View Template:
// view myModel
echo $tree->generate($stuff, array('element' => 'actions_tree','model' => 'myModel'));
View Template:
// element actions_tree.ctp
$model = array_keys($data);
echo $html->link($data[$model[0]]['nome'], array('action' => 'view', $data[$model[0]]['id']));
Andy, thanks for your helper! :)
Bug
21 Using autoAuth without default model
Controller Class:
<?phpif ($model === null) {
$model = Inflector::classify($view->params['models'][0]);
}
?>
Should be
Controller Class:
<?phpif ($model === null) {
$model = Inflector::classify($view->params['models'][0]);
$this->__settings['model'] = $model;
}
?>
Otherwise a bug occur at line 428 : Notice (8): Undefined index: [APP/views/helpers/tree.php, line 428]
Comment
22 __attributes function question
$this->__itemAttributes = array();
if ($clear) {
$this->__itemAttributes = array();
}
This seems to be pointless
if ($clear) {
$this->__itemAttributes = array();
}
is rendered useless by the line just before it
$this->__itemAttributes = array();
Also, what is the purpose of the $clear attribute? It is not listed in the function description, nor do any of the calls to it from the class use it.
Am I missing something?
Comment
23 Use a database field value as an id or class attribute
To do this I created 2 new settings, elementIdField and elementClassField.
$this->__settings = array_merge(array(
'model' => null,
...
'elementIdField' => false,
'elementClassField' => false,
), (array)$settings);
These can be set to a database field name that defines the id or class name.
$tree->generate($test_for_layout, array('class'=>'nav', 'elementClassField'=>'class', 'element'=>'menu/menu', 'model'=>'Menu'));
I added the following code to the __attributes function that would then generate the id/class names. I added this just before the line "if ($autoPath && $depth) {"
function __attributes($rType, $elementData = array(), $clear = true) {
...[begin added code]...
if ($elementIdField && $depth && $rType == $itemType) {
$attributes['id'][] = $this->__settings['data'][$model][$elementIdField];
}
if ($elementClassField && $depth && $rType == $itemType) {
$attributes['class'][] = $this->__settings['data'][$model][$elementClassField];
}
}
Here is a sample output:
<ul class="nav">
<li>Items
<ul>
<li class="parent">List items
<ul>
<li class="parent">By location
<ul>
<li class="link">For my location</li>
<li class="parent">locations A - K
<ul>
<li class="tablelist">item list</li>
Let me know if this is useful.
Bug
24 $firstChild asn $lastChild fix
FIX:
lines 153-154:
$__addType = true;
foreach ($data as $i => $result) {
change to:
$__addType = true;
$iteration = 0;
$iterations = count($data);
foreach ($data as $i => $result) {
AND
lines 188-197:
$prevRow = prev($data);
if (!$prevRow) {
$firstChild = true;
}
next($data);
$nextRow = next($data);
if (!$nextRow) {
$lastChild = true;
}
prev($data);
change to:
/*$prevRow = prev($data);
if (!$prevRow) {
$firstChild = true;
}
next($data);
$nextRow = next($data);
if (!$nextRow) {
$lastChild = true;
}
prev($data);*/
$firstChild = $iteration == 0;
$lastChild = $iteration == $iterations - 1;
AND
lines 278-281:
}
}
/* Cleanup */
change to:
}
++$iteration;
}
/* Cleanup */
Question
25 Can the treenode add a link?