Я провел последние несколько часов, пытаясь найти решение этого вопроса в Интернете. Я нашел множество примеров того, как конвертировать из вложенного набора в смежность... но мало что происходит наоборот. Примеры, которые я нашел, либо не работают, либо используют процедуры 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), но это не работает. Я получаю несколько строк, имеющих одинаковое значение слева. Этого не должно быть.
<?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;
}
?>
Это результат работы выше script.
INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '2', '5', 'Hard Cover')INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '2', '7', 'Large Format')INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '1', '8', 'Books')INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '1', '10', 'CD \' s ')INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '10', '13', 'Vintage')INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '1', '14', 'Magazines')INSERT INTO
nested_table
(id
,lft
,rgt
,category
) ЦЕННОСТИ ( NULL, '0', '15', 'ROOT')
Как вы можете видеть, существует несколько строк, в которых значение lft равно "1", равно "2". В вложенном наборе значения для левого и правого должны быть уникальными. Ниже приведен пример того, как вручную ввести идентификатор слева и справа во вложенном наборе:
Image Credit: Gijs Van Tulder, ссылка на статью