Easiest way to build a tree from a list of Ancestors

closures, mysql, php, tree

Solution

The first key is to sort the SQL results by the number of ancestors. I did this in PHP since I avoid the complexities of multi-digit numbers.

This provides a list of nodes in an order in which they can be validly inserted.

Array
(
    [1] => Array
        (
            [0] => 1
        )

    [4] => Array
        (
            [0] => 4
            [1] => 1
        )

    [2] => Array
        (
            [0] => 2
            [1] => 1
        )

    [3] => Array
        (
            [0] => 3
            [1] => 1
            [2] => 2
        )

)

At this point, I don't care about the keys, only the lists of ancestors. The path through the tree can be found between the intersection of available nodes and the remaining ancestors.

  function add_node($ancestors, &$tree) {
    if (count($ancestors) == 1) {
      $tree[array_pop($ancestors)] = array();
      return;
    }   
    $next_node = array_intersect($ancestors, array_keys($tree));
    $this->add_node(
        array_diff($ancestors, $next_node) , 
        $tree[array_pop($next_node)]
        );  
  }

Problem

In my heart, I feel that there must be a super simple recursive solution to this, but I cannot immediately grok it. I have a tree stored in SQL as a closure table. The tree looks like: (1 (2 (3), 4)), and the languages are MySQL's SQL and PHP 5.3. The closure table is thus: ``` +----------+------------+ | ancestor | descendant | +----------+------------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | | 1 | 2 | | 1 | 3 | | 1 | 4 | | 2 | 3 | +----------+------------+ ``` I can query the ancestors quite easily with: ``` SELECT descendant AS id, GROUP_CONCAT(ancestor) as ancestors FROM closure GROUP BY (descendant); +----+-----------+ | id | ancestors | +----+-----------+ | 1 | 1 | | 2 | 2,1 | | 3 | 3,1,2 | | 4 | 4,1 | +----+-----------+ ``` How can I easily build a tree in PHP with this data? Can I use a smarter query to pull more of the data from MySQL?

Original source