我正在尝试使用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子级?
我想在JSON中有更多子级的情况下,我将需要PHP进行递归。
的SQL
这看起来不像是分层数据的合理设计。考虑另一种方法,例如 邻接表 。
在MySQL 8中,您可以使用JSON_ARRAYAGG()和JSON_OBJECT()仅通过SQL获得JSON结果:
JSON_ARRAYAGG()
JSON_OBJECT()
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"}]}]}
db-fiddle演示
格式:
{ "name": "Bob", "children": [ { "name": "Ted", "children": [ { "name": "Fred" } ] }, { "name": "Carol", "children": [ { "name": "Harry" } ] }, { "name": "Alice", "children": [ { "name": "Mary" } ] } ] }
如果名称中不包含引号,则可以使用GROUP_CONCAT()以下版本手动构建JSON字符串:
GROUP_CONCAT()
$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 现在将包含
$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);
结果:
如果可能有多个根节点(中有多行level_1_name),请使用
level_1_name
json_encode($roots);