MultiTree Behavior
MultiTree is a drop-in behaviour to CakePHP's Core Tree Behavior allowing for more advanced operations and better performance on large data sets
This article assumes you have read and understood http://book.cakephp.org/view/228/Basic-Usage
Note: This would also be the config for drop in's from the core Tree Behaviour
left: lft
right: rght
root: root_id
level: level
Download code
Get parent based on Left/Right values
Download code
Get direct children only:
Download code
Get siblings including the node itself
Download code
Get previous siblings including the node itself
Download code
Get next siblings including the node itself
Download code
Download code
Insert new node as the next sibling of node 4
Download code
Not setting a parent_id or nulling it out will insert the node as a top level (root) node
Download code
Move node 9 up by 2 (if possible, otherwise move as high up as possible)
Download code
Move node 9 down by 3 (if possible, otherwise move as low down as possible)
Download code
Will make node 6 a new top level (root) node
Download code
This will delete node 25 and all its children
Download code
This will delete node 25 itself but if it has any children shift them one level up
Download code
Download code
parent_id's are broken but we have valid left and right values
Download code
Public Repository
http://github.com/cyberthom/MultiTree-Behavior (fork away!)Advantages
- Support for root_id (This will vastly increase speed for write operations on large data sets - this is because not the whole tree has to be rewritten when updating a node but only those rows with the same root id)
- Support for level caching
- Easier moving of nodes (MultiTree supports full move() to any id as opposed to Core Tree's moveUp and moveDown)
- More getter functions (easily retrieve siblings, children, parents etc.)
Caution
Use InnoDB (or a different engine that supports transactions, otherwise you have to LOCK tables manually during operations to prevent corrupted data in multi user environments)Configuration
Example 1
The following config is meant for large trees that are often updated as well a retrieved. It keeps track of a tree that has root_id's and level caching enabled. It is ideal for e.g. Comment TreesModel Class:
Download code
<?php class Comment extends AppModel {
var $name = 'Comment';
var $actsAs = array(
'MultiTree' => array(
'root' =>'root_id',
'level' =>'level'
)
);
}?>
Schema
Download code
CREATE TABLE `comments` (
`id` int(10) unsigned NOT NULL auto_increment,
`title` varchar(128) NOT NULL default '',
`body` text NOT NULL,
`created` datetime default NULL,
`modified` datetime default NULL,
`parent_id` int(10) unsigned default NULL,
`root_id` int(10) unsigned default NULL,
`lft` mediumint(8) unsigned default NULL,
`rght` mediumint(8) unsigned default NULL,
`level` mediumint(8) unsigned default NULL,
PRIMARY KEY (`id`),
KEY `rght` USING BTREE (`root_id`,`rght`,`lft`),
KEY `lft` USING BTREE (`root_id`,`lft`,`rght`),
KEY `parent_id` USING BTREE (`parent_id`,`sticky`,`created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Example 2
This following config is meant for small trees that are mainly retrieved and not often updated. It keeps track of a tree without root_id's and level caching disabled. It is ideal for e.g. Category TreesNote: This would also be the config for drop in's from the core Tree Behaviour
Model Class:
Download code
<?php class Category extends AppModel {
var $name = 'Comment';
var $actsAs = array(
'MultiTree' => array(
'root' => false,
'level' => false
)
);
}?>
Schema
Download code
CREATE TABLE `categories` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(128) NOT NULL default '',
`parent_id` int(10) unsigned default NULL,
`lft` mediumint(6) unsigned default NULL,
`rght` mediumint(6) unsigned default NULL,
PRIMARY KEY (`id`),
KEY `lft` USING BTREE (`lft`),
KEY `parent_id` USING BTREE (`parent_id`),
KEY `rght` USING BTREE (`rght`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Config defaults
parent: parent_idleft: lft
right: rght
root: root_id
level: level
Traversing the tree
Get parent
Get parent based on ParentDownload code
debug($this->Category->getParent(32));
Get parent based on Left/Right values
Download code
debug($this->Category->getParentFromTree(32));
Get path
Download code
debug($this->Category->getPath(32));
Get level
Download code
debug($this->Category->getLevel(32));
Get children
Download code
debug($this->Category->getChildren(32));
Get direct children only:
Download code
debug($this->Category->getChildren(32, true));
Get child count
Download code
debug($this->Category->getChildCount(32));
Get siblings
Download code
debug($this->Category->getSiblings(32));
Get siblings including the node itself
Download code
debug($this->Category->getSiblings(32, true));
Get previous siblings
Download code
debug($this->Category->getPrevSiblings(32));
Get previous siblings including the node itself
Download code
debug($this->Category->getPrevSiblings(32, true));
Get next siblings
Download code
debug($this->Category->getNextSiblings(32));
Get next siblings including the node itself
Download code
debug($this->Category->getNextSiblings(32, true));
Get previous sibling
Download code
debug($this->Category->getPrevSibling(32));
Get next sibling
Download code
debug($this->Category->getNextSibling(32));
Insert
Insert new node as the last child of node 1Download code
$format = array(
'name' => 'Cat',
'parent_id' => 1
);
$this->Category->save($format);
Insert new node as the next sibling of node 4
Download code
$format = array(
'name' => 'Lion',
'parent_id' => array('destination' => 4, 'position' => 'nextSibling')
);
$this->Category->save($format);
Not setting a parent_id or nulling it out will insert the node as a top level (root) node
Download code
$format = array(
'name' => 'Animal',
'parent_id' => null
);
$this->Category->save($format);
Move
Download code
$this->Category->move(6, 12, 'firstChild'); // Move node 6 to be the first child of node 12
$this->Category->move(6, 12, 'lastChild'); // Move node 6 to be the last child of node 12
$this->Category->move(6, 12, 'prevSibling'); // You get the idea..
$this->Category->move(6, 12, 'nextSibling');
Move node 9 up by 2 (if possible, otherwise move as high up as possible)
Download code
$this->Category->moveUp(9, 2);
Move node 9 down by 3 (if possible, otherwise move as low down as possible)
Download code
$this->Category->moveDown(9, 3);
Will make node 6 a new top level (root) node
Download code
$this->Category->move(6, null);
Delete
Download code
$this->Category->delete(25); // Same as removeFromTree(25)
This will delete node 25 and all its children
Download code
$this->Category->removeFromTree(25);
This will delete node 25 itself but if it has any children shift them one level up
Download code
$this->Category->removeFromTree(25, false);
Repair
left and right values are broken but we have valid parent_id'sDownload code
$this->Category->repair('tree');
parent_id's are broken but we have valid left and right values
Download code
$this->Category->repair('parent');
Comments
Comment
1 thank you
Comment
2 thx
Comment
3 github
I've added a public repository in case someone finds any bugs and wants to fix them or add any features: http://github.com/cyberthom/MultiTree-Behavior (fork away!)
Comment
4 moving of nodes
> opposed to Core Tree's moveUp and moveDown)
note that you can very easily do this with cake's treebehavior also.
See http://book.cakephp.org/view/521/Modifying-data
Comment
5 exact positioning
good point - however MultiTree allows you to specify the _exact_ position:
$format = array(
'name' => 'Lion',
'parent_id' => array('destination' => 4, 'position' => 'nextSibling')
);
$this->Category->save($format);
Question
6 Different trees
Can this plugin handle different trees in one table?
Question
7 Different trees
Do I still need http://bakery.cakephp.org/articles/view/scopedtreebehavior additionally to handle different trees in one table?
Thanks!
Comment
8 Re: Different trees
No, the whole point of this behavior is to store multiple trees in one table - they are separated by a root_id
Comment
9 repair resets values to 0
When we run the repair function all the values for lft, rght, root_id and level of most tree entries are reset to 0.
Any idea whre to start looking for the error?
Thanks!
Aldo
Question
10 Reordering Parents
Reordering the parents (parent_id=NULL) with moveUp and moveDown does not work. Both functions return false. Reordering of any >1 level childs work as normal. Do you have any hints on this issue?
Thanks,
Frank
Comment
11 Please Help
var $actsAs = array(
'MultiTree' => array(
'root' => 'root_id',
'parent' => 'parent_id',
'level' => 'level',
),
'Revision',
'Logable',
);
function CreateChild( $parentID )
{
$newID = false;
$numChildren = $this->getChildCount( $parentID );
$parent = $this->GetBaseInfo( $parentID );
$this->id = false;
$data = array(
'root_id' => $parent['TaskTemplate']['root_id'],
'parent_id' => $parentID,
'title' => 'New Task-' . $parentID . '-' . ($numChildren + 1),
'description' => 'Please enter a description or this task.',
'priority' => $parent['TaskTemplate']['priority'],
'required' => $parent['TaskTemplate']['required'],
'deleted' => 0,
);
if ( $this->save( $data ) )
{
#debug( $data );
$newID = $this->id;
}
return $newID;
}
I noticed in the behavior you remove the parent and root fields. Why is this? I appreciate any help you can offer.
Comment
12 repair resets values to 0
Comment
13 hi
Designer Resources, Php Tutorial
Comment
14 thanks
Thanks,
Porno izle
Comment
15 thanks
Comment
16 thanks
Comment
17 ed
Comment
18 lounge chair
lounge chair