1

データベースのテーブルからツリー構造を生成しようとしています。テーブルはフラットに格納され、各レコードはparent_idまたは0のいずれかです。最終的な目標は、選択ボックスとノードの配列を生成することです。

私がこれまでに持っているコードは次のとおりです。

function init($table, $parent_id = 0) 
{

    $sql = "SELECT id, {$this->parent_id_field}, {$this->name_field} FROM $table WHERE {$this->parent_id_field}=$parent_id ORDER BY display_order";

    $result = mysql_query($sql);

    $this->get_tree($result, 0);

    print_r($this->nodes);
    print_r($this->select);
    exit;
}

function get_tree($query, $depth = 0, $parent_obj = null)
{   
    while($row = mysql_fetch_object($query))
    {   
        /* Get node */
        $this->nodes[$row->parent_category_id][$row->id] = $row;

        /* Get select item */
        $text = "";
        if($row->parent_category_id != 0) {
            $text .= "    ";
        }
        $text .= "$row->name";
        $this->select[$row->id] = $text;

        echo "$depth $text\n";

        $sql = "SELECT id, parent_category_id, name FROM product_categories WHERE parent_category_id=".$row->id." ORDER BY display_order";

        $nextQuery = mysql_query($sql);
        $rows = mysql_num_rows($nextQuery);

        if($rows > 0) {
            $this->get_tree($nextQuery, ++$depth, $row);
        }            
    }
}

ほとんど機能していますが、完全ではありません。誰か私がそれを終わらせるのを手伝ってくれますか?

4

3 に答える 3

4

ほぼ間違いなく、現在の道を歩み続けるべきではありません。使用しようとしている再帰的な方法は、ツリーが少しでも大きくなると、ほぼ確実にパフォーマンスを低下させます。ツリーを頻繁に読み取る予定がある場合は、隣接リストではなく、ネストされたセット構造を確認する必要があります。

ネストされたセットを使用すると、適切にネストされたツリー全体を 1 回のクエリで簡単に取得できます。

木の議論については、これらの質問を参照してください。

単一のクエリで MySQL のツリー構造テーブルを任意の深さまでクエリすることは可能ですか?

データベースに階層データ構造を実装する

フラットテーブルをツリーに解析する最も効率的/エレガントな方法は何ですか?

于 2009-03-10T19:00:17.670 に答える
1
    $this->nodes[$row->parent_category_id][$row->id] = $row;

この行は、ORDER BY display_order を破壊しています。に変更します

    $this->nodes[$row->parent_category_id][] = $row;

私の次の問題は、その $row->parent_category_id 部分です。$row->parent_id だけでいいのではないですか?

編集:ああ、私はあなたのソースを十分に読んでいませんでした. WHERE 句を削除します。テーブル全体を一度に読み取ります。ツリーをもう一度後処理する必要があります。まず、データベースを配列のリストに読み込みます。次に、配列を再帰的に処理して出力を行います。

配列は次のようになります。

 Array(0 => Array(1 => $obj, 5 => $obj), 
       1 => Array(2 => $obj),
       2 => Array(3 => $obj, 4 => $obj),
       5 => Array(6 => $obj) );

 function display_tree() {
      // all the stuff above
      output_tree($this->nodes[0], 0); // pass all the parent_id = 0 arrays.
 }

 function output_tree($nodes, $depth = 0) {
     foreach($nodes as $k => $v) {
         echo str_repeat(' ', $depth*2) . $v->print_me();
         // print my sub trees
         output_tree($this->nodes[$k], $depth + 1);
     }
 }

 output:
 object 1
   object 2
     object 3
     object 4
 object 5
   object 6
于 2009-03-10T16:56:32.553 に答える
0

ここにこの行があると思います:

if($row->parent_category_id != 0) {
    $text .= "    ";
}

次のようにする必要があります。

while ($depth-- > 0) {
    $text .= "    ";
}

インデントする必要がある回数ではなく、1回だけインデントしています。

そして、この行:

$this->get_tree($nextQuery, ++$depth, $row);

次のようにする必要があります。

$this->get_tree($nextQuery, $depth + 1, $row);

ただし、おそらく他の回答のアドバイスに従い、テーブル全体を一度に取得してから一度に処理する必要があることに注意してください。これは、通常、データベースへのラウンドトリップを最小限に抑えたいためです (非常に大きなツリーがあり、その小さな部分を選択している場合など、あなたがやっている方法はより最適ですが、ここではそうではないと思います)

于 2009-03-10T18:21:35.720 に答える