MultiTree Behavior

By Thom (cyberthom)
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

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 Trees

Model 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 Trees
Note: 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_id
left: lft
right: rght
root: root_id
level: level


Traversing the tree

Get parent

Get parent based on Parent
Download 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 1
Download 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's
Download code $this->Category->repair('tree');
parent_id's are broken but we have valid left and right values
Download code $this->Category->repair('parent');

Page 2: Download

Comments 1400

CakePHP Team Comments Author Comments
 

Comment

1 thank you

very very very tnx
Posted Jan 26, 2010 by Saleh souzanchi
 

Comment

2 thx

Thanks for your help.
Posted Jan 27, 2010 by Toyosawa Yasutaka
 

Comment

3 github

You're welcome guys - Hope it helps!

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!)
Posted Jan 27, 2010 by Thom
 

Comment

4 moving of nodes

> Easier moving of nodes (MultiTree supports full move() to any id as
> 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
Posted Feb 1, 2010 by Dieter Plaetinck
 

Comment

5 exact positioning

> Easier moving of nodes (MultiTree supports full move() to any id as
> 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

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);
Posted Feb 1, 2010 by Thom
 

Question

6 Different trees

Hello,

Can this plugin handle different trees in one table?
Posted Mar 25, 2010 by Aldo
 

Question

7 Different trees

Nobody?

Do I still need http://bakery.cakephp.org/articles/view/scopedtreebehavior additionally to handle different trees in one table?

Thanks!
Posted Mar 31, 2010 by Aldo
 

Comment

8 Re: Different trees

Do I still need http://bakery.cakephp.org/articles/view/scopedtreebehavior additionally to handle different trees in one table?
No, the whole point of this behavior is to store multiple trees in one table - they are separated by a root_id
Posted Mar 31, 2010 by Thom
 

Comment

9 repair resets values to 0

Hello,

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
Posted Apr 16, 2010 by Aldo
 

Question

10 Reordering Parents

Hi

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
Posted May 8, 2010 by Frank
 

Comment

11 Please Help

I'm not sure if this project is still active, but it looks very interesting. In a current project I am in need of tracking multiple trees in a single table. I have tried to use this behavior without success. I have created 2 root nodes using the behavior. Later, when I try to add children a node is created but the parent, root, lft, adn rght fields are all null. Am I doing something wrong here.

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.
Posted May 23, 2010 by Jon
 

Comment

12 repair resets values to 0

Similar problem with the NULL values here. When using repair everything is reset to 0.
Posted May 25, 2010 by Aldo
 

Comment

13 hi

I have created 2 root nodes using the behavior.

Designer Resources, Php Tutorial
Posted Jul 15, 2010 by allby sie
 

Comment

14 thanks

I hope you find this component useful. If you like the component, find a bug, or have a feature request please post a comment.

Thanks,
Porno izle
Posted Jul 17, 2010 by ahmet
 

Comment

15 thanks

Great article - how can i include the ckfinder functionality in the code mentioned in the article?Dental Handpiece Repair
Posted Jul 19, 2010 by Jim
 

Comment

16 thanks

Great article - how can i include the ckfinder functionality in the code mentioned in the article?Marine supply store
Posted Jul 19, 2010 by Jim
 

Comment

17 ed

If we uphold the real facts that many times that it begins to run yesterday. vehicle transport Yes, you can be talked about school.
Posted Jul 22, 2010 by dan kaylee
 

Comment

18 lounge chair

By so many evolutions, they can keep on making users like to us them. And some of them are upgrading their stuff to the newest technology.
lounge chair
Posted Jul 25, 2010 by sanejenny