使用PHP和MySQL查询结果获取父级下所有子级、孙子级等节点

12
我一直在试图解决这个问题,但一无所获。希望有人能够帮助我。
我的问题是我正在使用邻接表数据模型在mysql中生成我的分层数据。我可以将下面的表检索到一个多维数组中,并为每个项使用关联数组。我的目标是,一旦获得此数组,我想获取另一个数组,其中包括父ID下的所有节点(子节点、孙子节点等),包括父项。我只是无法弄清楚如何在php中编写此代码。
在MySQL中,我的表如下所示:
id     name       parent_id
1  Electronics          0
2  Televisions          1
3  Portable Electronics 1
4  Tube                 2
5  LCD                  2
6  Plasma               2
7  Mp3 Players          3
8  CD Players           3
9  2 Way Radios         3
10 Flash                7

我可以使用以下代码将所有行检索为关联数组。

$r = mysql_query("SELECT * FROM test ");
        $data = array();
        while($row = mysql_fetch_assoc($r)) {
         $data[] = $row;
         }      

获得结果:

Array 
( 
    [0] => Array 
    ( 
        [id] => 1 
        [name] => Electronics 
        [parent_id] => 0 
    ) 
    [1] => Array 
    ( 
        [id] => 2 
        [name] => Televisions 
        [parent_id] => 1 
    ) 
    [2] => Array 
    ( 
        [id] => 3 
        [name] => Portable Electronics 
        [parent_id] => 1 
    )
    [3] => Array 
    (
        [id] => 4 
        [name] => Tube 
        [parent_id] => 2 
    )
    [4] => Array 
    (
        [id] => 5 
        [name] => LCD 
        [parent_id] => 2
    )
    [5] => Array
    (
        [id] => 6 
        [name] => Plasma 
        [parent_id] => 2
    )
    [6] => Array
    (
        [id] => 7 
        [name] => Mp3 Players 
        [parent_id] => 3 
    )
    [7] => Array 
    (
        [id] => 8 
        [name] => CD Players 
        [parent_id] => 3
    )
    [8] => Array 
    (
        [id] => 9 
        [name] => 2 Way Radios 
        [parent_id] => 3
    )
    [9] => Array
    (
        [id] => 10 
        [name] => Flash 
        [parent_id] => 7 
    ) 
)
用这些结果,我想通过一个ID来筛选它。
例如,如果我想要 Portable Electronics 下每个节点的关联数组,并且它们的 ID 为 3(代码中使用 ID),那么它将返回一个具有以下 ID 行的数组:
- 3 Portable Electronics(必须包括所选父级) - 7 Mp3 播放器(子级) - 8 CD 播放器(子级) - 9 双向收音机(子级) - 10 闪存(孙级)
如果 Flash 还有子级,则也会返回它们。
因此,最终结果将返回一个类似于上面的数组,但仅包含那些项。
请注意: 我不需要创建树结构的多维数组的函数(已经有解决方案了)。我想要构建一个函数:fetch_recursive($id),它接收一个 ID 并返回该级别及以下级别中的所有项目等等。
希望这可以帮助你。
提前感谢。

所以,你想要构建一个函数:fetch_recursive($id),它接收一个 ID 并返回该级别及以下所有项目。听起来像是一个简单的递归函数。你尝试过什么?我们能看到你的代码吗? - Nir Alfasi
是的,那就是我想要的,我尝试写了一些代码,但是没有任何进展 :| - Benny33
我现在明白你的意思了。看看我的修改后的解决方案。 - Zane Bien
2个回答

20

编辑:

我之前发布了一个解决方案,可以将你给出的输出构建成多维数组,并且可以从该特定数组中获取特定id的所有子元素。现在我已经找到了直接从你的输出中检索子元素的方法(无需先通过buildtree()函数进行):

function fetch_recursive($src_arr, $currentid, $parentfound = false, $cats = array())
{
    foreach($src_arr as $row)
    {
        if((!$parentfound && $row['id'] == $currentid) || $row['parent_id'] == $currentid)
        {
            $rowdata = array();
            foreach($row as $k => $v)
                $rowdata[$k] = $v;
            $cats[] = $rowdata;
            if($row['parent_id'] == $currentid)
                $cats = array_merge($cats, fetch_recursive($src_arr, $row['id'], true));
        }
    }
    return $cats;
}

要使用上述的函数,只需将输出数组$data作为第一个参数传递,并将您想要检索子元素的id作为第二个参数传递:

例如:

$list = fetch_recursive($data, 3);

这应该会给你正确的数组结构,用于id 3(就像在最后一个代码框的示例中看到的那样)。

原始回答:

直到现在,我还没有写过一个递归函数来将这个设计构建成嵌套树形结构。我相信有很多其他人已经写了类似的函数,但这个函数肯定可以为您工作:

function buildtree($src_arr, $parent_id = 0, $tree = array())
{
    foreach($src_arr as $idx => $row)
    {
        if($row['parent_id'] == $parent_id)
        {
            foreach($row as $k => $v)
                $tree[$row['id']][$k] = $v;
            unset($src_arr[$idx]);
            $tree[$row['id']]['children'] = buildtree($src_arr, $row['id']);
        }
    }
    ksort($tree);
    return $tree;
}

这个函数将通过邻接表递归地构建一棵树,并按升序保持id的顺序。这也使得每个父/子节点的id成为每个信息数组的键。

以下是代码:

$r = mysql_query("SELECT * FROM test ");
$data = array();
while($row = mysql_fetch_assoc($r)) {
    $data[] = $row;
}
echo '<pre>';
print_r(buildtree($data));
echo '</pre>';

会输出类似以下的内容:
Array 
(
    [1] => Array 
    (
        [id] => 1
        [name] => Electronics 
        [parent_id] => 0 
        [children] => Array
        (
            [2] => Array 
            ( 
                [id] => 2
                [name] => Televisions 
                [parent_id] => 1 
                [children] => Array
                (
                    [4] => Array 
                    (
                        [id] => 4
                        [name] => Tube 
                        [parent_id] => 2
                        [children] => Array()
                    )
                    [5] => Array 
                    (
                        [id] => 5
                        [name] => LCD 
                        [parent_id] => 2
                        [children] => Array()
                    )
                    [6] => Array
                    (
                        [id] => 6
                        [name] => Plasma 
                        [parent_id] => 2
                        [children] => Array()
                    )
                )
            )
            [3] => Array 
            (
                [id] => 3
                [name] => Portable Electronics 
                [parent_id] => 1
                [children] => Array
                (
                    [7] => Array
                    (
                        [id] => 7
                        [name] => Mp3 Players 
                        [parent_id] => 3 
                        [children] => Array
                        (
                            [10] => Array
                            (
                                [id] => 10
                                [name] => Flash 
                                [parent_id] => 7
                                [children] => Array()
                            ) 
                        )
                    )
                    [8] => Array 
                    (
                        [id] => 8
                        [name] => CD Players 
                        [parent_id] => 3
                        [children] => Array()
                    )
                    [9] => Array 
                    (
                        [id] => 9
                        [name] => 2 Way Radios 
                        [parent_id] => 3
                        [children] => Array()
                    )
                )
            )
        )
    )
)

为了将特定 id 的所有子节点放入一维数组中,您可以使用以下函数:
function fetch_recursive($tree, $parent_id, $parentfound = false, $list = array())
{
    foreach($tree as $k => $v)
    {
        if($parentfound || $k == $parent_id)
        {
            $rowdata = array();
            foreach($v as $field => $value)
                if($field != 'children')
                    $rowdata[$field] = $value;
            $list[] = $rowdata;
            if($v['children'])
                $list = array_merge($list, fetch_recursive($v['children'], $parent_id, true));
        }
        elseif($v['children'])
            $list = array_merge($list, fetch_recursive($v['children'], $parent_id));
    }
    return $list;
}

基于上面的buildtree()函数,假设我们想要获取id为3的所有子节点:

echo '<pre>';
print_r(fetch_recursive(buildtree($a), 3));
echo '</pre>';

这将输出:
Array
(
    [0] => Array
        (
            [id] => 3
            [name] => Portable Electronics
            [parent_id] => 1
        )

    [1] => Array
        (
            [id] => 7
            [name] => Mp3 Players
            [parent_id] => 3
        )

    [2] => Array
        (
            [id] => 10
            [name] => Flash
            [parent_id] => 7
        )

    [3] => Array
        (
            [id] => 8
            [name] => CD Players
            [parent_id] => 3
        )

    [4] => Array
        (
            [id] => 9
            [name] => 2 Way Radios
            [parent_id] => 3
        )

)

非常感谢,正是我想要的。你在这里发布的其他一些代码在几天前对我非常有帮助,无论如何,我相信它会对别人有用。再次感谢你详细的回复。干杯 - Benny33
这个工作正常。但我想知道在BFS中是否可以获取递归子节点。如果可以,请发布代码。 - ankitr
嗨Zane,你可以解释一下如何打印树吗? - Vipin Singh

0

这里有一种方法可以帮助你更进一步,你可以决定如何构建你的结果数组以及选择包含哪些字段。虽然这未经测试,但你应该能看到其逻辑。

// connect to db

// set id counter
$ids = 0;

// declare array
$categories = new Array();

// determine max ids
$query = mysql_query("SELECT COUNT(1) AS ids FROM test");
$result = mysql_fetch_array(query); // get result
$count = $result['ids'];

// loop through ids for parents
for($ids = 0; $ids <= $count; $ids++) {
  $query1 = mysql_query("SELECT * FROM test WHERE id = '" . $ids . "'");
  $query2 = mysql_query("SELECT id, name, parent_id FROM test WHERE parent_id = '" . $ids . "'");
  // check if has children
  if(mysql_num_rows($query2) > 0) {
    // iterate through children and add to array
    while (mysql_fetch_array($query2) as $row) {
      $categories[$ids]['child'][$row['id']] = $row['name'];
    }
  }
  // check if has siblings
  if(mysql_num_rows($query1) > 0) {
    // iterate through children and add to array
    while (mysql_fetch_array($query2) as $row) {
      $categories[$ids]['sibling'][$row['id']] = $row['name'];
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接