- Documentation
- Installing and configuring
- Selecting from a tree
- Modifying a tree
- Modifying a tree
- Useful code
- Change Log
- 1.05
- 1.04
- 1.03
- 1.02
- 1.01
- 1.00
- 0.99b
- 0.99
- 0.95
- 0.90
- 0.85
Note: Latest release and documentation are available from extension github page.
This extension allows managing trees stored in database as nested sets. It's implemented as Active Record behavior.
Implemented by creocoder.
Resources ¶
Documentation ¶
Installing and configuring ¶
First you need to configure model as follows:
public function behaviors()
{
return array(
'NestedSetBehavior'=>array(
'class'=>'ext.yiiext.behaviors.trees.NestedSetBehavior',
'leftAttribute'=>'lft',
'rightAttribute'=>'rgt',
'levelAttribute'=>'level',
);
}
There is no need to validate fields specified in leftAttribute
,
rightAttribute
, rootAttribute
and levelAttribute
options. Moreover,
there could be problems if there are validation rules for these. Please
check if there are no rules for fields mentioned in model's rules() method.
In case of storing a single tree per database, DB structure can be built with
extensions/yiiext/behaviors/trees/schema.sql
. If you're going to store multiple
trees you'll need extensions/yiiext/behaviors/trees/schema_many_roots.sql
.
By default leftAttribute
, rightAttribute
and levelAttribute
values are
matching filed names in default DB schemas so you can skip configuring these.
There are two ways this behavior can work: one tree per table and multiple trees
per table. The mode is selected based on the value of hasManyRoots
option that
is false
by default meaning single tree mode. In multiple trees mode you can
set rootAttribute
option to match existing filed in the table storing the tree.
Selecting from a tree ¶
In the following we'll use an example model Category
with the following in its
DB:
- 1. Mobile phones
- 2. iPhone
- 3. Samsung
- 4. X100
- 5. C200
- 6. Motorola
- 7. Cars
- 8. Audi
- 9. Ford
- 10. Mercedes
In this example we have two trees. Tree roots are ones with ID=1 and ID=7.
Getting all roots ¶
Using NestedSetBehavior::roots()
:
$roots=Category::model()->roots()->findAll();
Result:
Array of Active Record objects corresponding to Mobile phones and Cars nodes.
Getting all descendants of a node ¶
Using NestedSetBehavior::descendants()
:
$category=Category::model()->findByPk(1);
$descendants=$category->descendants()->findAll();
Result:
Array of Active Record objects corresponding to iPhone, Samsung, X100, C200 and Motorola.
Getting all children of a node ¶
Using NestedSetBehavior::children()
:
$category=Category::model()->findByPk(1);
$descendants=$category->children()->findAll();
Result:
Array of Active Record objects corresponding to iPhone, Samsung and Motorola.
Getting all ancestors of a node ¶
Using NestedSetBehavior::ancestors()
:
$category=Category::model()->findByPk(5);
$descendants=$category->ancestors()->findAll();
Result:
Array of Active Record objects corresponding to Samsung and Mobile phones.
Getting parent of a node ¶
Using NestedSetBehavior::getParent()
:
$category=Category::model()->findByPk(9);
$parent=$category->parent;
Result:
Array of Active Record objects corresponding to Cars.
Getting node siblings ¶
Using NestedSetBehavior::getPrevSibling()
or
NestedSetBehavior::getNextSibling()
:
$category=Category::model()->findByPk(9);
$nextSibling=$category->nextSibling;
Result:
Array of Active Record objects corresponding to Mercedes.
Getting the whole tree ¶
You can get the whole tree using standard AR methods like the following.
For single tree per table:
Category::model()->findAll(array('order'=>'lft'));
For multiple trees per table:
Category::model()->findAll(array('condition'=>'root_id=?','order'=>'lft'),array($root_id));
Modifying a tree ¶
In this section we'll build a tree like the one used in the previous section.
Creating root nodes ¶
You can create a root node using NestedSetBehavior::saveNode()
.
In a single tree per table mode you can create only one root node. If you'll attempt
to create more there will be CException thrown.
$root=new Root;
$root->title='Mobile Phones';
$root->saveNode();
$root=new Root;
$root->title='Cars';
$root->saveNode();
Result:
- 1. Mobile Phones
- 2. Cars
Adding child nodes ¶
There are multiple methods allowing you adding child nodes. To get more info about these refer to API. Let's use these to add nodes to the tree we have:
$category1=new Category;
$category1->title='Ford';
$category2=new Category;
$category2->title='Mercedes';
$category3=new Category;
$category3->title='Audi';
$root=Category::model()->findByPk(1);
$category1->appendTo($root);
$category2->insertAfter($category1);
$category3->insertBefore($category1);
Result:
- 1. Mobile phones
- 3. Audi
- 4. Ford
- 5. Mercedes
- 2. Cars
Logically the tree above doesn't looks correct. We'll fix it later.
$category1=new Category;
$category1->title='Samsung';
$category2=new Category;
$category2->title='Motorola';
$category3=new Category;
$category3->title='iPhone';
$root=Category::model()->findByPk(2);
$category1->appendTo($root);
$category2->insertAfter($category1);
$category3->prependTo($root);
Result:
- 1. Mobile phones
- 3. Audi
- 4. Ford
- 5. Mercedes
- 2. Cars
- 6. iPhone
- 7. Samsung
- 8. Motorola
$category1=new Category;
$category1->title='X100';
$category2=new Category;
$category2->title='C200';
$node=Category::model()->findByPk(3);
$category1->appendTo($node);
$category2->prependTo($node);
Result:
- 1. Mobile phones
- 3. Audi
- 9. С200
- 10. X100
- 4. Ford
- 5. Mercedes
- 2. Cars
- 6. iPhone
- 7. Samsung
- 8. Motorola
Modifying a tree ¶
In this section we'll finally make our tree logical.
Tree modification methods ¶
There are several methods allowing you to modify a tree. To get more info about these refer to API.
Let's start:
// move phones to the proper place
$x100=Category::model()->findByPk(10);
$c200=Category::model()->findByPk(9);
$samsung=Category::model()->findByPk(7);
$x100->moveAsFirst($samsung);
$c200->moveBefore($x100);
// now move all Samsung phones branch
$mobile_phones=Category::model()->findByPk(1);
$samsung->moveAsFirst($mobile_phones);
// move the rest of phone models
$iphone=Category::model()->findByPk(6);
$iphone->moveAsFirst($mobile_phones);
$motorola=Category::model()->findByPk(8);
$motorola->moveAfter($samsung);
// move car models to appropriate place
$cars=Category::model()->findByPk(2);
$audi=Category::model()->findByPk(3);
$ford=Category::model()->findByPk(4);
$mercedes=Category::model()->findByPk(5);
foreach(array($audi,$ford,$mercedes) as $category)
$category->moveAsLast($cars);
Result:
- 1. Mobile phones
- 6. iPhone
- 7. Samsung
- 10. X100
- 9. С200
- 8. Motorola
- 2. Cars
- 3. Audi
- 4. Ford
- 5. Mercedes
Moving a node making it a new root ¶
There is a special moveAsRoot()
method that allows moving a node and making it
a new root. All descendants are moved as well in this case.
Example:
$node=Category::model()->findByPk(10);
$node->moveAsRoot();
Identifying node type ¶
There are three methods to get node type: isRoot()
, isLeaf()
, isDescendantOf()
.
Example:
$root=Category::model()->findByPk(1);
CVarDumper::dump($root->isRoot()); //true;
CVarDumper::dump($root->isLeaf()); //false;
$node=Category::model()->findByPk(9);
CVarDumper::dump($node->isDescendantOf($root)); //true;
CVarDumper::dump($node->isRoot()); //false;
CVarDumper::dump($root->isLeaf()); //true;
$samsung=Category::model()->findByPk(7);
CVarDumper::dump($node->isDescendantOf($samsung)); //true;
Useful code ¶
Non-recursive tree traversal ¶
$level=0;
foreach($categories as $n=>$category)
{
if($category->level==$level)
echo CHtml::closeTag('li')."\n";
else if($category->level>$level)
echo CHtml::openTag('ul')."\n";
else
{
echo CHtml::closeTag('li')."\n";
for($i=$level-$model->level;$i;$i--)
{
echo CHtml::closeTag('ul')."\n";
echo CHtml::closeTag('li')."\n";
}
}
echo CHtml::openTag('li');
echo CHtml::encode($category->title);
$level=$category->level;
}
for($i=$level;$i;$i--)
{
echo CHtml::closeTag('li')."\n";
echo CHtml::closeTag('ul')."\n";
}
Change Log ¶
1.05 ¶
- External transaction support for NestedSetBehavior::delete() method (creocoder)
1.04 ¶
- Fix for $depth parameter of NestedSetBehavior::ancestors() method (creocoder)
1.03 ¶
- Class renamed to NestedSetBehavior (creocoder)
- Rare bug with cache correction after NestedSetBehavior::addNode() fixed (creocoder)
- Readmes non-recursive tree traversal algorithm fix (creocoder)
1.02 ¶
- Added moveAsRoot() method (creocoder)
- Updated readmes (Sam Dark)
1.01 ¶
- Added not integer pk support (creocoder)
- Remove
final
from class (creocoder)
1.00 ¶
- Some behavior refactoring before release (creocoder)
- Advanced doc added (creocoder)
0.99b ¶
- Allow to use multiply change tree operations (creocoder)
- Method saveNode() now create root in "single root mode" (creocoder)
- Unit tests refactored (creocoder)
- Some behavior refactoring (creocoder)
0.99 ¶
- Added note about removing fields from
rules
(Sam Dark) - Added Unit tests for single root mode (creocoder)
- All attributes are now correctly quoted (creocoder)
- Renamed fields: 'root'=>'rootAttribute', 'left'=>'leftAttribute', 'right'=>'rightAttribute', 'level'=>'levelAttribute' (creocoder)
- Renamed method parent() => getParent() (creocoder)
0.95 ¶
- Unit tests added (creocoder)
- Incorrect usage of save() and delete() instead of behavior's saveNode() and deleteNode() is now detected (creocoder)
0.90 ¶
- Moving a node to a different root is now supported (creocoder)
0.85 ¶
- Initial public release (creocoder)
great
good job ! Thanks !
excellent
a very needed extension ;)
Yes, it is
I use this extension in my comment's widget. Thanks, Sam!
How to set a node to become a root node?
example:
start:
root1 node 1.1 node 1.2 node 1.2.1 root2 node 2.1 node 2.1.1 node 2.1.2
end:
root1 node 1.1 node 1.2 node 1.2.1 root2 root3 (old 2.1) node 3.1 (old 2.1.1) node 3.1 (old 2.1.2)
How I can do that?
ancestors depth
In ancestors there is
$criteria->addCondition($alias.'.'.$this->level.'>='.($owner->{$this->level}+$depth));
if actual level is for example 5 and we want 1 ancestor
then
$model->ancestors(1)->find()
but condition is now level >= 6, I think it sholud be level >=4
so i changed to:
$criteria->addCondition($alias.'.'.$this->level.'>='.($owner->{$this->level}-$depth));
if someone need it - func convert flat result array to hierarchical
If you will use it also need to add childs var in model class.
I put this function in overloaded class
class ENestedSetBehavior2 extends ENestedSetBehavior { /** * Tree must be sorted by left key * @param array $tree */ public function hierarchical($tree = array()) { if(empty($tree)) return false; $tmp = array(); $parents = array(); foreach($tree as $k=>$node){ if($node->level == 1) { $tmp[$k] = $node; $parents[$node->level] = & $tmp[$k]->childs; } else { for($i = 1; $i < $node->level; $i++) $parent = & $parents[$i]; $parent[$k] = $node; $parents[$node->level] = & $parent[$k]->childs; } } return $tmp; } /** * Named scope * @param int depth * @param boolean $self - return self node * @return CActiveRecord the owner. */ public function descendants($depth=null, $self = false) { ...
Code example, in controller
$root = Category::model()->roots()->findByPk($id); $branch = $root->descendants(null, true)->findAll(); $tree = Category::model()->tree->hierarchical($branch); $this->render('tree', array( 'model' => $root, 'view' => $view, 'tree' => $tree, ));
and views
tree.php
<?php $this->renderPartial('_hierarchical', array('tree'=>$tree));?>
and _hierarchical.php
<?php if(!empty($tree)): ?> <ul> <?php foreach($tree as $node): ?> <li> <?php echo CHtml::link($node->title, array('viewNode', 'id'=>$node->id)); ?> <?php if(!empty($node->childs)) $this->renderPartial('_hierarchical', array('tree'=>$node->childs)); ?> </li> <?php endforeach; ?> </ul> <?php endif; ?>
this code will show somth like this
and thanks =)
Пасип, мужики, буду пользовать =)
parent_id
It would be nice if we could save the parent_id of the node along with lft/rgt/level. If you save the parent_id then the query to find the direct descendants (not to mention the parent) becomes trivial. It won't affect your other queries, but 2 of the most common (for me, anyway) - direct children and getting the parent - should see big speed increases on large tables. Or am I missing something? :)
default sort by lft
Can you add something like this :
[code]public function descendants($depth=null, $orderByLft=true)[/code]
order by lft is not always needed.
A complete save example with 'hasManyRoots' => false
Hi,
I am trying to implement this extension with a single root, but I can't find an example for saving nodes with it.
Could somebody please post a save snippet for this case?
Best regards,
Hint - Get the position of a node
Hi,
If you need to find the position of a node into its parent branch, you can use something like that in your model:
public function getPosition() { return ($this->lft - ($this->parent->lft+1))/2; }
then simply get the value by using $model->position
Very cool extension by the way ;)
Regards
How track event change node?, move, create, update, delete?
How track event change node, moved, created, updated, deleted?
Event onAfterSave not work! I need delete cache if changed structur of tree. And it is I have to do in model.
Thanks :) !
want to know does it can apply to different ActiveRecord class?
in a project there may have many models , which have the tree struct . and if this
extension can apply to multiple ar or can only to one specific ar . if attach to many different ar , does it need to modify the category table schema , may be add some field like : cate_type varchar(20) ..
or i misunderstand the usage , i will modify the ar's table add the table field come from the category .
the tree struct can added to a exist table(which in first version may be plain ) ,let's say : menu(id,text,url).
some time later , we need the menu to support tree struct , there are two choices :
1.add some another fields to the menu table ,like pid, level etc..;
2.- use another table such as node to maintain the relation :node(id ,type, id_type,type_pid,type_level)
the choice 2 may need do a lots things when perform the crud operations. for query
may use the left join or inner join . and create , update , delete become even complex .
so if i use this extension in my project , i have to modify the existing ar's table.
Several trees in the same table, is it possible?
First of all, great extension.
Now I need to extend its functionality in order to keep several trees in the same table. Not one tree with several roots, but several trees within the same table. I would need this "profile_id" filter to paint one tree or another.
I have a table "profile_component" with fields "id", "left", "right" and "level" to manage the nested set behavior. But it also has a field "profile_id", so that the records with profile_id=0 represent one tree, the records with profile_id=1 represent a second tree, and so on.
Does anybody know how should I extend the component so that I can get one tree or another by setting this "profile_id" attribute?
I´m using also JsTree to render the tree and EJNestedTreeActions to deal with the tree actions.
Thanks in advance
Fantastic work!
Beautiful work!
One note, this is technically not a nested set, but an adjacency list - this type of tree implementation is optimized for fast reading, but writes are more expensive than a nested set. It also had the advantage (over a nested set) of maintaining sibling order.
Personally I prefer adjacency lists - and this is an awesome implementation, queries neatly packaged as scopes, operations fully wrapped in transactions, fully unit tested, etc. - very, very nice work!
Thanks!
Transaction Checking while deleting node
This is really a great extension.
Please notice that while performing the moveNode action, the checking is being done to find out if a txn has already been set (i.e., if there was a beginTransaction() issued by some controller). And only if it has not been set, the extension tries to set one by itself.
But while deleting a Node, the same is not being done. I feel that this checking could have been done in the delete method also. In my implementation, i have enabled this checking and it is working fine, as of now.
jqGrid Tree with Nested Sets using multiple roots
I just implemented jqGrid Tree view using Nested Sets with multiple nodes. It took me sometime to work out the logic, so i thought i could share my code, if some other beginner like me needs to check.
Using Nested Sets in multiple roots mode requires the use of the 'adjacency' jqGrid tree Model. Atleast that is what i understand.
The view file details:
<?php Yii::app()->clientScript->registerCoreScript('jquery.ui'); Yii::app()->clientScript->registerCssFile(Yii::app()->baseUrl . '/css/ui-lightness/jquery-ui-1.8.13.custom.css'); Yii::app()->clientScript->registerCssFile(Yii::app()->baseUrl . '/css/ui.jqgrid.css'); Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl . '/js/jqGrid/i18n/grid.locale-en.js', CClientScript::POS_HEAD); Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl . '/js/jqGrid/jquery.jqGrid.min.js', CClientScript::POS_HEAD); ?> <table id="list"></table> <div id="pager"></div> <?php $script = ' jQuery("#list").jqGrid( { treeGrid: true, treeGridModel: "adjacency", ExpandColumn: "name", rowNum: -1, url: "' . $this->createUrl("fetchTree") . '", datatype: "json", treedatatype: "json", treeIcons: {plus:"ui-icon-plus",minus:"ui-icon-minus",leaf:"ui-icon-cancel"}, mtype: "POST", ExpandColClick: true, colNames:["id", "Group Name"], colModel : [ {name:"id", index:"id", width:1, hidden: true, key: true}, {name:"name", index:"name", width:300, sortable: false} ], height: "auto", caption: "View Groups" }); '; Yii::app()->clientScript->registerScript('jqGrid',$script); ?>
The actionFetchTree method:
public function actionFetchTree() { $ADDWHERE = ''; if (isset ($_REQUEST["nodeid"])) $node = (integer)$_REQUEST["nodeid"]; else $node = 0; // detect if here we post the data from allready loaded tree // we can make here other checks if( $node >0) { //in jqGrid the level starts from 0, but in nested sets with multiple roots the level starts from 1 $n_lvl = (integer)$_REQUEST["n_level"] + 1; $parent = Yii::app()->db ->createCommand("Select id, root, lft, rgt from tbl_tree WHERE id = $node") ->queryRow(); $ADDWHERE = " root = " . $parent["root"] . " AND level = " . ($n_lvl+1) . " AND lft > " . $parent["lft"] . " AND rgt < " . $parent["rgt"]; } else { // initial grid $n_lvl =0; $ADDWHERE = " level = 1 "; } $SQL1 = "SELECT id, root, name, level-1 as lvl, lft, rgt, is_ledger from tbl_tree WHERE $ADDWHERE order by root, lft"; header("Content-type: text/html;charset=utf-8"); $response->page = 1; $response->total = 1; $cmd = Yii::app()->db->createCommand($SQL1); $result = $cmd->queryAll(); $i=0; foreach ($result as $row) { if ($row['is_ledger'] == true) $leaf = 'true';else $leaf='false'; if ($row['lvl'] == 0) $root = NULL; else $root = $parent['id']; $l = (integer)$row['lvl']; if( $n_lvl == $l) { $response->rows[$i]['cell']=array ( $row['id'], $row['name'], $n_lvl, $root, $leaf, 'false' ); $i++; } } echo json_encode($response); }
The explanation is given in the jqGrid wiki site where the usage of Adjacency Model is described. The only point to remember here is that while sending the children nodes we need to send the immediate parent id, and not the root id for that tree. So, in the beginning of the code, we try to obtain the root details through a queryRow() call
glad to see the example about how to use this fantastic extension.
the usage example is very helpful , also we can check it out from svn , in the unit test directory one can find more usage!
how about developing an extension that bridge the nestedset model and the adjacency model , we can translate one model easily to another . there are some code pieces in the internet converting the nestedset to adjacency model . you need create both table and config the mapping for two table fields( because some table may be exist earlier and may have different field name ) ,often converting take place in midnight ,it will cost a lot of time .
How to build a CTreeView
A very useful extension, thank you so much for sharing!
Could you please help me get this clear — how exactly can I display the whole tree via CTreeView widget? What is the right way to get the whole tree, when three has many roots?
Nested Set Behavior Administration GUI with jsTree
Hi,I have just uploaded an extension for Nested Set Behavior Administration with jsTree plugin.This is the Link:
Nested Set Behavior Administration GUI with jsTree
pagination with nestedset
when a top level root has too many children , if want to use the pagination .
how to ? i noticed this method :
public function descendants($depth=null, $self = false)
too many children means the tree may be too deep or too wide . both situation need consider how to apply pagination , any idea ?
use it with CMenu
hi, thanks for your great job
is there a best solution to use it with CMenu?
Multiple Users
Hi there,
I'd like to attach a user_id field to this table and have 1 tree per user. Do you think this plugin will work in this way?
Thanks for the great work,
Marco.
Memory problem when using a large number of models instances
The Problem
I have noticed a bit of a problem with it if you use it for exports or similar tasks where you are traversing through the whole database getting data and pushing it out to a file.
The extension has an internal cache stored in a static variable. This can be disastrous since this means that the memory of the model you just used will be stored in memory and because of that is not released when you loop through to the next element.
I was traversing through about 100000 items and after adding a call to a model that uses the nested set behaviour my export script started running out of memory.
I am using a call like this in a loop:
$ancestor = $this->ancestors(1)->find();
If you look in the afterFind Method in the nested set behaviour it does this:
$owner=$this->getOwner(); self::$_cached[get_class($owner)][$this->_id=self::$_c++]=$owner;
This stores the object in a static array. Quite quickly this will fill the memory up. This problem is probably only ever going to show up in the rare event of you traversing through a large number of nodes and I can see that this is the wanted behaviour in most cases.
The Solution
Make sure to destruct the model manually after you have used it.
Like so:
The nested set behaviour has a destructor that removes the object from the static cache.
unset(self::$_cached[get_class($this->getOwner())][$this->_id]);
Suggestion
Add possibility to turn caching off in the behaviour.
Something like:
public function behaviors() { return array( 'NestedSetBehavior'=>array( 'class'=>'application.components.behaviors.trees.NestedSetBehavior', 'leftAttribute'=>'left', 'rightAttribute'=>'right', 'levelAttribute'=>'level', 'hasManyRoots'=>true, 'cache'=>false )); }
Looks useful
Could benefit from using of Postgresql's ltree data type where available:
http://www.postgresql.org/docs/9.1/static/ltree.html
Can't access on github
Would love to be able to use this extension but can't access it on the link provided to github.
Getting Fatal error: Cannot use object of type stdClass as array in /var/lib/jenkins/workspace/yiiext.github.com/app/components/YiiextGithubApi.php on line 72
github page is fixed
@andrew1 just fixed the error.
JOIN with Categories
Great extension. I got it work! But I have a question:
If I have a tbl_presentation and categories extension configured for these presentations. How can I get all the the categories (roots for example) for those presentations that have certain condition? (ie: tbl_presentation.status = 'active') ?
Recursive traversal how to do?
Can you give a recursive traversal of useful code?
Wrong data structure visualization in this example
Just look closer to the "Selecting from tree" section and "Getting all children of a node" example.
Example data structure:
- 2. iPhone - 3. Samsung - 4. X100 - 5. C200 - 6. Motorola
but CORRECT example data structure shoud be:
- 2. iPhone - 3. Samsung - 4. X100 - 5. C200 - 6. Motorola
Please fix the extension description and the readme file on github.
BTW - this is a great extension anyway - thank you!
La documentacion está muy pobre
debido a que no tenemos demasiada informacion aqui, es necesario ver el código directamente, pues es el lugar donde mencionan los parametros adicionales al momento de crear nodos sin realizar la validación.
here
$root = new Menu(); // $rolft = 1; // $root->rgt = 2; // $root->level = 1; $root->text = 'root'; $root->saveNode( false );
and
$help = new Menu(); $help->text = 'Help'; $help->enabled = true; $appended = $help->appendTo( $root, false );
leftAttribute, rightAttribute, Level for what ?
i'am confused what is purpose of leftAttribute, rightAttribute, and LevelAttribute
would not these scheme below already cover it all.
left and right attribute
@Lukito This isn't my extension but I do use it so maybe I can help.
It looks from your question that you might perhaps be confusing the nested set model with another tree model - perhaps adjacency list - though not sure where position fits in adjacency list?
I suggest googling nested sets. Nested set models need a left and right attribute - otherwise it wouldn't be a nested set - and without checking am guessing the variable names you named probably relate to the naming of these attributes within the database table used to store the tree - i.e. guessing extension author is providing convenient way to allow you to name those attributes in the database however suits.
Once you have set up your table this extension handles the display and manipulation of the tree fantastically and if you need the ability to graphically display and re-order items then it is awesome. I haven't seen a yii extension for doing this with the adjacency list model - though haven't looked for a while. Nested sets are clever - though not as clever as a closure table - and are superior to adjacency lists for some applications by avoiding the necessity for recursive procedures e.g. to obtain a list of grandchildren for example is only one query - effectively selecting the elements between left and right at the desired level (for grandchildren second level down). To achieve that with adjacency list would first require to select elements having a parent_id of the grandparent and then iterating over these children similarly looking for their descendants.
nested set
@andrew1 really thanks for explanation
before, i never heard about tree algorithm such as nested set, adjacency list, closure table
overall awesome for this extension.
thanks.
Non-recursive tree traversal (in this example is a bug that incorrectly displays the tree)
$level=0; foreach($categories as $n=>$category) { if($category->level==$level) echo CHtml::closeTag('li')."\n"; else if($category->level>$level) echo CHtml::openTag('ul')."\n"; else { echo CHtml::closeTag('li')."\n"; //you write for($i=$level-$model->level;$i;$i--) but we don't have $model, we have $category for($i=$level-$category->level;$i;$i--) { echo CHtml::closeTag('ul')."\n"; echo CHtml::closeTag('li')."\n"; } } echo CHtml::openTag('li',array('data-level'=>$category->level)); echo CHtml::encode($category->title); $level=$category->level; } for($i=$level;$i;$i--) { echo CHtml::closeTag('li')."\n"; echo CHtml::closeTag('ul')."\n"; }
Please correct this in the example, because many do not read the comments but simply copies the code that displays the tree is not correctly!
Movement of Multiple-Roots
I don't know if I'm doing something wrong,
but it seems that movement (ordering) Root Nodes is not possible?
(when multiple Roots are enabled?)
Would be nice if things like:
$rootNode=NestedCategory::model()->findByPk(1); $nextRootNode=$rootNode->next()->find(); $rootNode->moveAfter($nextRootNode);
Would support multiple Roots.
Any tips how to do that?
Or is it somehow possible to use a single-root and make that root "invisible"? ...
Root node sorting support In many root mode
I add some code based to the nestedsetbehavior version at https://gist.github.com/max107/10723289. And now, it support root node sorting.
Move node 7 before node 1, move node 2 before node 1 (see to the first example tree mentioned before). (Here I want to post an example tree, but the final display format is crashed. )
Large text in tree data array
This extension is fantastic, but how can I add a lot of html in text field? Not only entry from DB...?
Posted question in StackOwerflow
Large text in tree data array
This extension is fantastic, but how can I add a lot of html in text field (like render a view)? Not only entry from DB...?
Posted detailed question in StackOwerflow
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.