Part of the EllisLab Network
   
1 of 14
1
MPTtree, Hieararchical trees in a database table
Posted: 13 March 2008 04:15 PM   [ Ignore ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

Current Version: 0.1.6-fix2


MPTtree is a model to handle trees in a Nested Sets structure in a database table.

Features:
xpath and paths to nodes
ORM (currently without the relationship part), will be somewhat integrated with IgnitedRecord in the future
Iterators (Only for PHP 5)
Multiple instances with help of custom loader (optional)
A simple admin interface, using Ext JS for drag n drop (a separate zip file, in wiki)
Insertion, Moving, Deletion and Validation methods


Now I have released version 0.1.6 which has got a few bug fixes, a new tree2array method and get_descendants have been modified so they additionally can return a depth column (only non ORM).

0.1.6-fix contains some additional bug fixes.

MPTtree have currently only been tested with MySQL 5, and the queries are currently only written for MySQL (if someone wants to help me to port the SQL to other databases, or is it needed?).

Included in the zip file is a (good?) manual which describes most of the methods (if not all you need to know).


You can find the zip file here

If you have any comments or suggestions, feel free to reply (I’d like to have some feedback on this).

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
Posted: 14 March 2008 03:35 PM   [ Ignore ]   [ # 1 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  593
Joined  02-04-2008

Wow this looks cool! Reading over the manual now.

Any way you could set up some demos to show it in use?

Profile
 
 
Posted: 22 March 2008 02:17 PM   [ Ignore ]   [ # 2 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

Have been ill for some time, but now I’m alright. I have now updated MPTtree to version 0.1.6 (see top post) and
I have also made a simple wiki as an example (quite hastily done, can contain errors):

controllers/wiki.php:

<?php
class Wiki extends Controller{
    
function Wiki(){
        parent
::Controller();
        
$this->load->database();
        
$this->load->MPTT('wiki_tree''pages');
        
$this->load->helper('url');
    
}
    
    
function _remap($method){
        
switch ($method{
            
case 'add':
                
$this->add();
                break;
            
            case 
'edit':
                
$this->edit();
                break;
            case 
'del':
                
$this->del();
                break;
            
            default:
                
$this->index();
                break;
        
}
    }
    
    
function index(){
        
// load the segments and remove the "wiki" one
        
$segs $this->uri->segment_array();
        
array_shift($segs);
        
        
// find the page with xpath
        
$page $this->pages->xpath($segs);
        
        if(
$page != false){
            $this
->_show_page($page,$segs);
        
}
        else{
            
if(count($segs) == 0){
                
// no root, add one
                
$new_node->set('title','Default title');
                
$new_node->set('contents','Default Text');
                
$new_node->set('date',time());
                
$new_node->insert_as_root();
                
$this->_show_page($parent->get_all(),$segs);
                return;
            
}
            
// page does not exists
            
$data['title''404 Error: Page does not exist';
            
$data['contents''The page you requested cannot be found';
            
$this->load->view('wiki',$data);
        
}
    }
    
    
function edit(){
        
if($this->input->post('edit') == true && $this->input->post('page') !== false){
            $data[
'contents'$this->input->post('contents');
            
$data['title'$this->input->post('title');
            
$data['date'time();
            
// commit changes
            
$this->db->where('id',$this->input->post('page'));
            
$this->db->update('wiki_tree',$data);
            
// load data to display edited page
            
$page $this->pages->get_ORM_byid($this->input->post('page'));
            
$this->_show_page($page->get_all(),$page->path());
        
}
        else{
            
// display edit form
            
$page $this->pages->get_node($this->uri->segment(3));
            
$page['edit'true;
            
$this->load->view('wiki',$page);
        
}
    }
    
    
function add(){
        $parent 
$this->pages->get_ORM($this->uri->segment(3));
        
$new_node $this->pages->new_ORM(); // creates a new empty object
        // save data
        
$new_node->set('title','Default title');
        
$new_node->set('contents','Default Text');
        
$new_node->set('date',time());
        
$new_node->insert_as_first_child_of($parent);
        
        
// load data to display parent page
        
$this->_show_page($parent->get_all(),$parent->path());
    
}
    
    
function del(){
        $page 
$this->pages->get_ORM($this->uri->segment(3));
        if(
$page){
            $page
->delete();
        
}
        redirect
('/wiki');
    
}
    
    
function _show_page($page,$segs = array()){
        $page[
'edit_link'= array('wiki','edit',$page['lft']);
        
$page['del_link'= array('wiki','del',$page['lft']);
        
$page['children'$this->pages->get_children($page['lft']$page['rgt']);
        
$page['path'array_merge(array('wiki'),$segs);
        
$page['deleteable'true;
        
// just pass the result to the view
        
$this->load->view('wiki',$page);
    
}
}
?> 

views/wiki.php:

<html>
<
head>
<
title>Wiki<?php echo htmlentities($title?></title>
</
head>
<
body>
<
h1><?php echo htmlentities($title?></h1>

<?php if (isset($children) && count($children)): ?>
    
<p>Children:<?php foreach ($children as $child): ?>
    <?php 
echo anchor(array_merge($path,array($child['title'])),$child['title']?>
<?php 
endforeach ?></p>
<?php endif ?>
<p><?php echo anchor(array('wiki','add',$lft),'Add Child'?>
<hr />
<?php if (isset($edit) && $edit == true): ?>
    
<form method="POST" action="<?php echo site_url(array('wiki','edit')) ?>">
    <
input type="hidden" name="edit" value="true" />
    <
input type="hidden" name="page" value="<?php echo $id ?>" />
    
Title:<br />
    <
input type="text" name="title" value="<?php echo $title ?>"><br />
    
Content: <br />
    <
textarea name="contents" rows="20" cols="60"><?php echo htmlentities($contents?></textarea>
    <
br />
    <
input type="submit" value="Save" />
</
form>
<?php else: ?>
    <?php 
echo $contents?>
<?php 
endif ?>
<hr />
<?php if(isset($date)): ?>
<em>Last Modified<?php echo date('Y-m-d H:i:s',$date?></em>
<?php endif ?>
<?php 
echo isset($edit_link) ? ' - ' anchor($edit_link,'Edit Page') : '' ?>
 
<?php echo isset($deleteable) && $deleteable == true anchor($del_link,'Delete Page') : '' ?> - <a href="<?php echo site_url(array('wiki')) ?>">Home</a>
</
body>
</
html

Database table:
wiki_tree

columns:
id unsigned int auto increment
lft unsigned int
rgt unsigned int
title varchar 45
contents text
date unsigned int

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
Posted: 27 March 2008 03:25 PM   [ Ignore ]   [ # 3 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

I have now made some bug fixes (0.1.6-fix) to MPTtree, see change log for details.
Thanks for the bug reports!
The wiki above is an example (forgot to mention that earlier).

Any suggestions for new features?

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
Posted: 06 April 2008 06:11 AM   [ Ignore ]   [ # 4 ]  
Grad Student
Rank
Total Posts:  47
Joined  11-25-2007

Sorry, But I wonder what purpose I can use MPTtree for? Can I use this for a CI based CMS development? or shopping cart?

Profile
 
 
Posted: 06 April 2008 07:15 AM   [ Ignore ]   [ # 5 ]  
Lab Assistant
RankRank
Total Posts:  228
Joined  10-17-2006

Anything that involves trees. Pine trees, etc. wink

Say you have a list of locations:

World
-Europe
-Asia
-North-America
—USA
—-California
——Los Angeles

With MPTtree you can store the hierarchy of this in a database. Same goes for menus, product categories etc.

Profile
 
 
Posted: 06 April 2008 08:27 AM   [ Ignore ]   [ # 6 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

Another good thing with nested sets is that they can easily be paced in a certain order (not bound to sorting by columns).

If you want to make a CMS, look at the wiki example above. The example has a lot of flaws (Not able of having two pages with the same names as children to the same node, for example (has nothing to do with MPTtree, it’s how I implemented the easy searching by title in the controller)), but it is something that might point you in the right direction.

I’m also making a CMS with MPTtree, something reminding of Radiant CMS (I like the simplicity in the Radius tags), so don’t hesitate to ask smile.

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
Posted: 04 May 2008 01:30 PM   [ Ignore ]   [ # 7 ]  
Lab Assistant
Avatar
RankRank
Total Posts:  293
Joined  01-25-2008

@m4rw3r

I am fascinated by your example wiki ...
Is there a “fuller”, more complete (i.e. more documented) version of your work that you might’ve posted somewhere?

 Signature 

http://PawshPal.com/ - Funny Dog Pictures

Profile
 
 
Posted: 04 May 2008 01:54 PM   [ Ignore ]   [ # 8 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

@taewoo

No, not for the moment, the CMS I’m developing is currently on hold (haven’t got enough time and interest, need to rebuild the data representation of the pages (again :-( ) because I have got a better idea on how to organize them and the db (will still use MPTtree, though)).

But you can sit and experiment with and improve the example wiki if you want to have something to start from.

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
Posted: 11 May 2008 07:49 AM   [ Ignore ]   [ # 9 ]  
Summer Student
Total Posts:  11
Joined  05-11-2008

m4rw3r,

I’ve been trying to get your module to work, but without much success.  Using the wiki.php example, you posted, I’m getting the error:

Fatal error: Cannot pass parameter 1 by reference in /mywebserver’s_path/system/application/models/mpttree.php on line 405

This is after I setup the table as you described in your example.  I’m running PHP 5.25 with MySQL     4.1.22-standard.  I believe that I’m successfully connecting to the database without any problems (since loading the database() routines doesn’t result in any complaining). 

I’m also having the same problem when I’m running my own code examples. 

Is there supposed to be a default setting for lft, rgt or ID?  Right now I have them all set to 0, but fiddling around with the default values hasn’t solved the error.

Thanks for all your work on this code.  It looks really great - exactly what I was looking for, actually - and I’m excited to use it once I get things working.

Profile
 
 
Posted: 11 May 2008 08:01 AM   [ Ignore ]   [ # 10 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

Ok, seems like it’s complaining on the debug_message() function.

Remove the ampersand in the definition of debug_message():
function debug_message(&$message){
to
function debug_message($message){

The default settings are covered in the manual.

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
   
1 of 14
1