PHP - 如何动态构建访问深度数组内容的路径

6
我正在尝试在PHP中构建分层数组,从使用闭包表存储的关系数据库内容中获取。对于给定的结果,我将拥有到LEAF节点的完整路径,下面是我的结果集。
1~root~根节点
1~root~根节点>>>2~category1~第一个类别
1~root~根节点>>>3~category2~第二个类别
1~root~根节点>>>2~category1~第一个类别>>>4~subCatOfCategory1~Cat 1的子类别
这些是我的数据库结果。所以我想遍历它们并在PHP中构建一个层次结构,以便我可以将其转换为JSON并在DOJO中呈现树形图。
因此,当我遍历每一行时,我正在构建一个“路径”到叶子,因为只有在元素是“叶子”时我才需要将元素添加到树中... 沿着这样的思路,我决定使用“>>>”作为分隔符标记化每个结果,这样我就得到了在该行中的节点。然后,我循环遍历这些节点,通过“~”标记化每个节点,从而得到每个节点的属性。
因此,我有一个for循环来处理每一行,它基本上确定正在处理的节点是否不是叶子节点,如果不是,则将其ID添加到跟踪最终处理的叶子节点的路径的数组中。然后,当我最终到达LEAF时,我可以调用一个函数来插入一个节点,使用我沿途编制的PATH。
希望这一切都说得清楚...所以我在下面包含了代码.. 考虑上述第二个结果。当我处理完整个结果并即将调用insertNodeInTreeV2()函数时,数组如下所示... $fullTree是一个具有1个元素的数组,索引为[1]。该元素包含四个元素:ID(1)NAME(root)Description(the root node)CHILDREN(empty array) $pathEntries是一个具有一个元素(1)的数组。这意味着插入LEAF节点的路径是通过节点[1],也就是根节点。 $nodeToInsert是一个具有四个元素的数组: ID(2), NAME(category1), Description(First Category), CHILDREN(empty array) $treeRootPattern是一个字符串,其中包含我用于存储整个数组/树的变量名,在这种情况下为"fullTree"。
private function insertNodeInTreeV2( array &$fullTree, array $pathEntries, array $nodeToInsert, $treeRootPattern )
{
  $compiledPath = null;
  foreach ( $pathEntries as $path ) {
    $compiledPath .= $treeRootPattern . '[' . $path . '][\'CHILDREN\']';
  }
  // as this point $compiledPath = "fullTree[1]['CHILDREN']"
  $treeVar = $$compiledPath;
}

当我进行赋值$treeVar = $$compiledPath;时,我认为我正在将变量$treeVar设置为等于$fullTree [1] ['CHILDREN'](我已经在调试器中验证了这是一个有效的数组索引)。即使我将$compiledPath的内容粘贴到Eclipse调试器的新表达式中,它也会显示一个空数组,这是有道理的,因为这就是$fullTree [1] ['CHILDREN']所在的位置。
但实际上,运行时告诉我以下错误...
troller.php的第85行-未定义的变量:fullTree [1] ['CHILDREN']
任何关于此的帮助都将不胜感激...如果您有更好的方法让我从我描述的结果集中获取我正在尝试构建的分层数组,我将非常愿意采用更好的方法。
已更新以添加调用上述函数的代码-循环处理数据库结果的行,如上所述。
foreach ( $ontologyEntries as $entry ) {

        // iterating over rows of  '1~~root~~The root node>>>2~~category1~~The first category
        $nodes = explode( '>>>', $entry['path'] );
        $numNodes = count( $nodes ) - 1 ;

        $pathToNewNode = null;  // this is the path, based on ID, to get to this *new* node
        for ( $level = 0; $level <= $numNodes; $level++ ) {

            // Parse the node out of the database search result
            $thisNode = array(
                'ID'          => strtok($nodes[$level], '~~'),  /*   1   */
                'NAME'        => strtok( '~~'),                 /*   Root   */
                'DESCRIPTION' => strtok( '~~'),                 /*   This is the root node   */
                'CHILDREN'    => array()
            );

            if ( $level < $numNodes ) {   // Not a leaf, add it to the pathToThisNodeArray
                $pathToNewNode[] = $thisNode['ID'];
            }
            else {
                // processing a leaf, add it to the array
                $this->insertNodeInTreeV2( $$treeRootPattern,  $pathToNewNode, $thisNode, $treeRootPattern );
            }

        }

    }

哇,这是一个巨大的问题!我有一种感觉,它可以被压缩成几段话 - 让读者更容易理解 - 然后将代码(包括第85行)放在其位置。 - halfer
不同的方法:在插入子树时,使用对笔记的引用而不是变量变量($$treeRootPattern)。如果您必须使用像 $foo["bar"] 这样的字符串访问变量,则无法避免使用 eval。请参见 http://stackoverflow.com/questions/5065294/can-array-values-be-accessed-by-variable-variables - Basti
解释错误信息:$fullTree[1]['CHILDREN'] 不是一个有效的变量名。只有 $fullTree 是变量名称,其余部分由 PHP 解释。$fullTree[1]['CHILDREN'] 的意思是:访问数组 $fullTree。进入索引 1 指向的内存中。将其解释为数组。进入索引 CHILDREN 指向的内存中。因此,首先根本不存在一个叫做 $fullTree[1]['CHILDREN'] 的变量,所以您无法通过变量变量来访问它。 :-) - Basti
@Kevin,谢谢。这是一个很难平衡的问题 - 在这里,信息不足的问题远比信息过多的问题常见得多!我的标准是问题特定信息的存在;当我提出问题时,我会尝试以一种方式概括它,使得情况(和任何答案)对尽可能广泛的受众都有兴趣。 - halfer
我听说过一项研究表明,人们的注意力越来越短。这可能与最近电视节目的组织方式有关(5-10分钟的专注时间,然后广告=休息)。受过高等教育的人应该不会对此感到困扰。 - Basti
显示剩余2条评论
1个回答

5
请参考我在您的问题下方的评论,以获得解释。
$paths = array(
    "1~root~the root node",
    "1~root~the root node>>>2~category1~First category",
    "1~root~the root node>>>3~category2~Second category",
    "1~root~the root node>>>2~category1~First category>>>4~subCatOfCategory1~SubCategory of Cat 1"
);

$tree = array();

foreach ($paths as $path)
{
    $currentNode = &$tree;

    $parts = explode(">>>", $path);

    foreach ($parts as $part)
    {
         $node = explode("~", $part);

         // create all nodes along this path
         if (!isset($currentNode[$node[0]]))
         {
              $currentNode[$node[0]] = array(
                "ID"            => $node[0],
                "NAME"          => $node[1],
                "DESCRIPTION"   => $node[2],
                "CHILDREN"      => array(),
              );
         }

         $currentNode = &$currentNode[$node[0]]["CHILDREN"];
    }
}

var_dump($tree);

输出:

array
  1 => 
    array
      'ID' => string '1' (length=1)
      'NAME' => string 'root' (length=4)
      'DESCRIPTION' => string 'the root node' (length=13)
      'CHILDREN' => 
        array
          2 => 
            array
              'ID' => string '2' (length=1)
              'NAME' => string 'category1' (length=9)
              'DESCRIPTION' => string 'First category' (length=14)
              'CHILDREN' => 
                array
                  4 => 
                    array
                      'ID' => string '4' (length=1)
                      'NAME' => string 'subCatOfCategory1' (length=17)
                      'DESCRIPTION' => string 'SubCategory of Cat 1' (length=20)
                      'CHILDREN' => &
                        array
                          empty
          3 => 
            array
              'ID' => string '3' (length=1)
              'NAME' => string 'category2' (length=9)
              'DESCRIPTION' => string 'Second category' (length=15)
              'CHILDREN' => 
                array
                  empty

循环将创建路径中包含的所有节点,因此如果您还插入了“1~root~根节点>>>2~category1~第一个类别”,则无需插入“1~root~根节点”。您可以通过仅在节点是路径的最后一个节点时创建节点来更改此内容。路径的长度为“count($parts)”,您可以在内部foreach循环中计算您所在的级别。希望这符合您的要求。

饼屑,我认为你阅读了整个问题,值得+5!干得好,+1。 - halfer
谢谢!解决这个问题非常有趣。 :-) - Basti
Basti,非常感谢。我讨厌把事情搞得过于复杂化。它的表现完全符合预期。我感谢你的回答。我无法投票支持你的回答,因为我还没有足够的声望点数。 - rogodeter

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