<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Richard Knop&#039;s Zend Framework Blog &#187; Nested set model</title>
	<atom:link href="http://blog.richardknop.com/tag/nested-set-model/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.richardknop.com</link>
	<description>Zend Framework, PHP, MySQL, jQuery, JavaScript, AJAX, SEO, E-commerce and more</description>
	<lastBuildDate>Mon, 06 Sep 2010 15:49:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Nested set model</title>
		<link>http://blog.richardknop.com/2009/05/nested-set-model/</link>
		<comments>http://blog.richardknop.com/2009/05/nested-set-model/#comments</comments>
		<pubDate>Fri, 22 May 2009 16:07:20 +0000</pubDate>
		<dc:creator>Richard Knop</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Nested set model]]></category>

		<guid isPermaLink="false">http://blog.richardknop.com/?p=161</guid>
		<description><![CDATA[In the previous post I explained the adjacency list model and how to implement it in PHP. There is an alternative &#8211; the nested set model (also called modified preorder tree traversal). It is more complicated than the adjacency model but there is a reason why it might be better in certain situations: unlimited hierarchy [...]]]></description>
			<content:encoded><![CDATA[<p>In the previous post I explained the <a href="http://blog.richardknop.com/2009/05/adjacency-list-model/">adjacency list model</a> and how to implement it in PHP. There is an alternative &#8211; the nested set model (also called modified preorder tree traversal). It is more complicated than the adjacency model but there is a reason why it might be better in certain situations:</p>
<ul>
<li>unlimited hierarchy levels</li>
</ul>
<p>On the other hand, there are reasons why I wouldn&#8217;t recommend using it unless you have to:</p>
<ul>
<li>very complicated hierarchy modifications compared to the adjacency model  (you often have to update almost the whole table)</li>
<li>a small mistake in PHP code for modifying the hierarchy can break up the whole table (you must test your application carefully before deploying it to the production environment)</li>
</ul>
<p>Look at the picture to get a better idea about what the nested set model looks like:</p>
<p><img class="alignnone size-full wp-image-175" title="Nested set model" src="http://blog.richardknop.com/wp-content/uploads/2009/05/nested-set-model.gif" alt="Nested set model" width="593" height="313" /></p>
<p>Pay special attention to the red numbers &#8211; based on these numbers you can calculate the position of an item in the hierarchy. The schema (&#8216;lft&#8217; and &#8216;rgt&#8217; are used because &#8216;left&#8217; and &#8216;right&#8217; are MySQL reserved words):</p>
<pre>CREATE TABLE categories (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL,
PRIMARY KEY (id)
) ENGINE = INNODB;</pre>
<p>Let’s populate the table with some content we can work with:</p>
<pre>INSERT INTO categories (id, name, lft, rgt) VALUES
(NULL, 'Relational databases', 1, 12),
(NULL, 'SQLite', 2, 3),
(NULL, 'MySQL', 4, 9),
(NULL, 'PDO', 5, 6),
(NULL, 'Transactions', 7, 8),
(NULL, 'PostgreSQL', 10, 11);</pre>
<p>The query to select the full tree is simple:</p>
<pre>SELECT * FROM categories ORDER BY lft</pre>
<h2>PHP implementation</h2>
<p>Displaying the tree in an unordered XHTML list is not the hardest part. There are two important formulas:</p>
<pre>rgt &lt; last rgt =&gt; increment the hierarchy level (move one level down)
lft - last lft &gt; 2 =&gt; decrement the hierarchy level (move one level up)</pre>
<p>Therefor you can create the unordered list by keeping a track of left and right values:</p>
<div class="geshi no php">
<ol>
<li class="li1">
<div class="de1"><span class="re1">$user</span> <span class="sy0">=</span> <span class="st0">&#39;root&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$pass</span> <span class="sy0">=</span> <span class="st0">&#39;&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$conn</span> <span class="sy0">=</span> <span class="kw2">new</span> PDO<span class="br0">&#40;</span><span class="st0">&#39;mysql:host=localhost;dbname=test&#39;</span><span class="sy0">,</span> <span class="re1">$user</span><span class="sy0">,</span> <span class="re1">$pass</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$sql</span> <span class="sy0">=</span><span class="st0">&#39;SELECT * FROM categories ORDER BY lft&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$categories</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$left</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$right</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$level</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">while</span> <span class="br0">&#40;</span><span class="re1">$row</span> <span class="sy0">=</span> <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">fetchObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">empty</span><span class="br0">&#40;</span><span class="re1">$right</span><span class="br0">&#41;</span> <span class="sy0">===</span> <span class="kw2">false</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$lastRight</span> <span class="sy0">=</span> <span class="kw3">end</span><span class="br0">&#40;</span><span class="re1">$right</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$lastRight</span> <span class="sy0">&gt;</span> <span class="re1">$row</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$level</span> <span class="sy0">=</span> <span class="re1">$level</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">empty</span><span class="br0">&#40;</span><span class="re1">$left</span><span class="br0">&#41;</span> <span class="sy0">===</span> <span class="kw2">false</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$lastLeft</span> <span class="sy0">=</span> <span class="kw3">end</span><span class="br0">&#40;</span><span class="re1">$left</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$row</span><span class="sy0">-&gt;</span><span class="me1">lft</span> <span class="sy0">-</span> <span class="re1">$lastLeft</span> <span class="sy0">&gt;</span> <span class="nu0">2</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$level</span> <span class="sy0">=</span> <span class="re1">$level</span> <span class="sy0">-</span> <span class="nu0">1</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re1">$categories</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st0">&#39;id&#39;</span> <span class="sy0">=&gt;</span> <span class="re1">$row</span><span class="sy0">-&gt;</span><span class="me1">id</span><span class="sy0">,</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#39;name&#39;</span> <span class="sy0">=&gt;</span> <span class="re1">$row</span><span class="sy0">-&gt;</span><span class="me1">name</span><span class="sy0">,</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#39;level&#39;</span> <span class="sy0">=&gt;</span> <span class="re1">$level</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re1">$left</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re1">$row</span><span class="sy0">-&gt;</span><span class="me1">lft</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re1">$right</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re1">$row</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw3">echo</span> <span class="st0">&#39;&lt;ul&gt;&#39;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="re1">$count</span> <span class="sy0">=</span> <span class="kw3">count</span><span class="br0">&#40;</span><span class="re1">$categories</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$count</span> <span class="sy0">==</span> <span class="nu0">1</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw3">echo</span> <span class="st0">&#39;&lt;li&gt;&#39;</span><span class="sy0">,</span> <span class="re1">$categories</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;name&#39;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="st0">&#39;&lt;/li&gt;&#39;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re1">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span><span class="kw3">isset</span><span class="br0">&#40;</span><span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="st0">&#39;&lt;li&gt;&#39;</span><span class="sy0">,</span> <span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;name&#39;</span><span class="br0">&#93;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$i</span> <span class="sy0">&lt;</span> <span class="re1">$count</span> <span class="sy0">-</span> <span class="nu0">1</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span> <span class="sy0">&gt;</span> <span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="st0">&#39;&lt;ul&gt;&#39;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="st0">&#39;&lt;/li&gt;&#39;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span> <span class="sy0">&lt;</span> <span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="kw3">str_repeat</span><span class="br0">&#40;</span><span class="st0">&#39;&lt;/ul&gt;&lt;/li&gt;&#39;</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">,</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span> <span class="sy0">-</span> <span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="st0">&#39;&lt;li&gt;&#39;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="kw3">str_repeat</span><span class="br0">&#40;</span><span class="st0">&#39;&lt;/ul&gt;&lt;/li&gt;&#39;</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">,</span> <span class="re1">$categories</span><span class="br0">&#91;</span><span class="re1">$i</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&#39;level&#39;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re1">$i</span><span class="sy0">++;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw3">echo</span> <span class="st0">&#39;&lt;/ul&gt;&#39;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="sy0">;</span></div>
</li>
</ol>
</div>
<h2>Modifying the hierarchy</h2>
<p>This is where it gets interesting. Modifying the hierarchy created by the nested set model or in other words adding, updating or removing branches (nodes) from the tree while keeping the hierarchy integrity.</p>
<h3>Adding nodes</h3>
<p>There are two ways how to add a new node &#8211; from the left and from the right. Both have very similar implementation (I will show you just the latter). Before I show you how to do it here is another important formula:</p>
<pre>(rgt - lft - 1) / 2 = number of children nodes</pre>
<p>In theory, what you need to do is add a new node and update all nodes to the right (those that have higher &#8216;rgt&#8217; value). First, here is the algorithm in the pseudo language:</p>
<pre>get a parent node

for all nodes
    if rgt &gt;= parent-&gt;rgt
        rgt = rgt + 2
        if lft &lt;&gt; 1 and lft &gt; parent-&gt;lft
            lft = lft + 2
        endif
    endif
endfor

add a new node:
    lft = parent-&gt;lft + number of children nodes * 2 + 1
    rgt = lft + 1</pre>
<p>Here is a function:</p>
<div class="geshi no php">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">function</span> addNodeFromRight<span class="br0">&#40;</span><span class="re1">$conn</span><span class="sy0">,</span> <span class="re1">$parentId</span><span class="sy0">,</span> <span class="re1">$name</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; try <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">setAttribute</span><span class="br0">&#40;</span>PDO<span class="sy0">::</span><span class="me2">ATTR_ERRMODE</span><span class="sy0">,</span> PDO<span class="sy0">::</span><span class="me2">ERRMODE_EXCEPTION</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">beginTransaction</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;SELECT * FROM categories WHERE id = ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$parentId</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$parent</span> <span class="sy0">=</span> <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">fetchObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;UPDATE categories SET rgt = rgt + 2 WHERE rgt &gt;= ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$parent</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;UPDATE categories SET lft = lft + 2 WHERE rgt &gt;= ? AND lft &lt;&gt; 1 AND lft &gt; ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$parent</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="sy0">,</span> <span class="re1">$parent</span><span class="sy0">-&gt;</span><span class="me1">lft</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;INSERT INTO categories (name, lft, rgt) VALUES (?, ?, ?)&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$numberOfChildren</span> <span class="sy0">=</span> <span class="br0">&#40;</span><span class="re1">$parent</span><span class="sy0">-&gt;</span><span class="me1">rgt</span> <span class="sy0">-</span> <span class="re1">$parent</span><span class="sy0">-&gt;</span><span class="me1">lft</span> <span class="sy0">-</span> <span class="nu0">1</span><span class="br0">&#41;</span> <span class="sy0">/</span> <span class="nu0">2</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$lft</span> <span class="sy0">=</span> <span class="re1">$parent</span><span class="sy0">-&gt;</span><span class="me1">lft</span> <span class="sy0">+</span> <span class="re1">$numberOfChildren</span> <span class="sy0">*</span> <span class="nu0">2</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$rgt</span> <span class="sy0">=</span> <span class="re1">$lft</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$name</span><span class="sy0">,</span> <span class="re1">$lft</span><span class="sy0">,</span> <span class="re1">$rgt</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span> catch <span class="br0">&#40;</span>PDOException <span class="re1">$e</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">rollback</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="re1">$e</span><span class="sy0">-&gt;</span><span class="me1">getMessage</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<h3>Removing nodes</h3>
<p>Very similar to adding nodes but you must also take special care of children branches in case you want to remove a parent node. In the pseudo language:</p>
<pre>get a node

if node-&gt;lft &lt;&gt; 1

    delete the node

    for all nodes
        if rgt &gt; node-&gt;rgt
            rgt = rgt - 2
        endif
        if rgt &gt; node-&gt;rgt and lft &lt;&gt; 1 and lft &gt; node-&gt;lft
            lft = lft - 2
        endif
        if rgt &lt; node-&gt;rgt and lft &gt; node-&gt;lft
            lft = lft - 1
            rgt = rgt - 1
        endif
    endfor

endif</pre>
<p>And here&#8217;s how it could look in PHP:</p>
<div class="geshi no php">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">function</span> removeNode<span class="br0">&#40;</span><span class="re1">$conn</span><span class="sy0">,</span> <span class="re1">$id</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; try <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">setAttribute</span><span class="br0">&#40;</span>PDO<span class="sy0">::</span><span class="me2">ATTR_ERRMODE</span><span class="sy0">,</span> PDO<span class="sy0">::</span><span class="me2">ERRMODE_EXCEPTION</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">beginTransaction</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;SELECT * FROM categories WHERE id = ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$id</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$node</span> <span class="sy0">=</span> <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">fetchObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$node</span><span class="sy0">-&gt;</span><span class="me1">lft</span> <span class="sy0">&lt;&gt;</span> <span class="nu0">1</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;DELETE FROM categories WHERE id = ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$id</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;UPDATE categories SET rgt = rgt &#8211; 2 WHERE rgt &gt; ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$node</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;UPDATE categories SET lft = lft &#8211; 2 WHERE rgt &gt; ? AND lft &lt;&gt; 1 AND lft &gt; ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$node</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="sy0">,</span> <span class="re1">$node</span><span class="sy0">-&gt;</span><span class="me1">lft</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$sql</span> <span class="sy0">=</span> <span class="st0">&#39;UPDATE categories SET lft = lft &#8211; 1, rgt = rgt &#8211; 1 WHERE rgt &lt; ? AND lft &gt; ?&#39;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">prepare</span><span class="br0">&#40;</span><span class="re1">$sql</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re1">$node</span><span class="sy0">-&gt;</span><span class="me1">rgt</span><span class="sy0">,</span> <span class="re1">$node</span><span class="sy0">-&gt;</span><span class="me1">lft</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span> catch <span class="br0">&#40;</span>PDOException <span class="re1">$e</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$conn</span><span class="sy0">-&gt;</span><span class="me1">rollback</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="re1">$e</span><span class="sy0">-&gt;</span><span class="me1">getMessage</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>Note that I made sure that lft is not equal to 1 before removing the node. You cannot remove the top node or else the whole tree hierarchy will break up.</p>
<h3>Updating nodes</h3>
<p>You might have already guessed it &#8211; to update a node you can combine addNodeFromRight() and removeNode() functions.</p>
<p>That&#8217;s not the most efficient way to do it though. I&#8217;m too tired to think about the algorithm now but I&#8217;m sure you can figure it out after reading this post.</p>
<h2>A small trick</h2>
<p>Most of the time you will need a hierarchy with more separate trees (or subhierarchies) which would be much more complicated to achieve. There&#8217;s a small trick you can use though. Just don&#8217;t display the top node (it could be named &#8216;Categories&#8217; in this case) and decrease the hierarchy level in the script by 1.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.richardknop.com/2009/05/nested-set-model/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
