javascriptphpmysqld3.jsmultidimensional-array

Sankey diagram, multidimensional Array create with query in PHP


I want to create a multidimensional array for d3.sankey, but I don't understand how it works to build an multidimensional array.

What I need is:

d3.Sankey needs an input like:

{
"nodes":[
{"node":0,"name":"node0"},
{"node":1,"name":"node1"},
{"node":2,"name":"node2"},
{"node":3,"name":"node3"},
{"node":4,"name":"node4"}
],
"links":[
{"source":0,"target":2,"value":2},
{"source":1,"target":2,"value":2},
{"source":1,"target":3,"value":2},
{"source":0,"target":4,"value":2},
{"source":2,"target":3,"value":2},
{"source":2,"target":4,"value":2},
{"source":3,"target":4,"value":4}
]}

Actually I built that thing with strings. That sound really complicated and it is. One mistake and it doesn't work anymore.

So my question is how can I build that form with arrays.

So first of all I call the Database to give me all the entries which comes from and Inputtable. Looks Like: (yes, I'm from Germany so some words in that query are German words)

$stmt = self::$_db->prepare("SELECT BalancesheetInput.RessourceName, "
            . "SUM(BalancesheetInput.Amount) AS Amount, "
            . "Einheiten.Einheit AS Unit "
            . "FROM BalancesheetInput "
            . "INNER JOIN Einheiten ON Unit_FK = Unit_ID "
            . "GROUP BY BalancesheetInput.RessourceName");
        $stmt->execute();

        $fetcharray = $stmt->fetchALL(PDO::FETCH_ASSOC);

That "Input" query get the entries for the nodes and links which should refer to one Node which stands in the middle of the sankey-diagram (for better understanding I added a Picture)

Next step I need all the entries from an Outputtable

 $stmtOutput = self::$_db->prepare("SELECT BalancesheetOutput.RessourceName, "
            ."SUM(BalancesheetOutput.Amount) AS AmountOutput, "
            ."Einheiten.Einheit AS UnitOutput "
            ."FROM BalancesheetOutput "
            ."INNER JOIN Einheiten ON Unit_FK = Unit_ID "
            ."GROUP BY BalancesheetOutput.RessourceName");
        $stmtOutput->execute();
        $fetcharrayOutput = $stmtOutput->fetchAll(PDO::FETCH_ASSOC);

all these Entries refer from that Node in the Middle so its self (hope you understand what I mean)

What I'm doing at the moment is I get the Input and Output queries and put them together by creating a very long string.

$counter = 0;
    $ziel = count($fetcharray);
    $array_values = count($fetcharray);

    $counterafterInput = 0;

    $jsonNodes ='{"nodes":[';
    $jsonLinkes = '],"links":[';
    foreach ($fetcharray as $getnodes){
        
        if (($array_values-1) == $counter) {
           // saves node to string,
            //// wenn letzer Datenbankeintrag erreicht ist addet Schöneweide als Node
            $jsonNodes .= '{"node":'. $counter. ',"name":"' . $getnodes['RessourceName'] . '","units":"'.$getnodes['Unit'].'"},';
            $jsonNodes .= '{"node":'. ($counter+1). ',"name":"Schöneweide","units":"Blackbox"},';
           // $jsonNodes .= '{"node":'. ($counter+2). ',"name":"weiter","units":"'.$getnodes['Unit'].'"},'; //irgendwann obsolete
                        
            // saves node to string, wenn letzer Datenbankeintrag erreicht ist
            $jsonLinkes .= '{"source":'.$counter .',"target":' .$ziel .  ',"value":' . $getnodes['Amount'] . ',"units":"'.$getnodes['Unit'].'"},';
           // $jsonLinkes .= '{"source":'.($counter+1) .',"target":' .($ziel+1) .  ',"value":' . $getnodes['Amount'] . ',"units":"'.$getnodes['Unit'].'"}';

            $counter++;
            $counterafterInput = $counter;
            $counter++;
            
        }
        else{
        //saves nodes to string while counter != Anzahl der Datenbankeinträge
        //If else weil durch Komma getrennte Schreibeweise die nodes und Links eingetragen werden .... geht in die if ,wenn der vorletzte eintrag erreicht wird um
        //die letze Node anzuhängen. Die letzte Node ist die Blackbox Schöneweide(oder später auch ein anderes Gebiet
            
            $jsonNodes .= '{"node":'. $counter. ',"name":"' . $getnodes['RessourceName'] . '","units":"'.$getnodes['Unit'].'"},';
            $jsonLinkes .= '{"source":'.$counter .',"target":' .$ziel .  ',"value":' . $getnodes['Amount'] . ',"units":"'.$getnodes['Unit'].'"},';
        
  
        $counter++;
    }
    }

    $counterOutput = 0;

    $array_values_output = count($fetcharrayOutput);
    $jsnonnodesOutput = "";
    $jsonLinkesOutput = "";


    foreach ($fetcharrayOutput as $getnodesOutput)
    {
        if (($array_values_output-1) == $counterOutput){

        $jsnonnodesOutput .= '{"node":'.$counter.',"name":"'.$getnodesOutput['RessourceName'].'","units":"'.$getnodesOutput['UnitOutput'].'"}';
        $jsonLinkesOutput .= '{"source":'.$counterafterInput.',"target":'.$counter.',"value":'.$getnodesOutput['AmountOutput'].',"units":"'.$getnodesOutput['UnitOutput'].'"}]}';
        }
        else
        {
            $jsnonnodesOutput .= '{"node":'.$counter.',"name":"'.$getnodesOutput['RessourceName'].'","units":"'.$getnodesOutput['UnitOutput'].'"},';
            $jsonLinkesOutput .= '{"source":'.$counterafterInput.',"target":'.$counter.',"value":'.$getnodesOutput['AmountOutput'].',"units":"'.$getnodesOutput['UnitOutput'].'"},';
            $counterOutput++;
            $counter++;
        }


    }

    $JSONstring = $jsonNodes. $jsnonnodesOutput . $jsonLinkes .$jsonLinkesOutput;


   return $JSONstring;

So let me explain what I'm doing:

First step I set a Counter. Next I count all the entries in Query to set the Node in the middle. For example I got 5 entries so I need to set the node in the middle to 6. Nextstep is initialize

`$jsonNodes ='{"nodes":[';`

next step I run the foreach-loop to fill all the entries in string I need to get something like that

{"node":0,"name":"node0"},

my Code for that is

 $jsonNodes .= '{"node":'. $counter. ',"name":"' . $getnodes['RessourceName'] . '","units":"'.$getnodes['Unit'].'"},';

the Counter counts an Id for each entry

and also I have to set Links. So the nodes refer to the Node in the middle

 $jsonLinkes .= '{"source":'.$counter .',"target":' .$ziel .  ',"value":' . $getnodes['Amount'] . ',"units":"'.$getnodes['Unit'].'"},';

So source is the same ID like in nodes, target is the node in the middle amount and units are self explanatory.

In short I do the same to get the Output just that the source is the node in the middle and the target is each node itself

Actually it works but I think there is a much more better solution with arrays.

so Now I need Help to create an Array which I can save as variable for JavaScript.

If someone know how that works and tell me. That would be fantastic!


Solution

  • The better solution with arrays is to build an array with the structure you want and then run it through json_encode():

    $nodes = array();
    $nodes[] = array('node' => 0, 'name' => 'node0');
    $nodes[] = array('node' => 1, 'name' => 'node1');
    $nodes[] = array('node' => 2, 'name' => 'node2');
    
    $links = array();
    $links[] = array('source' => 0, 'target' => 2, 'value' => 2);
    $links[] = array('source' => 0, 'target' => 1, 'value' => 2);
    
    echo json_encode(array('nodes' => $nodes, 'links' => $links));
    // {"nodes":[{"node":0,"name":"node0"},{"node":1,"name":"node1"},{"node":2,"name":"node2"}],"links":[{"source":0,"target":2,"value":2},{"source":0,"target":1,"value":2}]}