Zebra_Mptt, a PHP class providing an implementation of the modified preorder tree traversal algorithm

Get the latest updates on this PHP library via RSS

What is Modified Preorder Tree Traversal?

MPTT is a fast algorithm for storing hierarchical data (like categories and their subcategories) in a relational database. This is a problem that most of us have had to deal with, and for which we’ve used an adjacency list, where each item in the table contains a pointer to its parent and where performance will naturally degrade with each level added as more queries need to be run in order to fetch a subtree of records.

The aim of the modified preorder tree traversal algorithm is to make retrieval operations very efficient. With it you can fetch an arbitrary subtree from the database using just two queries. The first one is for fetching details for the root node of the subtree, while the second one is for fetching all the children and grandchildren of the root node.

The tradeoff for this efficiency is that updating, deleting and inserting records is more expensive, as there’s some extra work required to keep the tree structure in a good state at all times. Also, the modified preorder tree traversal approach is less intuitive than the adjacency list approach because of its algorithmic flavour.

For more information about the modified preorder tree traversal method, read this excellent article called Storing Hierarchical Data in a Database Article.

What is Zebra_MPTT?

Zebra_MPTT is a PHP class that provides an implementation of the modified preorder tree traversal algorithm making it easy for you to use MPTT in your PHP applications.

It provides methods for adding nodes anywhere in the tree, deleting nodes, moving and copying nodes around the tree and methods for retrieving various information about the nodes.

Zebra_MPTT uses table locks making sure that database integrity is always preserved and that concurrent MySQL sessions don’t compromise data integrity. Also, Zebra_MPTT uses a caching mechanism which has as result the fact that regardless of the type, or the number of retrieval operations, the database is read only once per script execution!


Features review

  • provides methods for adding nodes anywhere in the tree, deleting nodes, moving and copying nodes around the tree and methods for retrieving various information about the nodes
  • uses a caching mechanism which has as result the fact that regardless of the type, or the number of retrieval operations, the database is read only once per script execution
  • uses table locks making sure that database integrity is always preserved and that concurrent MySQL sessions don’t compromise data integrity
  • use the mysqli extension
  • has comprehensive documentation
  • code is heavily commented and generates no warnings/errors/notices when PHP’s error reporting level is set to E_ALL


PHP 5.0.0+, MySQL 4.1.22+



Download the latest version, unpack it, and put it in a place accessible to your scripts. After unpacking, you will notice a directory called “install” containing a file named “mptt.sql”. This file contains the SQL code that will create a table that is used by the class to store the data. Import or execute the SQL code using your preferred MySQL manager (like phpMyAdmin or the fantastic Adminer) into a database of your choice.


How to use


// include the Zebra_Mptt class
require 'path/to/Zebra_Mptt.php';

// instantiate a new object
$mptt = new Zebra_Mptt();

// populate the table

// add 'Food' as a topmost node
$food = $mptt->add(0, 'Food');

// 'Fruit' and 'Meat' are direct descendants of 'Food'
$fruit = $mptt->add($food, 'Fruit');
$meat = $mptt->add($food, 'Meat');

// 'Red' and 'Yellow' are direct descendants of 'Fruit'
$red = $mptt->add($fruit, 'Red');
$yellow = $mptt->add($fruit, 'Yellow');

// add a fruit of each color
$mptt->add($red, 'Cherry');
$mptt->add($yellow, 'Banana');

// add two kinds of meat
$mptt->add($meat, 'Beef');
$mptt->add($meat, 'Pork');

// move 'Banana' to 'Meat'
$mptt->move($banana, $meat);

// get a flat array of descendants of 'Meat'

// get a multidimensional array (a tree) of all the data in the database




version 2.3.2

If you find this library to be useful to you, you can support the author by donating a small amount via PayPal:

Alternatively, you could show your support by starring this library on GitHub

Zebra_Mptt is distributed under the LGPL.

In plain English, this means that you have the right to view and to modify the source code of this software, but if you modify and distribute it, you are required to license your copy under a LGPL-compatible license, and to make the entire source code of your derivation available to anybody you distribute the software to.

You also have the right to use this software together with software that has different licensing terms (including, but not limited to, commercial and closed-source software), and distribute the combined software, as long as state that your software contains portions licensed under the LGPL license, and provide information about where the LGPL licensed software can be downloaded.

If you distribute copies of this software you may not change the copyright or license of this software.

You may also like:



Documentation Become a ninja.
Read the comprehensive documentation.


Click on a version to expand/collapse information.

version 2.3.2 (January 19, 2016)
  • fixed an issue with auto-loading when installed via Composer
  • updated references to the minimum required PHP version
version 2.3.0 (January 19, 2016)
  • this version breaks compatibility with previous versions
  • “get_children_count” and “get_descendats_count” methods were both replaced by the new get_descendant_count method
  • “get_selectables” method was renamed to to_select
  • added 3 new methods: get_siblings, get_next_sibling and get_previous_sibling
  • the move method can now be used to move nodes before and after another nodes (not just as a child of another node)
  • dropped support for the deprecated “mysql” extension; the library now only works with the “mysqli” extension
  • minimum required PHP version is now 5.0.5 instead of 4.4.9
  • fixed some issues preventing the library from running pn PHP7; thanks Jiri Melcak
  • improved compatibility with Composer
  • some minor performance tweaks
version 2.2.5 (November 14, 2013)
  • added a new update method, useful if you want to change a node’s name;
version 2.2.4 (September 14, 2013)
  • fixed a bug with the to_list method when custom column names were used; thanks hendra;
version 2.2.3 (July 14, 2013)
  • the “get_parent” method now returns all the properties of the parent node and not just the ID; thanks Edgar Veiga;
  • added a new method: to_list which can be used to generate HTML ordered or unordered lists for a given node; thanks to Dino Sane Moreira for suggesting;
  • project is now available on GitHub and as a package for Composer
version 2.2.2 (November 06, 2012)
  • fixed a bug where the last node returned by the “get_path” method did not have the correct key; thanks to Gus;
version 2.2.1 (July 19, 2012)
  • fixed a bug that escaped fixing in the previous release, where the get_selectables() method was triggering a “Strict Standards” notice in PHP 5.3+; thanks to mrplugins
  • fixed a bug where the “copy” method was not working correctly; thanks to Victor Hugo Contreras
version 2.2 (January 20, 2012)
  • fixed an issue with some constructs in the code that would trigger a “Strict standards: Only variables should be passed by reference” warnings in PHP 5.3+; thanks Juan Gutierrez
version 2.1 (June 15, 2011)
  • fixed a bug where some of the methods were not working anymore if custom column names were used for the MySQL table (thanks to hisham for reporting);
version 2.0 (June 11, 2011)
  • entire code was audited and improved;
  • added new methods;
  • many documentation refinements;
version 1.0 (July 22, 2009)
  • initial release;


79 responses to “Zebra_Mptt, a PHP class providing an implementation of the modified preorder tree traversal algorithm”

Follow the comments via RSS
  • Dino Sane Moreira, 2013-05-27, 21:44

    I believe this would be useful:

    function array2ul($array) {
        foreach($array as $key => $elem){
                $out = $out."<li>" . $elem['title'] . "</li>";
            } else  {
                $out = $out."<li>" .$elem['title'] . array2ul($elem['children']) . "</li>";
        return $out;
    echo array2ul($mptt->get_tree());
    • Stefan Gabos, 2013-05-29, 07:12

      thanks! that’s indeed something that could be useful. I just modified the code a bit so now it is

      function array2ul($array) {
        $out = '<ul>';
        foreach ($array as $key => $elem)
          $out .= '<li>' .
          $elem['title'] .
          (is_array($elem['children']) ? 
            array2ul($elem['children']) : 
          ) .
        return $out . '</ul>';
    • Peter, 2015-01-23, 15:31

      how to create the output like this:http://i1.wp.com/zacharyflower.com/wordpress/wp-content/uploads/2014/06/Photo-Jun-13-10-51-35-AM.jpg


  • hendra, 2013-09-14, 14:52

    error when use to_list()
    i believe the line 1385 you should change the $elem[‘title’] to the properties where user has copy the object for it…

    as example when my column is not ‘title’ but ‘name’ it would return error

    • Stefan Gabos, 2013-09-14, 21:06

      it’s fixed now. thanks a lot!

  • gary, 2013-09-24, 21:29

    Came across this class while looking at your image class, I like the mptt class (and am going to use it) but I needed some additional functionality so I went to extend it and that is when I realized you are using deprecated database functions. Why not use your own database class.

    In fact since I do need this class (unless you plan to) I am going to modify it to use your database class.

    Thanks for your time

    • Stefan Gabos, 2013-09-24, 21:56

      I will update the class to use mysqli instead of mysql but will not make it depend on Zebra_Database; everything should stay as decoupled as possible so that it’s easier to drop it in any project without forcing users to also use Zebra_Database

  • Thiago Fernandes, 2014-01-23, 22:28

    Hi.. Stefan I’m wondering if having all item in the lookup array will consume too much memory. Willing to implement some filtering in the query so it only looks for the necessary items.

    But not sure if that would work in your class context or even Mptt at all (sorry if I’m speaking bullshit).

    What do you think, is that kind of thinking correct? Is it possible to work with less items in the lookup?

    • Stefan Gabos, 2014-02-07, 11:15

      if you have many many items in your database, then it’s probably not a good idea to have everything in memory; if you have up to 100 or so, you are safe

    • Thiago Fernandes, 2014-03-18, 22:51

      I’m going to have more than 100.. surelly thousands. I’m planning to start storing the array in memcached to later change to a complete database solution. Not sure how much memcache will hold…

  • Daniele Leoni, 2014-02-10, 12:04

    Great work!! I really appreciate your class.
    I’m trying to integrate in codeigniter and it’s going well.

    I would like to suggest you to improve the to_list method and add two vars: current_position and prev_node.
    function get_list($node, $list_type = 'ul', $attributes = '', $current_position = 0, $prev_node = 0)

    They are really useful to retrieve parameters and move node.
    Your method move supposed to know position before moving.


  • Daniele Leoni, 2014-02-10, 12:05

    Or maybe methods for
    move up, down, left and right…

    • Stefan Gabos, 2014-02-10, 18:50

      i do plan on adding methods for moving a node up and down (there’s no left or right, though)

    • Daniele Leoni, 2014-02-11, 12:47

      Thank you very much!! For left and right I mean indent a node
      Recently I fully integrated in codeigniter
      Something like this


    • Stefan Gabos, 2014-02-11, 13:19

      “left” means “up” while “right” means “down”…

  • Robert, 2014-02-11, 23:57

    Hi, get_selectables() gives me list with all nodes, i want a list without root node(with parent 0). When i type get_selectables(1) or get_selectables(2) it still gives me list with all nodes.

    • Stefan Gabos, 2014-05-30, 15:54

      I am unable to reproduce that

    • Robert, 2014-07-26, 00:49

      no matter, I worked it out differently, I do not remember how, but my application is completed, thanks for nice library

  • Serge, 2014-12-21, 00:14

    Hello Stephan congratulations for your work,
    would it be possible to you to stop the example loop on the levels of 5
    it is for a pyramidal system or each sponsor receives a recursion on 5 levels? and if possible to detect levels

    Thank you for your answer

    • Serge, 2014-12-21, 00:16

      and pass good holidays

  • Hendra, 2015-10-07, 08:01

    any plan to update this class to be compatible with mysqli ??

    • Stefan Gabos, 2016-02-29, 12:09

      Done, thanks!

Leave a Reply

Your email address will not be published
You can use <strong>, <em>, <a>, <img>, <code>
Characters are not case-sensitive