如何从递归查询构建嵌套div的精确形式?

时间:2012-03-23 16:22:28

标签: php html

我有一个有点复杂的递归postgresql查询,它引入了一些东西(我已经为了这个问题的目的简化了它),如下所示:

id  depth   path        has_children

1   1       1           true
2   2       1.2         true
3   3       1.2.3       true
4   4       1.2.3.4     true
5   5       1.2.3.4.5   false
6   1       6           true
7   2       6.7         true
8   3       6.7.8       false
9   1       9           false
10  1       10          true
11  2       10.11       false

这是获取的结果(对于那些想知道为什么我将一些数组解析为对象的人,因为行被提取为对象而我只是复制了结果):

$tree = array
(
    (object) array
    (
        "id" => 1,
        "depth" => 1,
        "path" => "1",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 2,
        "depth" => 2,
        "path" => "1.2",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 3,
        "depth" => 3,
        "path" => "1.2.3",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 4,
        "depth" => 4,
        "path" => "1.2.3.4",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 5,
        "depth" => 5,
        "path" => "1.2.3.4.5",
        "has_children" => false
    ),

    (object) array
    (
        "id" => 6,
        "depth" => 1,
        "path" => "6",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 7,
        "depth" => 2,
        "path" => "6.7",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 8,
        "depth" => 3,
        "path" => "6.7.8",
        "has_children" => false
    ),

    (object) array
    (
        "id" => 9,
        "depth" => 1,
        "path" => "9",
        "has_children" => false
    ),

    (object) array
    (
        "id" => 10,
        "depth" => 1,
        "path" => "10",
        "has_children" => true
    ),

    (object) array
    (
        "id" => 11,
        "depth" => 2,
        "path" => "10.11",
        "has_children" => false
    )
);

我想将结果转换为此(使用给定的类名称):

<div id="foo">
    <div class="bar">
        <div class="qux">
            <p>1</p>
        </div>

        <div class="baz">
            <div class="qux">
                <p>1.2</p>
            </div>

            <div class="baz">
                <div class="qux">
                    <p>1.2.3</p>
                </div>

                <div class="baz">
                    <div class="qux">
                        <p>1.2.3.4</p>
                    </div>

                    <div class="baz">
                        <div class="qux">
                            <p>1.2.3.4.5</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="bar">
        <div class="qux">
            <p>6</p>
        </div>

        <div class="baz">
            <div class="qux">
                <p>6.7</p>
            </div>

            <div class="baz">
                <div class="qux">
                    <p>6.7.8</p>
                </div>
            </div>
        </div>
    </div>

    <div class="bar">
        <div class="qux">
            <p>9</p>
        </div>
    </div>

    <div class="bar">
        <div class="qux">
            <p>10</p>
        </div>

        <div class="baz">
            <div class="qux">
                <p>10.11</p>
            </div>
        </div>
    </div>
</div>

然而,在查看涉及ulli的太多示例后,我已经迷茫了自己。由于嵌套的ulli与嵌套的div不同,因此为我的使用调整此类示例失败。

我想要一个使用foreach(最好)或while循环的干净解决方案(不需要递归函数)。我也不希望将结果重新创建为多维数组。

4 个答案:

答案 0 :(得分:1)

您需要将db数据提取到一个数组中,您可以循环并使用depth和has_children,将数据传递给新的多维数组:

array(
    '1' => array(
        '1.2' => array(
            '1.2.3' => array(
                '1.2.3.4' => array('1.2.3.4.5')
            )
        )
    ),
    '6' => array(
        '6.7' => array('6.7.8')
    )
);

等。然后你可以循环它并使用foreach将每个数组作为div回显。

想象一下,您使用array('row' => array('key' => 'value', 'etc' => 'more'))形式发布的示例表中的已获取数据。对于每一行,您需要检查['has_children']值。如果它的计算结果为true,那么你应该将深度作为一个整数并使用for循环,例如for ($i=0; $i<$depth; $i++),创建一个数组数组,如上所示。

有几种方法可以做到这一点,更简单的方法是递归地创建数组,例如 $array = '1.2.3.4.5'然后$array = array('1.2.3.4' => $array),直到你有一个如上所述的多维数组。

拥有该数组,就像:

一样简单
function display($multiarray) {
    foreach ($multiarray as $name=>$path) {
        if (is_array($path)) {
            // Check if it's the first.
            $class = (strpos('.', $name)) ? 'baz' : 'bar';
            echo "<div class='{$class}'>\n\t";
            echo "<div class='qux'>\n\t<p>{$name}</p>\n</div>\n\n";
            display($path);
            echo "</div>";
        } else {
            echo "<div class='baz'>\n\t";
            echo "<div class='qux'>\n\t<p>{$path}</p>\n</div>";
            echo "</div>";
        }
    }
}

动态制作,但我希望你能得到主要的想法。

答案 1 :(得分:1)

您的数据持平会使编写递归函数变得更加困难。递归函数最自然地与嵌套数据一起工作。花了一段时间,但我能够创建一个与您的平面数据一起使用的部分递归解决方案。

<?php
$tree = array(array("id"=>1,"depth"=>1,"path"=>"1","has_children"=>true),array("id"=>2,"depth"=>2,"path"=>"1.2","has_children"=>true),array("id"=>3,"depth"=>3,"path"=>"1.2.3","has_children"=>true),array("id"=>4,"depth"=>4,"path"=>"1.2.3.4","has_children"=>true),array("id"=>5,"depth"=>5,"path"=>"1.2.3.4.5","has_children"=>false),array("id"=>6,"depth"=>1,"path"=>"6","has_children"=>true),array("id"=>7,"depth"=>2,"path"=>"6.7","has_children"=>true),array("id"=>8,"depth"=>3,"path"=>"6.7.8","has_children"=>false),array("id"=>9,"depth"=>1,"path"=>"9","has_children"=>false),array("id"=>10,"depth"=>1,"path"=>"10","has_children"=>true),array("id"=>11,"depth"=>2,"path"=>"10.11","has_children"=>false));

header('Content-Type: text/plain');

function print_tree(&$tree, $i, $top_level)
{
    $indent = str_repeat('    ', $tree[$i]['depth']);

    echo $indent."<div class=\"".($top_level ? 'bar' : 'baz')."\">\n";
    echo $indent."    <div class=\"qux\">\n";
    echo $indent."        <p>".$tree[$i]['path']."</p>\n";
    echo $indent."    </div>\n";

    if($tree[$i]['has_children'])
    {
        print_tree($tree, $i+1, false);
    }

    echo $indent."</div>\n";
}

echo "<div id=\"foo\">\n";

$count = count($tree);
for($i = 0; $i < $count; ++$i)
{
    if($tree[$i]['depth'] == 1)
    {
        print_tree($tree, $i, true);
    }
}

echo "</div>\n";
?>

这将输出:

<div id="foo">
    <div class="bar">
        <div class="qux">
            <p>1</p>
        </div>
        <div class="baz">
            <div class="qux">
                <p>1.2</p>
            </div>
            <div class="baz">
                <div class="qux">
                    <p>1.2.3</p>
                </div>
                <div class="baz">
                    <div class="qux">
                        <p>1.2.3.4</p>
                    </div>
                    <div class="baz">
                        <div class="qux">
                            <p>1.2.3.4.5</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="bar">
        <div class="qux">
            <p>6</p>
        </div>
        <div class="baz">
            <div class="qux">
                <p>6.7</p>
            </div>
            <div class="baz">
                <div class="qux">
                    <p>6.7.8</p>
                </div>
            </div>
        </div>
    </div>
    <div class="bar">
        <div class="qux">
            <p>9</p>
        </div>
    </div>
    <div class="bar">
        <div class="qux">
            <p>10</p>
        </div>
        <div class="baz">
            <div class="qux">
                <p>10.11</p>
            </div>
        </div>
    </div>
</div>

答案 2 :(得分:1)

这个闪亮的纯SQL 将完全生成您的字符串:

WITH x AS (
    SELECT *
          ,(lag(depth) OVER (ORDER BY id) +1) - depth AS end_divs
          ,CASE WHEN depth = 1 THEN 'bar' ELSE 'baz' END AS class
          ,E'\n' || repeat('    ', depth) AS i -- newline + indent
    FROM   tbl
    ORDER  BY id
    )
SELECT '<div id="foo">'
    || string_agg(
          CASE WHEN end_divs > 0 THEN 
             (SELECT string_agg(repeat ('    ', n), E'</div>\n')
              FROM   generate_series (end_divs,0,-1) n)
          ELSE '' END
          || i || '<div class="' || class ||'">'
          || i || '    <div class="qux">'
          || i || '        <p>' || path || '</p>'
          || i || '    </div>'
        , E'\n' ORDER BY id
       )
    || (SELECT E'\n' || string_agg(repeat ('    ', n-1), E'</div>\n')
        FROM   generate_series((SELECT depth+1 FROM tbl ORDER BY id DESC LIMIT 1)
                              , 0 , -1) n)
FROM   x;

阅读起来并不容易,我会留下将这些功能记录下来。

答案 3 :(得分:1)

可能不是最漂亮的代码,但它完全按照您的指定输出:

<div id="foo">
<?php

$openingElement = true;
$divsOpened = 0;
$indent = 1;

foreach ($tree as $row) {
    if ($openingElement === true) {
        print str_repeat(' ', $indent * 4) . '<div class="bar">' . PHP_EOL;
    } else {
        print str_repeat(' ', $indent * 4) . '<div class="baz">' . PHP_EOL;
    }

    $indent++;
    $divsOpened++;

    print str_repeat(' ', $indent * 4) . '<div class="qux">' . PHP_EOL;
    $indent++;
    print str_repeat(' ', $indent * 4) . '<p>' . $row->path . '</p>' . PHP_EOL;
    $indent--;
    print str_repeat(' ', $indent * 4) . '</div>' . PHP_EOL;

    if ($row->has_children) {
        $openingElement = false;
        print PHP_EOL;
    } else {
        for ($i = $divsOpened; $i > 0; $i--) {
            print str_repeat(' ', $i * 4) . '</div>' . PHP_EOL;
            $indent--;
            $divsOpened--;
        }
        $openingElement = true;
    }
}

?>
</div>