用PHP生成水平和垂直家谱表

用PHP生成水平和垂直家谱表,php,recursion,Php,Recursion,我正在开发一种家谱类型的应用程序,允许将后代添加到父母的下面,尽可能深入和广泛。我有一个完美构建的数据库,这不是这里的问题。我遇到的问题是用HTML生成表 数据结构示例: Array ( [1] => Array ( [name] => Igor [children] => 2 [siblings] => 0 [level] => 1

我正在开发一种家谱类型的应用程序,允许将后代添加到父母的下面,尽可能深入和广泛。我有一个完美构建的数据库,这不是这里的问题。我遇到的问题是用HTML生成表

数据结构示例:

Array
(
    [1] => Array
        (
            [name] => Igor
            [children] => 2
            [siblings] => 0
            [level] => 1
            [descendants] => Array
                (
                    [7] => Array
                        (
                            [name] => Rapid
                            [children] => 2
                            [siblings] => 1
                            [level] => 2
                            [descendants] => Array
                                (
                                    [8] => Array
                                        (
                                            [name] => Hodor
                                            [children] => 1
                                            [siblings] => 1
                                            [level] => 3
                                            [descendants] => Array
                                                (
                                                    [9] => Array
                                                        (
                                                            [name] => Hodor II
                                                            [children] => 1
                                                            [siblings] => 0
                                                            [level] => 4
                                                            [descendants] => Array
                                                                (
                                                                    [20] => Array
                                                                        (
                                                                            [name] => Hodor III
                                                                            [children] => 0
                                                                            [siblings] => 0
                                                                            [level] => 5
                                                                        )

                                                                )

                                                        )

                                                )

                                        )

                                    [14] => Array
                                        (
                                            [name] => Rapid II
                                            [children] => 0
                                            [siblings] => 1
                                            [level] => 3
                                        )

                                )

                        )

                    [2] => Array
                        (
                            [name] => Thunder
                            [children] => 0
                            [siblings] => 1
                            [level] => 2
                        )

                )

        )

)
<?php

$tree = [
    'Igor' => [
        'Rapid'   => [
            'Hodor'    => [
                'Hodor II' => [
                    'Hodor III' => null
                ]
            ],
            'Rapid II' => null
        ],
        'Thunder' => [
            'Thunder II' => [
                'Thunder III' => [
                    'Thunder IV' => [
                        'Thunder V' => null
                    ]
                ]
            ]
        ]
    ]
];

// Loop over the tree. Every person in the root of the tree
// gets his own table(s).
foreach ($tree as $name => $children) {
    $table = [];
    parsePerson($name, $children, $table);
    $table = cleanupRows($table);

    output($table);
    $table = convertRowsToHorizontal($table);
    output($table, true);
}

/**
 * Convert a person in the tree to an array to be used to print the tables.
 * The span of a person is either the sum of its children's spans,
 * or 1 if it has no children.
 *
 * @param string $name
 * @param array  $children
 * @param array  $table
 * @param int    $level
 * @param int    $position
 *
 * @return int
 */
function parsePerson($name, $children, &$table, $level = 0, $position = 0)
{
    if (!empty($children)) {
        $span = 0;

        foreach ($children as $childName => $childChildren) {
            $span += parsePerson(
                $childName,
                $childChildren,
                $table,
                $level + 1,
                $position + $span
            );
        }
    } else {
        $span = 1;
    }

    $table[$level][$position] = getCell($name, $span);;

    return $span;
}

/**
 * Insert empty cells where needed and sort by keys.
 *
 * @param array $table
 *
 * @return array
 */
function cleanupRows($table)
{
    $width = $table[0][0]['span'];

    foreach ($table as $rowNumber => $row) {
        $spanSoFar = 0;
        foreach ($row as $position => $cell) {
            addExtraCells($table, $spanSoFar, $rowNumber, $position);
            $spanSoFar += $cell['span'];
        }
        addExtraCells($table, $spanSoFar, $rowNumber, $width);
        ksort($table[$rowNumber]);
    }
    ksort($table);

    return $table;
}

/**
 * @param array $table
 * @param int   $spanSoFar
 * @param int   $rowNumber
 * @param int   $position
 */
function addExtraCells(&$table, &$spanSoFar, $rowNumber, $position)
{
    while ($spanSoFar < $position) {
        $table[$rowNumber][$spanSoFar] = getCell();
        $spanSoFar += 1;
    }
}

/**
 * @param string $name
 * @param int    $span
 *
 * @return array
 */
function getCell($name = '', $span = 1)
{
    return ['name' => $name, 'span' => $span];
}

/**
 * Convert the table array from vertical representation to horizontal
 * representation. By switching 1st and 2nd level array keys.
 *
 * @param array $table
 *
 * @return array
 */
function convertRowsToHorizontal($table)
{
    $horizontal = [];

    foreach ($table as $rowNumber => $row) {
        foreach ($row as $cellNumber => $cell) {
            $horizontal[$cellNumber][$rowNumber] = $cell;
        }
    }
    ksort($horizontal);

    return $horizontal;
}

/**
 * Print the table.
 *
 * @param array $table
 * @param bool  $horizontal
 */
function output($table, $horizontal = false)
{
    $colRow = $horizontal ? 'row' : 'col';

    echo '<table border="1">';
    foreach ($table as $row) {
        echo '<tr>';
        foreach ($row as $cell) {
            echo '<td ' . $colRow . 'span="' . $cell['span'];
            echo '" align="center">';
            echo $cell['name'];
            echo '</td>';
        }
        echo '</tr>';
    }
    echo '</table>';
}
数字数组键是人员的ID

水平工作台的所需输出:

垂直:


我不确定什么是递归循环数据的最佳方法,同时保留行跨度和列跨度。我怎样才能在任何深度上有效地做到这一点?

我认为这段代码应该可以做到这一点,它适用于您的示例数据&我尝试过的其他一些数据集。以下是输出的屏幕截图:

有关所有部件工作原理的进一步说明,请参见注释

<?php

$tree = [
    [
        'name'        => 'Igor',
        'children'    => 2,
        'siblings'    => 0,
        'level'       => 1,
        'descendants' => [
            [
                'name'        => 'Rapid',
                'children'    => 2,
                'siblings'    => 1,
                'level'       => 2,
                'descendants' => [
                    [
                        'name'        => 'Hodor',
                        'children'    => 1,
                        'siblings'    => 1,
                        'level'       => 3,
                        'descendants' => [
                            [
                                'name'        => 'Hodor II',
                                'children'    => 1,
                                'siblings'    => 0,
                                'level'       => 4,
                                'descendants' => [
                                    [
                                        'name'     => 'Hodor III',
                                        'children' => 0,
                                        'siblings' => 0,
                                        'level'    => 5
                                    ]
                                ]
                            ]
                        ]
                    ],
                    [
                        'name'     => 'Rapid II',
                        'children' => 0,
                        'siblings' => 1,
                        'level'    => 3
                    ]
                ]
            ],
            [
                'name'     => 'Thunder',
                'children' => 0,
                'siblings' => 1,
                'level'    => 2
            ]
        ]
    ]
];

// Loop over the tree. Every person in the root of the tree
// gets his own table(s).
foreach ($tree as $person) {
    $rows = [];
    parsePerson($person, $rows);

    $rows = cleanupRows($rows);

    output($rows);
    $rows = convertRowsToHorizontal($rows);
    output($rows);
}

/**
 * Convert a person in the tree to an array to be used to print the tables.
 *
 * @param array $person
 * @param array $rows
 * @param int   $level
 * @param int   $position
 *
 * @return int
 */
function parsePerson($person, &$rows, $level = 0, $position = 0)
{
    if (!empty($person['descendants'])) {
        // The colspan of this row is the sum of the colspans of
        // its children
        $colspan = 0;

        foreach ($person['descendants'] as $descendant) {
            $colspan += parsePerson(
                $descendant,
                $rows,
                $level + 1,
                $position + $colspan
            );
        }
    } else {
        // If this person has no children, the colspan is 1.
        $colspan = 1;
    }

    $rows[$level][$position] = [
        'colspan' => $colspan,
        'name'    => $person['name']
    ];

    return $colspan;
}

/**
 * Insert empty cells where needed and sort by keys.
 *
 * @param array $rows
 *
 * @return array
 */
function cleanupRows($rows)
{
    $width = $rows[0][0]['colspan'];
    foreach ($rows as $rowNumber => $row) {
        $spanSoFar = 0;
        foreach ($row as $position => $cell) {
            // Insert empty cells in the row.
            if ($spanSoFar < $position) {
                for ($i = $spanSoFar; $i < $position; $i++) {
                    $rows[$rowNumber][$i] = ['name' => '', 'colspan' => 1];
                    $spanSoFar += 1;
                }
            }
            $spanSoFar += $cell['colspan'];
        }
        // Insert empty cells at the end of the row.
        if ($spanSoFar < $width) {
            for ($i = $spanSoFar; $i < $width; $i++) {
                $rows[$rowNumber][$i] = ['name' => '', 'colspan' => 1];
            }
        }
        // Sort cells by index.
        ksort($rows[$rowNumber]);
    }
    // Sort rows by index.
    ksort($rows);

    return $rows;
}

/**
 * Convert the table array from vertical representation to horizontal
 * representation.
 *
 * @param array $rows
 *
 * @return array
 */
function convertRowsToHorizontal($rows)
{
    // Create a new array containing all fields for the vertical representation
    // of the table.
    $newRows = [];

    // Fill the new array with data from the vertical table.
    foreach ($rows as $rowNumber => $row) {
        foreach ($row as $cellNumber => $cell) {
            $newRows[$cellNumber][$rowNumber] = [
                'name'    => $cell['name'],
                'rowspan' => $cell['colspan']
            ];
        }
    }

    ksort($newRows);

    return $newRows;
}

/**
 * Print the table.
 *
 * @param array $rows
 */
function output($rows)
{
    echo '<table border="1">';
    foreach ($rows as $row) {
        echo '<tr>';
        foreach ($row as $cell) {
            if (!empty($cell['colspan'])) {
                echo '<td colspan="' . $cell['colspan'] . '" align="center">';
            } else {
                echo '<td rowspan="' . $cell['rowspan'] . '" align="center">';
            }
            echo $cell['name'];
            echo '</td>';
        }
        echo '</tr>';
    }
    echo '</table>';
}

好吧,你必须设计这个,但让我们开始黑客:

<?php
$tree = [
      [
        'name'        => 'Igor',
        'children'    => 2,
        'siblings'    => 0,
        'level'       => 1,
        'descendants' => [
            [
                'name'        => 'Rapid',
                'children'    => 2,
                'siblings'    => 1,
                'level'       => 2,
                'descendants' => [
                    [
                        'name'        => 'Hodor',
                        'children'    => 1,
                        'siblings'    => 1,
                        'level'       => 3,
                        'descendants' => [
                            [
                                'name'        => 'Hodor II',
                                'children'    => 1,
                                'siblings'    => 0,
                                'level'       => 4,
                                'descendants' => [
                                    [
                                        'name'     => 'Hodor III',
                                        'children' => 0,
                                        'siblings' => 0,
                                        'level'    => 5
                                    ]
                                ]
                            ]
                        ]
                    ],
                    [
                        'name'     => 'Rapid II',
                        'children' => 0,
                        'siblings' => 1,
                        'level'    => 3
                    ]
                ]
            ],
            [
                'name'     => 'Thunder',
                'children' => 0,
                'siblings' => 1,
                'level'    => 2
            ]
        ]
    ]
];

我知道这不一定能满足您的需求,但我想在这里把它作为表的替代品扔掉

代码的基础是一个递归函数,我想这与其他答案很相似

function getChildren($tree)
{
    $html = "";
    if (is_array($tree) && count($tree)) {
        $html .= "<ul>\n";
        foreach ($tree as $key=>$leaf) {
            $info = "ID: $key\nChildren: $leaf[children]\nSiblings: $leaf[siblings]\nLevel: $leaf[level]";
            $info = htmlspecialchars($info);
            $name = htmlspecialchars($leaf["name"]);
            $html .= "<li>\n<a href='#' title='$info'>$name</a>\n";
            if (isset($leaf["descendants"])) {
                $html .= getChildren($leaf["descendants"]);
            }
            $html .= "</li>\n";
        }
        $html .= "</ul>\n";
    }
    return $html;
}

$who = array_values($tree)[0]["name"];
$html = getChildren($tree);


使用
表格
tr
td
代码将非常复杂,因为您需要管理colspan和row span,您可以只使用
ul li
,然后执行一些css技巧将为您提供所需的输出

请看一下下面的解决方案,我使用简单的php递归函数生成html,然后添加了一些css,完成了:)

PHP代码

$tree = array(
    array(
        'name'        => 'Igor',
        'children'    => 2,
        'siblings'    => 0,
        'level'       => 1,
        'descendants' => array(
            array(
                'name'        => 'Rapid',
                'children'    => 2,
                'siblings'    => 1,
                'level'       => 2,
                'descendants' => array(
                    array(
                        'name'        => 'Hodor',
                        'children'    => 1,
                        'siblings'    => 1,
                        'level'       => 3,
                        'descendants' => array(
                            array(
                                'name'        => 'Hodor II',
                                'children'    => 1,
                                'siblings'    => 0,
                                'level'       => 4,
                                'descendants' => array(
                                    array(
                                        'name'     => 'Hodor III',
                                        'children' => 0,
                                        'siblings' => 0,
                                        'level'    => 5
                                    )
                                )
                            )
                        )
                    ),
                    array(
                        'name'     => 'Rapid II',
                        'children' => 0,
                        'siblings' => 1,
                        'level'    => 3
                    )
                )
            ),
            array(
                'name'     => 'Thunder',
                'children' => 0,
                'siblings' => 1,
                'level'    => 2
            )
        )
    )
);
echo '<pre>';
function recurseTree($array){

  foreach($array as $v){
      $out .= '<li class="taxon">';
      $out .= '<div class="label">'.$v['name'].'</div>';

    if(is_array($v['descendants'])){
      $out .= '<ul  class="wrapper">'.recurseTree($v['descendants']).'</ul>';
    }
    $out .= '</li>';
  }
  return $out;
}

echo '<div class="horizontal"><ul class="wrapper">'.recurseTree($tree).'</ul>';
echo '<br />';
echo '<br />';
echo '<br />';
echo '<div class="verticle"><ul class="wrapper">'.recurseTree($tree).'</ul>';
$tree=数组(
排列(
'name'=>'Igor',
“儿童”=>2,
“兄弟姐妹”=>0,
“级别”=>1,
“子体”=>数组(
排列(
'名称'=>'快速',
“儿童”=>2,
“兄弟姐妹”=>1,
“级别”=>2,
“子体”=>数组(
排列(
'name'=>'Hodor',
“儿童”=>1,
“兄弟姐妹”=>1,
“级别”=>3,
“子体”=>数组(
排列(
'name'=>'Hodor II',
“儿童”=>1,
“兄弟姐妹”=>0,
“级别”=>4,
“子体”=>数组(
排列(
'name'=>'Hodor III',
“儿童”=>0,
“兄弟姐妹”=>0,
“级别”=>5
)
)
)
)
),
排列(
“名称”=>“Rapid II”,
“儿童”=>0,
“兄弟姐妹”=>1,
“级别”=>3
)
)
),
排列(
“name”=>“Thunder”,
“儿童”=>0,
“兄弟姐妹”=>1,
“级别”=>2
)
)
)
);
回声';
函数recurseTree($array){
foreach($v数组){
$out.='
  • '; $out.=''.$v['name'].'; if(是_数组($v['substands'])){ $out.='
      '.recurseTree($v['substands'])。
    '; } $out.='
  • '; } 退回$out; } echo'
      '.recurseTree($tree)。
    ; 回声“
    ”; 回声“
    ”; 回声“
    ”; echo'
      '.recurseTree($tree)。
    CSS代码

    $tree = array(
        array(
            'name'        => 'Igor',
            'children'    => 2,
            'siblings'    => 0,
            'level'       => 1,
            'descendants' => array(
                array(
                    'name'        => 'Rapid',
                    'children'    => 2,
                    'siblings'    => 1,
                    'level'       => 2,
                    'descendants' => array(
                        array(
                            'name'        => 'Hodor',
                            'children'    => 1,
                            'siblings'    => 1,
                            'level'       => 3,
                            'descendants' => array(
                                array(
                                    'name'        => 'Hodor II',
                                    'children'    => 1,
                                    'siblings'    => 0,
                                    'level'       => 4,
                                    'descendants' => array(
                                        array(
                                            'name'     => 'Hodor III',
                                            'children' => 0,
                                            'siblings' => 0,
                                            'level'    => 5
                                        )
                                    )
                                )
                            )
                        ),
                        array(
                            'name'     => 'Rapid II',
                            'children' => 0,
                            'siblings' => 1,
                            'level'    => 3
                        )
                    )
                ),
                array(
                    'name'     => 'Thunder',
                    'children' => 0,
                    'siblings' => 1,
                    'level'    => 2
                )
            )
        )
    );
    echo '<pre>';
    function recurseTree($array){
    
      foreach($array as $v){
          $out .= '<li class="taxon">';
          $out .= '<div class="label">'.$v['name'].'</div>';
    
        if(is_array($v['descendants'])){
          $out .= '<ul  class="wrapper">'.recurseTree($v['descendants']).'</ul>';
        }
        $out .= '</li>';
      }
      return $out;
    }
    
    echo '<div class="horizontal"><ul class="wrapper">'.recurseTree($tree).'</ul>';
    echo '<br />';
    echo '<br />';
    echo '<br />';
    echo '<div class="verticle"><ul class="wrapper">'.recurseTree($tree).'</ul>';
    
    
    .水平.标签{
    边界半径:1px;
    文本对齐:居中;
    }
    .水平包装{
    垂直对齐:中间对齐;
    }
    .horizontal.label、.horizontal.wrapper{
    显示:表格单元格;
    垂直对齐:中间对齐;
    }
    .水平分类单元{
    显示:表格行;
    溢出:隐藏;
    外形:1px实心#ddd;
    文本对齐:左对齐;
    边界间距:5px;
    }
    .垂直.标签{
    边界半径:1px;
    文本对齐:居中;
    }
    .垂直包装{
    垂直对齐:中间对齐;
    }
    .verticle.label、.verticle.wrapper{
    显示:表格行;
    垂直对齐:中间对齐;
    }
    .垂直分类群{
    显示:表格单元格;
    溢出:隐藏;
    外形:1px实心#ddd;
    文本对齐:左对齐;
    边界间距:5px;
    }
    
    在代码中,用水平类包装整个html,它将以水平格式显示,垂直类将以垂直格式显示。所以两种格式都可以使用相同的代码


    输出屏幕截图

    你会说NOSQL吗?请看我的答案,我刚刚使用了小递归函数来迭代数组和一些css技巧并完成了:)
    function getChildren($tree)
    {
        $html = "";
        if (is_array($tree) && count($tree)) {
            $html .= "<ul>\n";
            foreach ($tree as $key=>$leaf) {
                $info = "ID: $key\nChildren: $leaf[children]\nSiblings: $leaf[siblings]\nLevel: $leaf[level]";
                $info = htmlspecialchars($info);
                $name = htmlspecialchars($leaf["name"]);
                $html .= "<li>\n<a href='#' title='$info'>$name</a>\n";
                if (isset($leaf["descendants"])) {
                    $html .= getChildren($leaf["descendants"]);
                }
                $html .= "</li>\n";
            }
            $html .= "</ul>\n";
        }
        return $html;
    }
    
    $who = array_values($tree)[0]["name"];
    $html = getChildren($tree);
    
    $tree = array(
        array(
            'name'        => 'Igor',
            'children'    => 2,
            'siblings'    => 0,
            'level'       => 1,
            'descendants' => array(
                array(
                    'name'        => 'Rapid',
                    'children'    => 2,
                    'siblings'    => 1,
                    'level'       => 2,
                    'descendants' => array(
                        array(
                            'name'        => 'Hodor',
                            'children'    => 1,
                            'siblings'    => 1,
                            'level'       => 3,
                            'descendants' => array(
                                array(
                                    'name'        => 'Hodor II',
                                    'children'    => 1,
                                    'siblings'    => 0,
                                    'level'       => 4,
                                    'descendants' => array(
                                        array(
                                            'name'     => 'Hodor III',
                                            'children' => 0,
                                            'siblings' => 0,
                                            'level'    => 5
                                        )
                                    )
                                )
                            )
                        ),
                        array(
                            'name'     => 'Rapid II',
                            'children' => 0,
                            'siblings' => 1,
                            'level'    => 3
                        )
                    )
                ),
                array(
                    'name'     => 'Thunder',
                    'children' => 0,
                    'siblings' => 1,
                    'level'    => 2
                )
            )
        )
    );
    echo '<pre>';
    function recurseTree($array){
    
      foreach($array as $v){
          $out .= '<li class="taxon">';
          $out .= '<div class="label">'.$v['name'].'</div>';
    
        if(is_array($v['descendants'])){
          $out .= '<ul  class="wrapper">'.recurseTree($v['descendants']).'</ul>';
        }
        $out .= '</li>';
      }
      return $out;
    }
    
    echo '<div class="horizontal"><ul class="wrapper">'.recurseTree($tree).'</ul>';
    echo '<br />';
    echo '<br />';
    echo '<br />';
    echo '<div class="verticle"><ul class="wrapper">'.recurseTree($tree).'</ul>';
    
    <style>
    .horizontal .label{
        border-radius: 1px;
        text-align: center;
    }
    
    .horizontal .wrapper{
        vertical-align: middle;
    }
    
    .horizontal .label, .horizontal .wrapper{
        display: table-cell;
        vertical-align: middle;             
    }
    
    .horizontal .taxon{
        display: table-row;
        overflow: hidden;
        outline: 1px solid #ddd;
        text-align: left;
        border-spacing: 5px;
    }
    
    .verticle .label{
        border-radius: 1px;
        text-align: center;
    }
    
    .verticle .wrapper{
        vertical-align: middle;
    }
    
    .verticle .label, .verticle .wrapper{
        display: table-row;
        vertical-align: middle;             
    }
    
    .verticle .taxon{
        display: table-cell;
        overflow: hidden;
        outline: 1px solid #ddd;
        text-align: left;
        border-spacing: 5px;
    }
    </style>