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