6

クロージャー テーブルを使用して格納されているリレーショナル データベースのコンテンツから、PHP で階層配列を構築しようとしています。特定の結果については、ノードへのフル パスLEAFが表示されます。以下は結果セットのようになります。

1~ルート~ルートノード

1~ルート~ルートノード>>>2~category1~最初のカテゴリ

1~ルート~ルートノード>>>3~category2~2番目のカテゴリ

1~ルート~ルートノード>>>2~category1~最初のカテゴリ>>>4~subCatOfCategory1~Cat 1のSubCategory

とにかく、それらは私のデータベースの結果です。だから私はそれらをトラバースし、PHP で階層構造を構築して、それを JSON に変換し、DOJO でツリーをレンダリングできるようにしたいと考えています。

要素が「リーフ」で​​ある場合にのみツリーに要素を追加する必要があるため、各行をたどってリーフへの「パス」を構築しています...その考えに沿って、各結果をトークン化することにしました区切り文字として ">>>" を使用すると、その行にあるノードが表示されます。次に、これらのノードをループして、各ノードの属性を取得する「~」で各ノードをトークン化します。

したがって、各 ROW を処理するための for ループがあり、基本的に、処理されるノードがリーフではない場合、その ID を配列に追加して、処理される最終的なリーフに到達するためのパスを追跡することを決定します。その後、最終的に LEAF に到達したら、途中でコンパイルした PATH を使用して、ノードを挿入する関数を呼び出すことができます。

うまくいけば、すべてが理にかなっています..だから私は以下のコードを含めました..上記の2番目の結果を考えてみましょう。その結果全体を処理し、関数 insertNodeInTreeV2() を呼び出そうとすると、配列は次のようになります...

$fullTree[1] でインデックス付けされた 1 つの要素を持つ配列です。その要素には、次の 4 つの要素を持つ配列が含まれます: ID(1)NAME(root)Description(the root node)CHILDREN(empty array)

$pathEntries要素が 1 つのみ (1) の配列です。つまり、挿入される LEAF ノードへの PATH は、ルート ノードであるノード [1] によるものです。

$nodeToInsertID(2)NAME(category1)Description(First Category)、 の4 つの要素を持つ配列です。CHILDREN(empty array)

$treeRootPatternは、配列/ツリー全体を格納するために使用している変数名を含む STRING です。この場合は「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 デバッガーの新しい Expression に貼り付けても、空の配列が表示されます。これは $fullTree[1]['CHILDREN'] にあるため、意味があります。

しかし、代わりに、ランタイムは次のエラーを教えてくれます...

troller.php 行 85 - 未定義の変数: fullTree[1]['CHILDREN']

これに関するヘルプは大歓迎です...そして、私が構築しようとしている階層配列に説明した結果セットから取得するためのより良い方法があれば、より良い方法を採用したいと思っています。

上記の関数を呼び出すコードを追加するために更新 -- for ループは、上記のようにデータベース結果の行を処理します。

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 );
            }

        }

    }
4

1 に答える 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~the root nodeも挿入する場合はを挿入する必要はありません1~root~the root node>>>2~category1~First category

ノードがパスの最後のノードである場合にのみ、ノードを作成することでこれを変更できます。パスの長さはcount($parts)であり、内側の foreach ループ内のどのレベルにいるかを数えることができます。

これがあなたの望むものであることを願っています。

于 2012-04-04T23:30:18.617 に答える