如何使用 PHP 和 MySQL 将父子(邻接)表转换为嵌套集?
在过去的几个小时里,我一直试图在网上找到这个问题的解决方案。我发现了很多关于如何从嵌套集转换为邻接的示例...但很少有人反其道而行之。我发现的示例要么不起作用,要么使用MySQL过程。不幸的是,我不能为这个项目使用过程。我需要一个纯PHP解决方案。
我有一个使用下面邻接模型的表:
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
我想将其转换为下面的嵌套集表:
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
以下是我需要的图片:
我有一个函数,基于这个论坛帖子(http://www.sitepoint.com/forums/showthread.php?t=320444)的伪代码,但它不起作用。我得到的多行对 left 具有相同的值。这不应该发生。
<?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;
}
?>
这是上述脚本的输出。
插入到 (, , , ) 值( 空, “2”, “5”, “硬封面” )
nested_table
id
lft
rgt
category
插入到 (, , , ) 值( NULL, '2', '7', 'Large Format' )
nested_table
id
lft
rgt
category
插入到 (, , , ) 值 ( NULL, '1', '8', 'Books' )
nested_table
id
lft
rgt
category
插入到 (, , , ) 值( 空, “1”, “10”, “CD”)
nested_table
id
lft
rgt
category
插入到 (, , , ) 值( 空, '10', '13', 'Vintage' )
nested_table
id
lft
rgt
category
插入到 (, , , ) 值( NULL, '1', '14', 'Magazines' )
nested_table
id
lft
rgt
category
插入到 (, , , ) 值( NULL, '0', '15', 'ROOT' )
nested_table
id
lft
rgt
category
如您所见,有多个行共享 lft 值“1”,“2”也是如此 在嵌套集中,left 和 right 的值必须是唯一的。下面是如何在嵌套集中手动对左右 ID 进行编号的示例:
图片来源:Gijs Van Tulder,参考文章