親子関係 - php 再帰関数 return



クエリをループせずにカテゴリとサブカテゴリのPHPツリー構造 (3)

私は、サブカテゴリが独自のサブカテゴリを持つことができるサブカテゴリをいくつでも持つカテゴリのリストを作成しようとしています。

私はMySQLデータベースからすべてのカテゴリを選択しました、ネコは標準的な関連配列のリストにあり、各カテゴリにはid、name、parentidがあります。

私は基本的に猫の単一レベルの配列を取ってそれを多次元配列構造に変えることができたいと思っています。それぞれのカテゴリはサブキャットの配列を含む要素を持つことができます。

さて、私は簡単に各カテゴリのクエリをループすることでこれを達成することができますが、これは理想から遠いです、私はDB上で余分なヒットなしでそれをしようとしています。

私はこのために再帰的な関数が必要であることを理解しています。 誰もがこのツリースタイルの構造の正しい方向に私を指すことができますか?

乾杯


Answer #1

これは仕事です:

$items = array(
        (object) array('id' => 42, 'parent_id' => 1),
        (object) array('id' => 43, 'parent_id' => 42),
        (object) array('id' => 1,  'parent_id' => 0),
);

$childs = array();

foreach($items as $item)
    $childs[$item->parent_id][] = $item;

foreach($items as $item) if (isset($childs[$item->id]))
    $item->childs = $childs[$item->id];

$tree = $childs[0];

print_r($tree);

これは、最初にparent_idでカテゴリを索引付けすることによって機能します。 それから各カテゴリについて、 category->childschilds[category->id]に設定するだけで、ツリーが構築されます!

ですから、 $treeはカテゴリツリーです。 parent_id = 0の項目の配列を含んでいます。これらの項目自体には子要素の配列が含まれていますが、それ自体は...

print_r($tree)出力:

stdClass Object
(
    [id] => 1
    [parent_id] => 0
    [childs] => Array
        (
            [0] => stdClass Object
                (
                    [id] => 42
                    [parent_id] => 1
                    [childs] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [id] => 43
                                    [parent_id] => 42
                                )

                        )

                )

        )

)

ここに最終的な関数があります:

function buildTree($items) {

    $childs = array();

    foreach($items as $item)
        $childs[$item->parent_id][] = $item;

    foreach($items as $item) if (isset($childs[$item->id]))
        $item->childs = $childs[$item->id];

    return $childs[0];
}

$tree = buildTree($items);

同じバージョンですが、配列を使っていますが、これは参照を使って演奏する必要があるので少し難解です(でも同様です):

$items = array(
        array('id' => 42, 'parent_id' => 1),
        array('id' => 43, 'parent_id' => 42),
        array('id' => 1,  'parent_id' => 0),
);

$childs = array();
foreach($items as &$item) $childs[$item['parent_id']][] = &$item;
unset($item);

foreach($items as &$item) if (isset($childs[$item['id']]))
        $item['childs'] = $childs[$item['id']];
unset($item);

$tree = $childs[0];

したがって、最後の関数の配列バージョン:

function buildTree($items) {

    $childs = array();

    foreach($items as &$item) $childs[$item['parent_id']][] = &$item;
    unset($item);

    foreach($items as &$item) if (isset($childs[$item['id']]))
            $item['childs'] = $childs[$item['id']];

    return $childs[0];
}

$tree = buildTree($items);

Answer #2

メソッドを参照してください:

function buildTree(array &$elements, $parentId = 0) {

    $branch = array();    
    foreach ($elements as $element) {
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[$element['id']] = $element;
        }
    }
    return $branch;
}

Answer #3

私は同じ問題を抱えてこのように解決しました。DBから猫の行を取り出し、それぞれのルートカテゴリに対して、レベル(深さ)0からツリーを構築します。最も効率的な解決法ではないかもしれませんが、私のために働きます。

$globalTree = array();
$fp = fopen("/tmp/taxonomy.csv", "w");

// I get categories from command line, but if you want all, you can fetch from table
$categories = $db->fetchCol("SELECT id FROM categories WHERE parentid = '0'");

foreach ($categories as $category) {
    buildTree($category, 0);
    printTree($category);
    $globalTree = array();
}

fclose($file);

function buildTree($categoryId, $level)
{
    global $db, $globalTree;
    $rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId);
    $childNodes = $db->fetchAll("SELECT * FROM categories WHERE parentid = ? AND id <> ? ORDER BY id", array($rootNode['id'], $rootNode['id']));
    if(count($childNodes) < 1) {
        return 0;
    } else {
        $childLvl = $level + 1;
        foreach ($childNodes as $childNode) {
            $id = $childNode['id'];
            $childLevel = isset($globalTree[$id])? max($globalTree[$id]['depth'], $level): $level;
            $globalTree[$id] = array_merge($childNode, array('depth' => $childLevel));
            buildTree($id, $childLvl);
        }
    }
}

function printTree($categoryId) {
    global $globalTree, $fp, $db;
    $rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId);
    fwrite($fp, $rootNode['id'] . " : " . $rootNode['name'] . "\n");
    foreach ($globalTree as $node) {
        for ($i=0; $i <= $node['depth']; $i++) {
            fwrite($fp, ",");
        }
        fwrite($fp, $node['id'] " : " . $node['name'] . "\n");
    }
}

ps。 私はOPがDBクエリなしのソリューションを探していることを認識していますが、これは再帰を伴い、このタイプの質問に対する再帰的ソリューションを探すこの質問を遭遇した人を助け、DBクエリに気にしません。





multidimensional-array