Welcome Guest, Not a member yet? Register   Sign In
MPTtree, Hieararchical trees in a database table
#1

[eluser]m4rw3r[/eluser]
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).
#2

[eluser]louis w[/eluser]
Wow this looks cool! Reading over the manual now.

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

[eluser]m4rw3r[/eluser]
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:
Code:
<?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:
Code:
<html>
<head>
<title>Wiki: <?php echo htmlentities($title) ?></title>
</head>
<body>
<h1>&lt;?php echo htmlentities($title) ?&gt;</h1>

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

Database table:
wiki_tree

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

[eluser]m4rw3r[/eluser]
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?
#5

[eluser]Tony Nash[/eluser]
Sorry, But I wonder what purpose I can use MPTtree for? Can I use this for a CI based CMS development? or shopping cart?
#6

[eluser]nmweb[/eluser]
Anything that involves trees. Pine trees, etc. Wink

Say you have a list of locations:
<code>
World
-Europe
-Asia
-North-America
--USA
---California
----Los Angeles
</code>
With MPTtree you can store the hierarchy of this in a database. Same goes for menus, product categories etc.
#7

[eluser]m4rw3r[/eluser]
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.
#8

[eluser]taewoo[/eluser]
@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?
#9

[eluser]m4rw3r[/eluser]
@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.
#10

[eluser]MaxPar[/eluser]
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:

Quote: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.




Theme © iAndrew 2016 - Forum software by © MyBB