我正在尝试使用PHP从数据库结果中创建以下JSON(大大简化了...):
{
"name": "Bob",
"children": [{
"name": "Ted",
"children": [{
"name": "Fred"
}]
},
{
"name": "Carol",
"children": [{
"name": "Harry"
}]
},
{
"name": "Alice",
"children": [{
"name": "Mary"
}]
}
]
}
数据库表:
Table 'level_1':
level_1_pk| level_1_name
-------------------------
1 | Bob
Table 'level_2':
level_2_pk| level_2_name | level_1_fk
-------------------------
1 | Ted | 1
2 | Carol | 1
3 | Alice | 1
Table 'level_3':
level_3_pk| level_3_name | level_2_fk
-------------------------
1 | Fred | 1
2 | Harry | 2
3 | Mary | 3
代码:
$query = "SELECT *
FROM level_1
LEFT JOIN level_2
ON level_1.level_1_pk = level_2.level_1_fk";
$result = $connection->query($query);
while ($row = mysqli_fetch_assoc($result)){
$data[$row['level_1_name']] [] = array(
"name" => $row['level_2_name']
);
}
echo json_encode($data);
产生:
{"Bob":[{"name":"Ted"},{"name":"Carol"},{"name":"Alice"}]}
问题:
如何获取下一个级别level_3,并按照上面定义的JSON的要求在JSON中包含文本“ children”和level_3 child?
我想在JSON中有更多子级的情况下,我将需要PHP是递归的。
答案 0 :(得分:1)
我将推荐以下递归:
function getPeople($levelNum = 1, $parent = 0) {
if ($levelNum > 3) return array(); // break recursion condition
global $connection;
$level = 'level_' . $levelNum; // also can check here if the table exist by this name
$query = "SELECT * FROM ". $level;
if ($parent) // if there is parent add him to query
$query .= "WHERE " . $level . "_fk = " . $parent;
$result = $connection->query($query);
while ($row = mysqli_fetch_assoc($result)) { // for each row:
$idCol = $level . "_pk"; // get the primary ID key
$id = $row[$idCol]; // get the ID
$localResult[$id] = array("Name" => $row[$level . "_name"]); // set local array with key as ID and name
}
foreach ($localResult as $id => $elem) { // elem is array with only name
$children = getPeople($levelNum + 1, $id); // recursively get all children
if ($children)
$elem["children"] = $children;
$data[] = $elem; // append the new elem to origin array
}
return $data;
}
初始呼叫应类似于getPeople()
或json_encode(getPeople())
;
通知-我使用最大深度作为递归中断,假设您知道最大深度-您也可以(建议这样做)跳过中断条件,仅检查表名是否存在! (作为$level
字符串)
我将其写为伪代码,因为我实际上并未构建表-它可能有语法错误,但逻辑应该是可靠的...
答案 1 :(得分:1)
这看起来并不适合分层数据。考虑另一种方法,例如邻接列表。
在MySQL 8中,您可以使用JSON_ARRAYAGG()
和JSON_OBJECT()
仅使用SQL来获取JSON结果:
select json_object(
'name', l1.level_1_name,
'children', json_arrayagg(json_object('name', l2.level_2_name, 'children', l2.children))
) as json
from level_1 l1
left join (
select l2.level_2_name
, l2.level_1_fk
, json_arrayagg(json_object('name', l3.level_3_name)) as children
from level_2 l2
left join level_3 l3 on l3.level_2_fk = l2.level_2_pk
group by l2.level_2_pk
) l2 on l2.level_1_fk = l1.level_1_pk
group by level_1_pk
结果是:
{"name": "Bob", "children": [{"name": "Ted", "children": [{"name": "Fred"}]}, {"name": "Carol", "children": [{"name": "Harry"}]}, {"name": "Alice", "children": [{"name": "Mary"}]}]}
格式化:
{
"name": "Bob",
"children": [
{
"name": "Ted",
"children": [
{
"name": "Fred"
}
]
},
{
"name": "Carol",
"children": [
{
"name": "Harry"
}
]
},
{
"name": "Alice",
"children": [
{
"name": "Mary"
}
]
}
]
}
如果名称中不包含引号,则可以使用GROUP_CONCAT()
在旧版本中手动构造JSON字符串:
$query = <<<MySQL
select concat('{',
'"name": ', '"', l1.level_1_name, '", ',
'"children": ', '[', group_concat(
'{',
'"name": ', '"', l2.level_2_name, '", ',
'"children": ', '[', l2.children, ']',
'}'
separator ', '), ']'
'}') as json
from level_1 l1
left join (
select l2.level_2_name
, l2.level_1_fk
, group_concat('{', '"name": ', '"', l3.level_3_name, '"', '}') as children
from level_2 l2
left join level_3 l3 on l3.level_2_fk = l2.level_2_pk
group by l2.level_2_pk
) l2 on l2.level_1_fk = l1.level_1_pk
group by level_1_pk
MySQL;
结果将是相同的(请参见demo)
您还可以编写一个更简单的SQL查询并在PHP中构造嵌套结构:
$result = $connection->query("
select level_1_name as name, null as parent
from level_1
union all
select l2.level_2_name as name, l1.level_1_name as parent
from level_2 l2
join level_1 l1 on l1.level_1_pk = l2.level_1_fk
union all
select l3.level_3_name as name, l2.level_2_name as parent
from level_3 l3
join level_2 l2 on l2.level_2_pk = l3.level_2_fk
");
结果是
name | parent
----------------
Bob | null
Ted | Bob
Carol | Bob
Alice | Bob
Fred | Ted
Harry | Carol
Mary | Alice
注意:名称在所有表中都应该是唯一的。但是,如果有可能重复,我不知道会得到什么结果。
现在将行另存为对象,并以名称索引:
$data = []
while ($row = $result->fetch_object()) {
$data[$row->name] = $row;
}
$data
现在将包含
[
'Bob' => (object)['name' => 'Bob', 'parent' => NULL],
'Ted' => (object)['name' => 'Ted', 'parent' => 'Bob'],
'Carol' => (object)['name' => 'Carol', 'parent' => 'Bob'],
'Alice' => (object)['name' => 'Alice', 'parent' => 'Bob'],
'Fred' => (object)['name' => 'Fred', 'parent' => 'Ted'],
'Harry' => (object)['name' => 'Harry', 'parent' => 'Carol'],
'Mary' => (object)['name' => 'Mary', 'parent' => 'Alice'],
]
我们现在可以在单个循环中链接节点:
$roots = [];
foreach ($data as $row) {
if ($row->parent === null) {
$roots[] = $row;
} else {
$data[$row->parent]->children[] = $row;
}
unset($row->parent);
}
echo json_encode($roots[0], JSON_PRETTY_PRINT);
结果:
{
"name": "Bob",
"children": [
{
"name": "Ted",
"children": [
{
"name": "Fred"
}
]
},
{
"name": "Carol",
"children": [
{
"name": "Harry"
}
]
},
{
"name": "Alice",
"children": [
{
"name": "Mary"
}
]
}
]
}
如果可能有多个根节点(level_1_name
中有多行),请使用
json_encode($roots);