Introduction

This introduction is based on this article, (c) James Simpson.

The nested set model is a particular technique for representing nested sets (also known as trees or hierarchies) in relational databases. The term was apparently introduced by Joe Celko; others describe the same technique without naming it or using different terms. (Source: Wikipedia)

A typical example of a tree structure is an organigram of an organisation. We'll be using this to explain how nested sets work.

nested set visual

The above diagram is indicating that each node is aware of all its descendants, and vice versa. For example, we can see easily enough from the diagram that Ben and Angela are "children" of Bill. We can also see that Tim and James are "children" of Ben. This ultimately gives us a link between each member of the hierarchy.

For us to represent this we need to store two more values per node, that define the relationship between the nodes of the tree structure. These are shown on the diagram in blue and red. We call these Left (red values) and Right (blue values) pointers. These values are stored on the table, this is all we need to fully represent a nested sets hierarchy.

Those who are into Mathematics know that you can use a different diagram to represent tree structures:

nested set visual

Although this might look confusing at first, and certainly more difficult to read, it is immediately clear why this is called nested sets, and why this way of storing hierarchical data in a flat table is efficient: you can directly see the relation between parent and children (all children are contained within their parent), and every node is aware of its parent, and all its descendants. Using this representation you can clearly see that all pointers line up beautifully. This will come in very handy when we need to access the tree.

Storing multiple tree structures in a single table

You can store multiple tree structures into a single database table. To make this possible, you can add a tree-id column to the table. The tree-id identifies the tree the node belongs to.

A tree-id may either be an integer or a string, and it can be auto-generated for you when you create a new tree, in case integers are used. Integers also make it easier to use the tree-id as a foreign key in a one-to-many relation, and their index lookup in the database is faster. It's strongly suggested you don't use a string value as a tree-id.

Table structure

You can add as many columns to the table as you want, but to be able to work, the Nestedset Model requires certain columns to be present:

  • Left node pointer, default column name is 'left_id'
  • Right node pointer, default column name is 'right_id'
  • Tree id, a required column only if the table contains multiple trees (no default)

The actual names of the columns used for these functions can be configured in the model. Altough not strictly needed, several methods require a "title" or "name" field. The name of this column can be configured in the model as well. If you use a method that requires it to be present, but you haven't defined it, an exception will be thrown.

This is a minumal stucture for a nested sets table:

CREATE TABLE `tree` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `left_id` int(11) unsigned NOT NULL,
  `right_id` int(11) unsigned NOT NULL,
  `tree_id` int(11) unsigned NOT NULL,
  `name` varchar(50),
  PRIMARY KEY (`id`),
  KEY `left_id` (`left_id`),        // optional, might speed up certain lookups
  KEY `right_id` (`right_id`),      // optional, might speed up certain lookups
)

Configuration

The Nestedset Model adds an additional property, $_tree, to the model, with which you can configure the model:

  • left_field defines the name of the table column that is used to store the tree's left pointer. The column name defaults to "left_id".
  • right_field defines the name of the table column that is used to store the tree's right pointer. The column name defaults to "right_id".
  • tree_field defines the name of the table column that is used to store the tree's id, a value that uniquely identifies a tree in case you want to store multiple trees in a single table. If you use a numeric value as tree-id, the model can automatically assign it when you create new tree root nodes. If it is not numeric, you have to supply the value before you call save(). There is no default value.
  • title_field defines the name of the table column that is used to store the tree node's title or name. It is optional, the Model works well without it, but it is required for some of the models methods. Where applicable this is mentioned in the documentation of the method. There is no default value.

The next section show you what a basic Nestedset model looks like, and how you configure it.

Defining the model

Using the nested sets model is as easy as extending \Orm\Model_Nestedset instead of \Orm\Model. This marks your model as being a nested sets model, adds a lot of methods to manipulate trees and nodes, and changes the default behaviour of the delete() and save() methods to make sure the tree stays consistent.

<?php
class Model_Tree extends \Orm\Model_Nestedset
{
	/**
	* @var  string  name of the table to be used by this model
	*/
    protected static $_table_name = 'tree';

	/**
	 * @var  array  array of object properties
	 */
    protected static $_properties = array(
		'id',
		'left_id',
		'right_id',
		'tree_id',
		'name',
    );

	/**
	 * @var  array  array with the tree configuration
	 */
    protected static $_tree = array(
		'left_field'     => 'left_id',		// name of the tree node left index field
		'right_field'    => 'right_id',		// name of the tree node right index field
		'tree_field'     => 'tree_id',		// name of the tree node tree index field
		'title_field'    => 'name',		//  name of the tree node title field
    );

}
?>

As you can see, a pretty standard ORM model configuration. The only additional class property is $_tree, which defines the tree configuration. The model is then set up like a normal ORM model, including any relations or other properties that you wish to use.

Using the model

Based on the example in the introduction and the model defined above, let's start by creating this tree.

<?php
// Bill, our MD, is the root of the tree
$bill = Model_Tree::forge(array('name' => 'Bill'));

// if you save a new node, it will automatically become a tree root node
$bill->save();

// give our MD some subordinates
$angela = Model_Tree::forge(array('name' => 'Angela'));
$ben = Model_Tree::forge(array('name' => 'Ben'));

// they are children of Bill in the tree
$angela->child($bill)->save();
$ben->child($bill)->save();

// add the rest of Customer service
$henry = Model_Tree::forge(array('name' => 'Henry'));
$henry->child($angela)->save();
$nicola = Model_Tree::forge(array('name' => 'Nicola'));
$nicola->child($angela)->save();

// and the development team
$kerry = Model_Tree::forge(array('name' => 'Kerry'));
$kerry->child($ben)->save();
// a sibling of Kerry is a child of Ben too...
$james = Model_Tree::forge(array('name' => 'James'));
$james->sibling($kerry)->save();

// assign the teams benjamin to Kerry
$tim = Model_Tree::forge(array('name' => 'Tim'));
$tim->child($kerry)->save();
?>

As you can see, creating a tree and adding tree nodes is pretty straitforward. One of the main differences between the Nestedset model and other ORM models, it that is has methods that operate on other model objects. You don't just query the model, or save an object, you do it in relation to another object in the tree. That doesn't mean you can't use the traditional methods, Model::find() and Model::query() still work like for any other ORM model.

Let's move on to describing the methods that are available for getting information out of our tree and manupulating the tree structure.

Behavioral changes

In addition to the primary key, which is read-only in every ORM model object once set, a Nestedset model doesn't allow you to make changes to the left- and right pointers of the node, and to the tree-id if defined. Attempting to do so, either via assigment, using a call to set() or by using unset(), will result in a InvalidArgumentException.

save($cascade = null, $use_transaction = false)

The save method works exactly the same as the regular model's implementation.

However, when you use this method on new tree node objects, additional functionality is added to make sure the tree structure stays consistent. When you call it on an object and you have used one of the collection methods documented below, the new object is inserted in the tree relative to the object passed to the collection method.

If you haven't used a collection method, save will assume you want to insert a new root node. If the model is a multi-tree model, it will create a new root with the next available tree-id (if the id is numeric. If not, assign a new tree-id to the node object yourself).

If a root node can not be created because one already exists, an OutOfBoundsException will be thrown. If the tree-id is not numeric, and you haven't supplied one manually, the result is unpredicable!

delete($cascade = null, $use_transaction = false)

The delete method works exactly the same as the regular model's implementation.

If the node you delete is not a leaf node (i.e. is has children), all descendants of the node will move one level up in the tree, they will become descendants of the nodes parent. If you want to delete an entire (subsection of the) tree, use the delete_tree() method instead.

You can not use the delete() method on a root node if the root node has more then one child node. Doing so would create an inconsistent tree, as you can't have multiple nodes at the highest level. Trying do to so will throw a DomainException!

Collection methods

Collection methods are methods that allow you to perform an operation, either a get() or a save(), on a part of a nestedset tree. Some methods are available for both, some are only relevant for one. For clarity, they are documented seperately. Using a collection method on an operation that doesn't support it will cause an OutOfBoundsException to be thrown.

Collection methods for getting nodes

Like with standard ORM models, you can use the get() method to return an array of objects, and the get_one() method to return a single object. If a collection method is supposed to return a single result, like for example root(), get() will return an array with that single object. If a collection method returns multiple objects, like for example children(), get_one() will return the first object from the result set. It is undetermined which one that will be.
get() and get_one() return null if no result was found.

root()

The root method allows you to get the root of the tree of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the root of the tree $henry belongs to
$root = $henry->root()->get_one();

// echos 'true'
echo $root == $bill ? 'true' : 'false';

// get the root of a specific tree
$root = Model_Tree::forge()->set_tree_id($mytreeid)->root()->get_one();

roots()

The roots method allows you to get all the root nodes. If the model is not a multi-tree model, only the one root node is returned.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get all roots in the tree
$roots = $henry->roots()->get();

// echos 'true' (there's only one root in the tree)
echo reset($roots) == $bill ? 'true' : 'false';

// get alls root without having an existing object
$roots = Model_Tree::forge()->roots()->get();

parent()

The parent method allows you to get the parent node of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the parent of $nicola
$parent = $nicola->parent()->get_one();

// echos 'true'
echo $parent == $angela ? 'true' : 'false';

// returns null, it won't work without a node tree context
$parent = Model_Tree::forge()->parent()->get_one();

children()

The children method allows you to get all child nodes of the object you call this method on. Note that this returns only children, no grandchildren. If you are interested in the entire sub-tree, use the descendants method instead.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the children of $angela
$children = $angela->children()->get();

// echos 'true'
echo $children == array($henry->id => $henry, $nicola->id => $nicola) ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->children()->get();

ancestors()

The ancestors method allows you to get all ancestor nodes of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the ancestors of $henry
$ancestors = $henry->children()->get();

// echos 'true'
echo $ancestors == array($bill->id => $bill, $angela->id => $angela) ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->ancestors()->get();

Ancestors are returned in order of their position in the tree, with the root node first.

descendants()

The descendants method allows you to get all descendant nodes of the object you call this method on. This allows you to fetch an entire sub-tree.The result will NOT include the start node itself.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the descendants of $ben
$descendants = $ben->descendants()->get();

// echos 'true'
echo $descendants == array($kerry->id => $kerry, $tim->id => $tim, $james->id => $james) ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->descendants()->get();

Descendants are returned in order of their position in the tree.

leaf_descendants()

The leaf_descendants method allows you to get leaf descendant nodes of the object you call this method on. Unlike the descendants method, this method only returns nodes that do not have children (the leafs of the tree).

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the leaf descendants of $ben
$descendants = $ben->leaf_descendants()->get();

// echos 'true'. Note that $kerry is not returned now, as that has a child object
echo $descendants == array($tim->id => $tim, $james->id => $james) ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->leaf_descendants()->get();

Leaf descendants are returned in order of their position in the tree.

siblings()

The siblings method allows you to get all sibling nodes of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the siblings of $kerry
$siblings = $kerry->siblings()->get();

// echos 'true'
echo $siblings == array($kerry->id => $kerry, $james->id => $james) ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->siblings()->get();

Siblings are returned in order of their position in the tree. Note that the result set will include the current node!

child()

The child method is an alias for the last_child method.

first_child()

The first_child method allows you to get the first child node of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the first child of $bill
$child = $bill->first_child()->get_one();

// echos 'true'
echo $child == $angela ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->first_child()->get_one();

last_child()

The last_child method allows you to get the last child node of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the last child of $bill
$child = $bill->last_child()->get_one();

// echos 'true'
echo $child == $ben ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->last_child()->get_one();

sibling()

The sibling method is an alias for the next_sibling method.

previous_sibling()

The previous_sibling method allows you to get the previous_sibling node of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the previous sibling of $nicola
$sibling = $nicola->previous_silbing()->get_one();

// echos 'true'
echo $sibling == $henry ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->previous_sibling()->get_one();

// will return null too, $henry has no previous sibling
$sibling = $henry->previous_silbing()->get_one();

next_sibling()

The next_sibling method allows you to get the next sibling node of the object you call this method on.

Static No
Parameters None.
Returns Model_Nestedset, for chaining.
Example
// get the next sibling of $henry
$sibling = $henry->next_silbing()->get_one();

// echos 'true'
echo $sibling == $nicola ? 'true' : 'false';

// returns null, it won't work without a current node context
$parent = Model_Tree::forge()->previous_sibling()->get_one();

// will return null too, $nicola has no previous sibling
$sibling = $nicola->next_silbing()->get_one();

path()

The path method returns the path to the current node as a string, using the column identified by the "title_field" in the models tree configuration.

Static No
Parameters None.
Returns string, the path to the node
Throws OutOfBoundsException, if the model doesn't define a "title_field" column.
Example
// get the path to Tim, returns "Bill/Ben/Kerry/Tim"
$path = $tim->path();

// get the path to Nicola, returns "Bill/Angela/Nicola"
$path = $nicola->path();

Collection methods for saving nodes

Like with a standard ORM model, save() will either insert the object as a new record in the table, or it updates an existing record in case the object was retrieved from the database.

However, unlike a standard ORM model in a nestedset tree is is very relevant where exactly in the tree an object is saved. If you don't specify a collection method before save, saving an existing object will just do that, identical to normal ORM model behaviour. If you did specify a collection method, the object will be relocated in the tree when you save. If your saving a new object, not specifing a collection method will create a new root node. If you did specify one, it will determine where in the tree the new node will be inserted.

child($to = null)

The child method is an alias for the last_child method.

first_child($to = null)

The first_child method tells the model you want to save the object you call this method on as the first child of the (primary key of the) node object passed.

Static No
Parameters
Param Default Description
$to
null
The node object or the primary key of the node which has to become the parent of the object on which this method is called.
Returns Model_Nestedset, for chaining.
Example
// give bill a new subordinate, before Angela in the tree
$john = Model_Tree::forge(array('name' => 'John'))->first_child($bill)->save();

// promote Kerry to a subordinate of Bill
$kerry->first_child($bill)->save();

last_child($to = null)

The last_child method tells the model you want to save the object you call this method on as the last child of the (primary key of the) node object passed.

Static No
Parameters
Param Default Description
$to
null
The node object or the primary key of the node which has to become the parent of the object on which this method is called.
Returns Model_Nestedset, for chaining.
Example
// give bill a new subordinate, after Ben in the tree
$john = Model_Tree::forge(array('name' => 'John'))->last_child($bill)->save();

// promote Kerry to a subordinate of Bill
$kerry->last_child($bill)->save();

From a hierarchy perspective, first_child and last_child do the same. Only the exact position in the tree, the relation to it's siblings after the insert or relocation of the node, is different.

sibling($to = null)

The sibling method is an alias for the next_sibling method.

previous_sibling($to = null)

The previous_sibling method tells the model you want to save the object you call this method on as the previous sibling of the (primary key of the) node object passed (just before the passed object, on the same level in the tree).

Static No
Parameters
Param Default Description
$to
null
The node object or the primary key of the node which has to become the next sibling of the object on which this method is called.
Returns Model_Nestedset, for chaining.
Example
// give bill a new subordinate, before Angela in the tree
$john = Model_Tree::forge(array('name' => 'John'))->previous_sibling($angela)->save();

// demote James to be a subordinate of Ben, besides Tim
$james->previous_sibling($tim)->save();

next_sibling($to = null)

The next_sibling method tells the model you want to save the object you call this method on as the next sibling of the (primary key of the) node object passed (just after the passed object, on the same level in the tree).

Static No
Parameters
Param Default Description
$to
null
The node object or the primary key of the node which has to become the previous sibling of the object on which this method is called.
Returns Model_Nestedset, for chaining.
Example
// give bill a new subordinate, after Angela in the tree
$john = Model_Tree::forge(array('name' => 'John'))->next_sibling($angela)->save();

// demote James to be a subordinate of Ben, besides Tim
$james->next_sibling($tim)->save();

Test methods

Test methods are used to query a particular state of the object with regards to their position in the tree.

is_root()

The is_root method tells you if the object you're calling it on is a root node or not.

Static No
Parameters None.
Returns Bool, true if the object is a root node, false if not
Example
// is Bill a root node? echo's 'true'
echo $bill->is_root() ? 'true' : 'false';

// is Tim a root node? echo's 'false'
echo $tim->is_root() ? 'true' : 'false';

is_leaf()

The is_leaf method tells you if the object you're calling it on is a leaf, i.e. that is doesn't have child nodes.

Static No
Parameters None.
Returns Bool, true if the object is a leaf node, false if not
Example
// is Tim a leaf node? echo's 'true'
echo $tim->is_leaf() ? 'true' : 'false';

// is Angela a leaf node? echo's 'false'
echo $angela->is_leaf() ? 'true' : 'false';

is_child()

The is_child method tells you if the object you're calling it on is a child node, i.e. that is has a parent node. This is the reverse of is_root(), as all non-root nodes have a parent.

Static No
Parameters None.
Returns Bool, true if the object is a child node, false if not
Example
// is Henry a root node? echo's 'true'
echo $henry->is_child() ? 'true' : 'false';

// is Bill a root node? echo's 'false'
echo $bill->is_child() ? 'true' : 'false';

is_child_of(Model_Nestedset $to = null)

The is_child_of method allows you to check if the object you call this method on is a child node (a direct descendant) of the node object passed.

Static No
Parameters
Param Default Description
$to
null
The node which you want to check against.
Returns Bool, true if the object is a child node of the node passed, false if not
Example
// is Ben a child node of Bill? echo's 'true'
echo $ben->is_child_of($bill) ? 'true' : 'false';

// is James a child node of Bill? echo's 'false', as it's a grandchild
echo $james->is_child_of($bill) ? 'true' : 'false';

// is Nicola a child node of Ben? echo's 'false', different part of the tree
echo $nicola->is_child_of($ben) ? 'true' : 'false';

is_descendant_of(Model_Nestedset $to = null)

The is_descendant_of method allows you to check if the object you call this method on is a descendant node of the node object passed.

Static No
Parameters
Param Default Description
$to
null
The node which you want to check against.
Returns Bool, true if the object is a descendant node of the node passed, false if not
Example
// is Ben a descendant node of Bill? echo's 'true'
echo $ben->is_descendant_of($bill) ? 'true' : 'false';

// is James a descendant node of Bill? echo's 'true', as it's a grandchild
echo $james->is_descendant_of($bill) ? 'true' : 'false';

// is Nicola a descendant node of Ben? echo's 'false', different part of the tree
echo $nicola->is_descendant_of($ben) ? 'true' : 'false';

is_parent_of(Model_Nestedset $to = null)

The is_parent_of method allows you to check if the object you call this method on is the parent node of the node object passed.

Static No
Parameters
Param Default Description
$to
null
The node which you want to check against.
Returns Bool, true if the object is the parent node of the node passed, false if not
Example
// is Bill the parent of Ben? echo's 'true'
echo $bill->is_parent_of($ben) ? 'true' : 'false';

// is Bill the parent of James? echo's 'false', as it's a grandparent
echo $bill->is_parent_of($james) ? 'true' : 'false';

is_ancestor_of(Model_Nestedset $to = null)

The is_ancestor_of method allows you to check if the object you call this method on is a ancestor of the node object passed.

Static No
Parameters
Param Default Description
$to
null
The node which you want to check against.
Returns Bool, true if the object is an ancestor of the node passed, false if not
Example
// is Bill an ancestor of Ben? echo's 'true'
echo $bill->is_ancestor_of($ben) ? 'true' : 'false';

// is Bill an ancestor of James? echo's 'true', as it's a grandparent
echo $bill->is_ancestor_of($james) ? 'true' : 'false';

is_same_model_as(Model_Nestedset $to = null)

This method is mainly used internally to avoid operations across two different Nestedset models.

is_same_tree_as(Model_Nestedset $to = null)

This method is mainly used internally to avoid operations across two different Nestedset models. It not only checks if both objects are instances of the same model, but also if both instances belong to the same tree in a multi-tree model.

has_parent()

The has_parent method is an alias for the is_child method.

has_children()

The has_children method is the reverse of the is_leaf method. If an object is not a leaf, it implies it has child nodes.

has_previous_sibling()

The has_previous_sibling method tells you if the object you're calling it on has a previous sibling.

Static No
Parameters None.
Returns Bool, true if the object has a previous sibling, false if not
Example
// Does James have a previous sibling? echo's 'true'
echo $james->has_previous_sibling() ? 'true' : 'false';

// Does Tim have a previous sibling? echo's 'false'
echo $tim->has_previous_sibling() ? 'true' : 'false';

has_next_sibling()

The has_next_sibling method tells you if the object you're calling it on has a next sibling.

Static No
Parameters None.
Returns Bool, true if the object has a next sibling, false if not
Example
// Does Henry have a next sibling? echo's 'true'
echo $henry->has_next_sibling() ? 'true' : 'false';

// Does Ben have a next sibling? echo's 'false'
echo $ben->has_next_sibling() ? 'true' : 'false';

Miscellaneous methods

delete_tree($cascade = null, $use_transaction = false)

The delete_tree works identical to the delete() method, which the difference that this method DOES delete all children as well.

If you use the delete_tree() method on a root node, the entire tree will be deleted!

tree_config($name = null)

You use this method to get one or all tree configuration options of the model. It is mainly used internally to construct queries that operate on parts of the tree, or on a specific tree in a multi-tree model.

Static Yes
Parameters
Param Default Description
$name
null
The name of the configuration property to return.
Returns A configuration value, an array of values, or null if the requested value does not exist.
Example
// get the name of the tree-id column
$tree_id = Model_Tree::tree_config('tree_id');

get_tree_id()

You use this method to get the tree-id in a multi-tree environment. If there is a valid node to operate on, the tree-id of that node is returned. If not, the value set using set_tree_id() is returned. If no tree-id has been set, an exception is thrown. This method is mainly used internally to make sure queries run in the context of the current tree in a multi-tree environment.

Static No
Parameters None.
Returns mixed, the tree-id of the current tree, or the default tree-id set.
Example
// get the tree_id (returns 1)
$current_tree = $bill->get_tree_id();

set_tree_id($tree = null)

You use this method to set the tree-id in a multi-tree environment. You mainly use this when you want to run a get() or get_one() but you don't have an existing node to work on.

Static No
Parameters
Param Default Description
$tree
null
The tree-id of the tree to select. If no value, or null, is passed, the stored tree-id is reset.
Returns Model_Nestedset, for chaining.
Example
// get the root of a tree without having a previous node to work with
$root = Model_Tree::forge()->set_tree_id($mytreeid)->get_one();

build_query()

The build_query method returns an ORM query object, like Model_Nestedset::query() but with an added where clause in case of a multi-tree model, to make sure the query runs in the context of the current tree. It requires a tree-id to be available, so if you call this on a new object, use set_tree_id to make sure a valid id is defined.

Static No
Parameters None.
Returns an ORM Query object.
Example
// get all nodes in the current tree with an enabled status set
$enabled = Model_Tree::forge()
	->set_tree_id($mytreeid)
	->build_query()
	->where('enabled', '=', 1)
	->get();

// using an existing object, this will use the tree-id of the $bill object
$enabled = $bill
	->build_query()
	->where('enabled', '=', 1)
	->get();

get_query()

The get_query method returns an ORM query object, but unlike build_query() this method requires a collection methods to be used. It allows you to use the standard Model methods to constuct a first selection of nodes, and use the returned Query object to fine-tune the query.

Static No
Parameters None.
Returns an ORM Query object.
Example
// get all child nodes of Bill with an enabled status set
$enabled = $bill
	->children()
	->get_query()
	->where('enabled', '=', 1)
	->get();

count_children()

The count_children method returns the number of children (direct descendants) of the object you're calling it on.

Static No
Parameters None.
Returns Integer
Example
// Get the number of Bill's children, echo's 2
echo $bill->count_children();

// Get the number of Kerry's children, echo's 1
echo $kerry->count_children();

// Get the number of Tim's children, echo's 0
echo $tim->count_children();

// echo's 0 too, a new object can't have children
echo Model_Tree::forge()->count_children();

count_descendants()

The count_descendants method returns the number of descendants of the object you're calling it on.

Static No
Parameters None.
Returns Integer
Example
// Get the number of Bill's descendants, echo's 7
echo $bill->count_descendants();

// Get the number of Ben's children, echo's 3
echo $ben->count_descendants();

// echo's 0, a new object can't have descendants
echo Model_Tree::forge()->count_descendants();

depth()

The depth method returns the depth or level the current node is in the tree, where the level of the root node is 0.

Static No
Parameters None.
Returns Mixed, integer or false if the current object is not valid
Example
// Get the level of Bill in the organisation
echo $bill->depth(); // echo's 0, he's top dog!

// Get the level of Angela in the organisation
echo $angela->depth(); // echo's 1, as she's an N+1

// Get the level of Tim in the organisation
echo $tim->depth(); // echo's 3, as low as you can go...

// returns false, you can't get the depth of a new node
$result = Model_Tree::forge()->depth();

dump_tree($as_object = false, $children = 'children')

The dump_tree method returns the entire tree, using the current object as root node into a multi-dimensional array.

Static No
Parameters
Param Default Description
$as_object
false
If true, it will return a hierarchical tree of objects instead of a multi-dimensional array.
$children
'children'
data for child nodes will be added to a property or array key with this name.
Returns Mixed, multi-dimensional array, or the current object, with added children.
Example
// returns a multi-dimensional array. $tree['children'] will contain the children of Bill
$tree = $bill->dump_tree();

// returns the 'bill' object with a property 'children' containing the children of Bill
$tree = $bill->dump_tree(true);

// using $tree, this returns array($angela, $ben)
$children = $tree->children;

// and this returns array($henry, $nicola), the children of $angela
$grandchildren = reset($children)->children;

// ORM objects are always used by reference!
$bill->dump_tree(true);

// so this works fine too...
$children = $bill->children;

As with all ORM array's, the table's primary key is used as array index. Note that because the ORM uses objects by reference from a single cache, if you choose to use 'as_object', children will be added to existing objects too.