Mysql
 sql >> Database >  >> RDS >> Mysql

Creazione di JSON gerarchico dai risultati di MySQL e PHP per l'albero D3.js?

Questo non sembra un design decente per i dati gerarchici. Prendi in considerazione un altro approccio come l'elenco di adiacenze .

Soluzione n. 1 - Supporto JSON MySQL 8:

Con MySQL 8 puoi usare JSON_ARRAYAGG() e JSON_OBJECT() per ottenere il risultato JSON solo con SQL:

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

Il risultato è:

{"name": "Bob", "children": [{"name": "Ted", "children": [{"name": "Fred"}]}, {"name": "Carol", "children": [{"name": "Harry"}]}, {"name": "Alice", "children": [{"name": "Mary"}]}]}

demo db-fiddle

Formattato:

{
    "name": "Bob",
    "children": [
        {
            "name": "Ted",
            "children": [
                {
                    "name": "Fred"
                }
            ]
        },
        {
            "name": "Carol",
            "children": [
                {
                    "name": "Harry"
                }
            ]
        },
        {
            "name": "Alice",
            "children": [
                {
                    "name": "Mary"
                }
            ]
        }
    ]
}

Soluzione n. 2 - Creazione di JSON con GROUP_CONCAT():

Se i nomi non contengono virgolette, puoi costruire manualmente la stringa JSON nelle versioni precedenti usando 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;

Il risultato sarebbe lo stesso (vedi demo )

Soluzione #3 - Costruire una struttura nestet con oggetti PHP:

Puoi anche scrivere una query SQL più semplice e costruire la struttura nidificata in 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
");

Il risultato è

name    | parent
----------------
Bob     | null
Ted     | Bob
Carol   | Bob
Alice   | Bob
Fred    | Ted
Harry   | Carol
Mary    | Alice

demo

Nota:il nome deve essere univoco lungo tutte le tabelle. Ma non so quale risultato ti aspetteresti, se fossero possibili duplicati.

Ora salva le righe come oggetti in un array indicizzato dal nome:

$data = []
while ($row = $result->fetch_object()) {
    $data[$row->name] = $row;
}

$data ora conterrà

[
    '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'],
]

Ora possiamo collegare i nodi in un unico ciclo:

$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);

Il risultato:

{
    "name": "Bob",
    "children": [
        {
            "name": "Ted",
            "children": [
                {
                    "name": "Fred"
                }
            ]
        },
        {
            "name": "Carol",
            "children": [
                {
                    "name": "Harry"
                }
            ]
        },
        {
            "name": "Alice",
            "children": [
                {
                    "name": "Mary"
                }
            ]
        }
    ]
}

demo

Se sono possibili più nodi radice (più righe in level_1_name ), quindi utilizzare

json_encode($roots);