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