Como você converte uma tabela pai-filho (adjacência) em um conjunto aninhado usando PHP e MySQL?

Passei as últimas horas tentando encontrar a solução para esta pergunta online. Eu encontrei muitos exemplos de como converter um conjunto aninhado em adjacência ... mas poucos que são o contrário. Os exemplos que encontrei não funcionam ou usam procedimentos MySQL. Infelizmente, não posso usar procedimentos para este projeto. Eu preciso de uma solução PHP pura.

Eu tenho uma tabela que usa o modelo de adjacência abaixo:

id          parent_id         category
1           0                 Books
2           0                 CD's
3           0                 Magazines
4           1                 Books/Hardcover
5           1                 Books/Large Format
6           3                 Magazines/Vintage

E gostaria de convertê-lo para uma tabela Nested Set abaixo:

id    left    right          category
0     1       14             Root Node
1     2       7              Books
4     3       4              Books/Hardcover
5     5       6              Books/Large Format
2     8       9              CD's
3     10      13             Magazines
6     11      12             Magazines/Vintage

Aqui está uma imagem do que eu preciso:

Eu tenho uma função, com base no pseudo-código desta postagem do fórum (http://www.sitepoint.com/forums/showthread.php?t=320444) mas não funciona. Eu recebo várias linhas que têm o mesmo valor para a esquerda. Isso não deveria acontecer.

<?php

/**

--
-- Table structure for table `adjacent_table`
--

CREATE TABLE IF NOT EXISTS `adjacent_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `father_id` int(11) DEFAULT NULL,
  `category` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;

--
-- Dumping data for table `adjacent_table`
--

INSERT INTO `adjacent_table` (`id`, `father_id`, `category`) VALUES
(1, 0, 'ROOT'),
(2, 1, 'Books'),
(3, 1, 'CD''s'),
(4, 1, 'Magazines'),
(5, 2, 'Hard Cover'),
(6, 2, 'Large Format'),
(7, 4, 'Vintage');

--
-- Table structure for table `nested_table`
--

CREATE TABLE IF NOT EXISTS `nested_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `lft` int(11) DEFAULT NULL,
  `rgt` int(11) DEFAULT NULL,
  `category` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

*/

    mysql_connect('localhost','USER','PASSWORD') or die(mysql_error());
    mysql_select_db('DATABASE') or die(mysql_error());
    adjacent_to_nested(0);

    /**
     * adjacent_to_nested
     *
     * Reads a "adjacent model" table and converts it to a "Nested Set" table.
     * @param   integer     $i_id           Should be the id of the "root node" in the adjacent table;
     * @param   integer     $i_left         Should only be used on recursive calls.  Holds the current value for lft
     */
    function adjacent_to_nested($i_id, $i_left = 0)
    {

        // the right value of this node is the left value + 1
        $i_right = $i_left + 1;

        // get all children of this node
        $a_children = get_source_children($i_id);
        foreach  ($a_children as $a)
        {

            // recursive execution of this function for each child of this node
            // $i_right is the current right value, which is incremented by the 
            // import_from_dc_link_category method
            $i_right = adjacent_to_nested($a['id'], $i_right);

            // insert stuff into the our new "Nested Sets" table
            $s_query = "
                INSERT INTO `nested_table` (`id`, `lft`, `rgt`, `category`) 
                VALUES(
                    NULL, 
                    '".$i_left."',
                    '".$i_right."',
                    '".mysql_real_escape_string($a['category'])."'
                )
            ";
            if (!mysql_query($s_query))
            {
                echo "<pre>$s_query</pre>\n";
                throw new Exception(mysql_error());  
            }
            echo "<p>$s_query</p>\n";

            // get the newly created row id
            $i_new_nested_id = mysql_insert_id();

        }

        return $i_right + 1;
    }



    /**
     * get_source_children
     *
     * Examines the "adjacent" table and finds all the immediate children of a node
     * @param   integer     $i_id           The unique id for a node in the adjacent_table table
     * @return  array                       Returns an array of results or an empty array if no results.
     */
    function get_source_children($i_id)
    {


        $a_return = array();
        $s_query = "SELECT * FROM `adjacent_table` WHERE `father_id` = '".$i_id."'";
        if (!$i_result = mysql_query($s_query))
        {
            echo "<pre>$s_query</pre>\n";
            throw new Exception(mysql_error());  
        }
        if (mysql_num_rows($i_result) > 0)
        {
            while($a = mysql_fetch_assoc($i_result))
            {
                $a_return[] = $a;
            }
        }

        return $a_return;
    }

?>

Esta é a saída do script acima.

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALORES (NULL, '2', '5', 'Capa Dura')

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALUES (NULL, '2', '7', 'Grande formato')

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALUES (NULL, '1', '8', 'Livros')

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALUES (NULL, '1', '10', 'CD \' s ')

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALUES (NULL, '10', '13', 'Vintage')

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALUES (NULL, '1', '14', 'Revistas')

INSIRA DENTRO DEnested_table (id, lft, rgt, category) VALORES (NULL, '0', '15', 'ROOT')

Como você pode ver, existem várias linhas compartilhando o valor lft de "1" e o mesmo vale para "2" Em um conjunto aninhado, os valores para esquerda e direita devem ser exclusivos. Aqui está um exemplo de como numerar manualmente os IDs esquerdo e direito em um conjunto aninhado:

Crédito de imagem: Gijs Van Tulder,artigo ref

questionAnswers(2)

yourAnswerToTheQuestion