Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 10 of 10

Thread: MPTT Class

  1. #1
    Senior Coder
    Join Date
    Aug 2003
    Location
    One step ahead of you.
    Posts
    2,815
    Thanks
    0
    Thanked 3 Times in 3 Posts

    MPTT Class

    This class should handle the management of a MPTT tree.

    Manual (sort of):

    Make sure you have set the constants:
    • TABLE_DATA - The name of the table in which the tree data is stored.
    • FIELD_KEY - The name of the PRIMARY KEY column (the one with row id's).
    • FIELD_LEFT_ID - The name of the column in which the left id's are stored.
    • FIELD_RIGHT_ID - The name of the column in which the right id's are stored.
    • FIELD_LEVEL - The name of the column in which the elemts levels are stored.

    Yes, all 4 columns are required in your table setup.

    Method description:

    mptt:mptt($link = null)
    $link - (optional) MySQL link identifier.

    mptt:add($parent, $child_num = 0, $misc_data = false)
    Add an element to the tree as a child of $parent and as $child_num'th child. If $data is not supplied the insert id will be returned.
    $parent - The id of the parent of the added object (0 for root).
    $child_num - The new element will be placed as the $child_num'th child of the parent*.
    $misc_data - An array containing other data to be added to the tree. If not set the function will return the insert id of the new row.
    Returns bool(true) on success unless $misc_data is not set.

    mptt::delete($id, $keep_children = false)
    Deletes element $id with or without children. If children should be kept they will become children of $id's parent.
    $id - The id of the element to delete.
    $keep_children - If true the children of the deleted element will move to the parent of the deleted element.
    Returns bool(true) on success.

    mptt::move($id, $target_id, $child_num = 0)
    Move a element (with children) $id, under element $target_id as the $child_num'th child of that element.
    $id - The element to move
    $target_id - The id of the new parent.
    $child_num - The new element will be placed as the $child_num'th child of the parent*.

    mptt::copy($id, $parent, $child_num = 0)
    Copies element $id (with children) to $parent as the $child_mun'th child.
    $id - The element to move
    $parent - The id of the new parent.
    $child_num - The new element will be placed as the $child_num'th child of the parent*.

    mptt::swap($id1, $id2)
    Swaps two elements and ONLY the elements!
    $id1 and $id2 - Id's of the elements to swap.

    mptt::check_consistency(&$errors=array()) // Kind of experimental. But worked in tests.
    Check if all of the tree's left and right id's are correct.
    $errors - This will hold an array of error messages if any messages accour (null otherwise).
    Returns true if there are no errors.

    *$child_num is by default 0.
    0 means that the element will be inserted as the first child, 1 as the 2nd child and so on.
    You can also use negative numbers. -1 would be the last child... pretty much like substr().

    Code:
    It was too large. Attached.

    Example:
    SQL & example data:
    Code:
    CREATE TABLE `data` (
      `id` mediumint(8) unsigned NOT NULL auto_increment,
      `title` varchar(200) NOT NULL,
      `left_id` int(10) NOT NULL,
      `right_id` int(10) NOT NULL,
      `level` mediumint(8) unsigned NOT NULL,
      UNIQUE KEY `id` (`id`)
    );
    
    # The same example as it was on aesthetic-theory
    INSERT INTO `data` VALUES (1, 'Music', 1, 14, 1);
    INSERT INTO `data` VALUES (2, 'Ludo', 2, 5, 2);
    INSERT INTO `data` VALUES (3, 'self titled', 3, 4, 3);
    INSERT INTO `data` VALUES (4, 'weezer', 6, 13, 2);
    INSERT INTO `data` VALUES (5, 'pinkerton', 7, 12, 3);
    INSERT INTO `data` VALUES (6, 'el scorcho lyrics', 8, 9, 4);
    INSERT INTO `data` VALUES (7, 'pink triangle lyrics', 10, 11, 4);
    INSERT INTO `data` VALUES (8, 'Foods', 15, 22, 1);
    INSERT INTO `data` VALUES (9, 'Meat', 16, 21, 2);
    INSERT INTO `data` VALUES (10, 'Steak', 17, 18, 3);
    INSERT INTO `data` VALUES (11, 'ribs', 19, 20, 3);
    PHP & HTML:
    PHP Code:
    <?php
    // Example
    ?>
    <html>
    <head>
    <title>MPTT</title>
    </head>
    <body>

    <form name="add" action="<?php print $_SERVER['PHP_SELF']; ?>" method="POST">
    Parent ID: <input type="text" name="parent" /><br />
    As child: <input type="text" name="child_num" value="0" /><br />
    Title: <input type="text" name="title" /><br />
    <input type="submit" name="add" value="Add" />
    </form>

    <form name="delete" action="<?php print $_SERVER['PHP_SELF']; ?>" method="POST">
    ID: <input type="text" name="id" /><br />
    Keep children? <input type="radio" name="keep_children" value="0" checked="checked" />No
    <input type="radio" name="keep_children" value="1" />Yes<br />
    <input type="submit" name="delete" value="Delete" />
    </form>

    <form name="move" action="<?php print $_SERVER['PHP_SELF']; ?>" method="POST">
    ID: <input type="text" name="id" /><br />
    Target: <input type="text" name="target_id" /><br />
    As child: <input type="text" name="child_num" value="0" /><br />
    <input type="submit" name="move" value="Move" />
    </form>

    <form name="copy" action="<?php print $_SERVER['PHP_SELF']; ?>" method="POST">
    ID: <input type="text" name="id" /><br />
    Target: <input type="text" name="target_id" /><br />
    As child: <input type="text" name="child_num" value="0" /><br />
    <input type="submit" name="copy" value="Copy" />
    </form>

    <form name="swap" action="<?php print $_SERVER['PHP_SELF']; ?>" method="POST">
    ID1: <input type="text" name="id1" /><br />
    ID2: <input type="text" name="id2" /><br />
    <input type="submit" name="swap" value="Swap" />
    </form>
    <?php
    error_reporting
    (E_ALL);


    // DB constants
    define('DB_HOST''localhost');
    define('DB_USERNAME''root');
    define('DB_PASSWORD''');
    define('DB_DATABASE''mptt');

    // connect to the database
    $link mysql_connect(DB_HOSTDB_USERNAMEDB_PASSWORD) or die (mysql_error());
    mysql_select_db(DB_DATABASE) or die (mysql_error());

    require 
    'class_mptt.php';
    $mptt = new mptt($link);
    if(isset(
    $_POST['add']))
    {
        
    $mptt->add($_POST['parent'], $_POST['child_num'], array('title' => $_POST['title']));
    }
    elseif(isset(
    $_POST['delete']))
    {
        
    $mptt->delete($_POST['id'], (bool) $_POST['keep_children']);
    }
    elseif(isset(
    $_POST['move']))
    {
        
    $mptt->move($_POST['id'], $_POST['target_id'], $_POST['child_num']);
    }
    elseif(isset(
    $_POST['copy']))
    {
        
    $mptt->copy($_POST['id'], $_POST['target_id'], $_POST['child_num']);
    }
    elseif(isset(
    $_POST['swap']))
    {
        
    $mptt->swap($_POST['id1'], $_POST['id2']);
    }

    $sql 'SELECT * FROM `' TABLE_DATA '` WHERE `id` > 0 ORDER BY `left_id` ASC';

    $raw_result mysql_query($sql) or die(mysql_error() . 'here');

    while(
    $item mysql_fetch_array($raw_result))
    {
        print 
    '<div style="margin-left: ' $item['level'] . 'em;">' $item['id'] . ' | ' $item['title'] . ' - ' $item['level'] . ' - {' $item['left_id'] . ', ' $item['right_id'] . '}</div>' "\n";
    }



    if(!
    $mptt->check_consistency($errors))
    {

        print 
    'Tree is NOT consistant!<pre>';
        
    var_dump($errors);
        print 
    '</pre>';
    }
    else
    {
        print 
    'Tree is consistant!';
    }
    ?>
    </body>
    </html>
    Attached Files Attached Files
    Last edited by marek_mar; 02-17-2006 at 06:17 PM.
    I'm not sure if this was any help, but I hope it didn't make you stupider.

    Experience is something you get just after you really need it.
    PHP Installation Guide Feedback welcome.

  • #2
    Regular Coder ralph l mayo's Avatar
    Join Date
    Nov 2005
    Posts
    951
    Thanks
    1
    Thanked 31 Times in 29 Posts
    A few cool things about MPTT:

    You can get breadcrumbs like at the top of these forum pages (CodingForums.com > :: Server side development > PHP > Post a PHP snippet) for a given id by iterating through something like this (pseudocode):
    Code:
    SELECT title
        FROM yourmptttable
        WHERE lft < id's lft AND rgt > id's rgt    -- all elements that contain id
        ORDER BY lft ASC    -- follow the tree
    Do the opposite to get all substituents, as in all replies to a topic or whatever:
    Code:
    SELECT titlefield   
        FROM yourmptttable
        WHERE lft > id's lft AND r < id's rgt    -- all elements contained by id
        ORDER BY lft ASC    
        LIMIT ((id's rgt - id's lft - 1)/2)    -- optional

  • #3
    Senior Coder
    Join Date
    Aug 2003
    Location
    One step ahead of you.
    Posts
    2,815
    Thanks
    0
    Thanked 3 Times in 3 Posts
    I added two new methods: mptt:copy() and mptt::check_consistency() and fixed a little bug with $child_num.
    I updated the first post.
    I'm not sure if this was any help, but I hope it didn't make you stupider.

    Experience is something you get just after you really need it.
    PHP Installation Guide Feedback welcome.

  • #4
    Senior Coder
    Join Date
    Aug 2003
    Location
    One step ahead of you.
    Posts
    2,815
    Thanks
    0
    Thanked 3 Times in 3 Posts
    It's about time this got updated. I always tell everyone to use this...
    • Requires PHP5.1.1 or newer now.
    • Added: This class now uses the internal_dbal mentioned in this post.
    • Added: Transactions. Transactions offer a more secure way of inserting updating the database, discarding the changes when something goes wrong.
    • Added class mppt_toolbox. It has some useful methods. This is still being worked on.
    • Added: mppt_toolbox::calc_levels(). If you had an MPPT tree but don't have the 'level' column then this function will fill in the values.
    • Added: configurable fields. The values that were previously constants are now configurable just like the config in my session class. mppt extends the config class to gain the functionality.
    • Fixed: mppt::add() would sometimes break the tree.
    • Moved: mppt::check_consistency() to mppt_toolbox::check_consistency()
    Attached Files Attached Files
    I'm not sure if this was any help, but I hope it didn't make you stupider.

    Experience is something you get just after you really need it.
    PHP Installation Guide Feedback welcome.

  • #5
    New to the CF scene
    Join Date
    Dec 2007
    Posts
    1
    Thanks
    0
    Thanked 0 Times in 0 Posts
    I encountered some problems, when I started to copy or move some trees into another.

    I then saw, that you do not order the children in the get_children-Method, but I think, this is crucible for the function to work properly.

    So add
    some "ORDER BY `left_id`" in your Code

    Thanks otherwise for your brilliant effort

  • #6
    Regular Coder ralph l mayo's Avatar
    Join Date
    Nov 2005
    Posts
    951
    Thanks
    1
    Thanked 31 Times in 29 Posts
    FYI this class is a huge minefield of race conditions that will go inconsistent eventually. Transactions actually make it worse by widening the window where the initial SELECT parts of the actions get branch dimensions that have changed by the time the INSERT/UPDATE parts are committed with the interpolated values. You basically have to lock the table for reads and writes through the duration of every writing action.
    Last edited by ralph l mayo; 12-20-2007 at 06:01 AM.

  • #7
    Senior Coder
    Join Date
    Jan 2007
    Posts
    1,648
    Thanks
    1
    Thanked 58 Times in 54 Posts
    You go through the effort of creating a class around this code, but then you still use global constants....

    Not to mention that this is using PHP4 OOP syntax.

  • #8
    New to the CF scene
    Join Date
    Sep 2009
    Posts
    1
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Ty very much for your nice piece of code
    very useful

    I have made some improvements (some useful functions from Cakephp, tree behaviour) wich I wanna share with you (Based in PHP4 version of your class):

    childcount()
    getpath()
    tree()
    generatetreelist()

    extends DB class, for queries and execution of SQL (http://slaout.linux62.org/php/index.html)

    mptt + bd class.zip

  • #9
    New to the CF scene
    Join Date
    Jul 2010
    Posts
    2
    Thanks
    0
    Thanked 0 Times in 0 Posts
    You should use the class,you are trying ti create, not global constants

  • #10
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    This code is nearly 5 years old. Class constants were not created until PHP 5.0 object overhaul. Therefore, the only way to create a class controlled constant set is to declare constants at a global scope (which all constants created by define are anyway). Putting it at the top outside of the class definition is much easier to find then inside a constructor for modification purposes.
    But yes, in a PHP5.0+ OO variation, you would used class named constants.
    PHP Code:
    header('HTTP/1.1 420 Enhance Your Calm'); 
    Been gone for a few months, and haven't programmed in that long of a time. Meh, I'll wing it ;)


  •  

    LinkBacks (?)

    1. 02-26-2014, 07:15 AM

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •