I run queries on 3 tables and I'm trying to format results into a nested array. It's the inner array thats got me stumped. sizes array should be in the beer array object but its outside of it. Any help appreciated.
JSON
{
"brewerId": "41",
"brewerName": "Prancing Pony Brewing",
"beers": [
{
"beerid": "816",
"productName": "Prancing Pony Amber Ale",
"ibu": "18.00",
"abv": "5.00",
"style": "Amber Ale"
},
{
"beerid": "817",
"productName": "Prancing Pony Copper Ale",
"ibu": "25.00",
"abv": "5.80",
"style": "Indian Pale Ale"
},
{
"beerid": "837",
"productName": "Prancing Pony Pale Ale",
"ibu": "37.00",
"abv": "5.50",
"style": "Pale Ale"
},
{
"beerid": "838",
"productName": "Prancing Pony India Red Ale",
"ibu": "60.00",
"abv": "7.90",
"style": "Indian Pale Ale"
}
],
"sizes": [
{
"beerId": "816",
"size": "330ml Bottle"
},
{
"beerId": "816",
"size": "330ml Can"
},
{
"beerId": "837",
"size": "345ml Can"
},
{
"beerId": "837",
"size": "375ml Bottle"
}
]
},
PHP
$result = $func->getBrewers();
$json_response = array();
while ($row = mysqli_fetch_array($result))
{
$row_array = array();
$row_array['brewerId'] = $row['brewerId'];
$row_array['brewerName'] = $row['brewerName'];
$row_array['beers'] = array();
$brewer_pk = $row['brewerId'];
$beer_qry = $func->getBeers($brewer_pk);
while ($beer_fet = mysqli_fetch_array($beer_qry))
{
$row_array['beers'][] = array(
'beerid' => $beer_fet['beerid'],
'productName' => $beer_fet['productName'],
'ibu' => $beer_fet['ibu'],
'abv' => $beer_fet['abv'],
'notes' => $beer_fet['notes'],
'style' => $beer_fet['style'],
);
$beer_pk = $beer_fet['beerid'];
$size_qry = $func->getSizes($beer_pk);
while ($size_fet = mysqli_fetch_array($size_qry))
{
$row_array['sizes'][] = array(
'beerId' => $size_fet['beerId'],
'size' => $size_fet['size'],
);
}
}
array_push($json_response, $row_array);
}
echo json_encode($json_response);
It seems you misindented at some point and just went wrong from that point on.
$result = $func->getBrewers();
$json_response = array();
while ($row = mysqli_fetch_array($result))
{
$row_array = array(
'brewerId' => $row['brewerId'],
'brewerName' => $row['brewerName'],
'beers' => array()
);
$brewer_pk = $row['brewerId'];
$beer_qry = $func->getBeers($brewer_pk);
while ($beer_fet = mysqli_fetch_array($beer_qry))
{
$tmp = array(
'beerid' => $beer_fet['beerid'],
'productName' => $beer_fet['productName'],
'ibu' => $beer_fet['ibu'],
'abc' => $beer_fet['abv'],
'notes' => $beer_fet['notes'],
'style' => $beer_fet['style'],
'sizes' => array()
);
$beer_pk = $tmp['beerid'];
$size_qry = $func->getSizes($beer_pk);
while ($size_fet = mysqli_fetch_array($size_qry))
{
$tmp['sizes'][] = array(
'beerId' => $size_fet['beerId'],
'size' => $size_fet['size'],
);
}
$row_array['beers'][] => $tmp;
}
array_push($json_response, $row_array);
}
echo json_encode($json_response);
In order to simplify the referencing somewhat, I've rewritten the beers to be created in a $tmp variable, which now also has a 'sizes' array to which the various sizes will be appended.
After the beers and sizes are added, the $tmp will be added to the beers.
Do note that you will now be performing some more database queries, as it now (as requested) fetches the sizes per beer and not just for the last one.
Related
I'm creating nested (hierarchical) groups in JSON. The structure is simply - grand parent -> parent -> child:
main
secondary
mcondition
This is using the following MySQL/PHP to so far format main -> mcondition.
How should I change this to add the second level ('secondary' e.g. the parent level) between main and mcondition?
The column for secondary is mcondition.secondary
$query = 'SELECT * FROM mcondition ORDER BY mcondition.main ASC';
$result = $connection->query( $query );
$results = array();
$temp = array();
while ($line = mysqli_fetch_array($result)) {
$results[] = $line;
}
foreach($results as $row) {
$temp[$row['main']]['text'] = $row['main'];
if(!isset($temp[$row['main']]['children'])) {
$temp[$row['main']]['children'] = array();
}
array_push($temp[$row['main']]['children'], array(
'id' => $row['mcondition_pk'],
'text' => $row['mcondition_name']
));
}
$temp = array_values($temp);
echo json_encode($temp);
This is what the JSON currently looks like:
[
{
"text": "Main Heading 1",
"children": [
{
"id": "1",
"text": "mcondition_1"
},
{
"id": "17",
"text": "mcondition_4"
}
]
},
{
"text": "Main Heading 2",
"children": [
{
"id": "49",
"text": "mcondition_2"
},
{
"id": "48",
"text": "mcondition_5"
}
]
},
{
"text": "Main Heading 3",
"children": [
{
"id": "68",
"text": "mcondition_3"
},
{
"id": "67",
"text": "mcondition_6"
}
]
}
]
This is the structure of the table mcondition:
+---------------+------------+------+-----------+
| mcondition_pk | mcondition | main | secondary |
+---------------+------------+------+-----------+
Column mcondition is unique.
I'm still not 100% sure what your expected json would look like, but you should be able to do something like this. You may need to make some modifications to get exactly what you're wanting. I'm using an array $results to simulate the data acquisition from the database. This will get you a nested hierarchical structure.
$out = [];
$results = [
['main' => 'mk1', 'secondary' => 'sk1', 'mcondition_pk' => 1, 'mcondition' => 'mcondition_1'],
['main' => 'mk1', 'secondary' => 'sk2', 'mcondition_pk' => 2, 'mcondition' => 'mcondition_2'],
['main' => 'mk1', 'secondary' => 'sk2', 'mcondition_pk' => 3, 'mcondition' => 'mcondition_3'],
['main' => 'mk2', 'secondary' => 'sk3', 'mcondition_pk' => 4, 'mcondition' => 'mcondition_4'],
['main' => 'mk2', 'secondary' => 'sk3', 'mcondition_pk' => 5, 'mcondition' => 'mcondition_5'],
['main' => 'mk3', 'secondary' => 'sk4', 'mcondition_pk' => 6, 'mcondition' => 'mcondition_6'],
['main' => 'mk3', 'secondary' => 'sk5', 'mcondition_pk' => 7, 'mcondition' => 'mcondition_7'],
['main' => 'mk3', 'secondary' => 'sk5', 'mcondition_pk' => 8, 'mcondition' => 'mcondition_8'],
];
foreach($results as $row) {
$mk = $row['main'];
$sk = $row['secondary'];
$pk = $row['mcondition_pk'];
$cond = $row['mcondition'];
$temp = ['id' => $pk, 'text' => $cond];
if(!isset($out[$mk])) { $out[$mk] = ['text' => $mk]; }
if(!isset($out[$mk][$sk])) { $out[$mk][$sk] = ['text' => $sk, 'children' => []]; }
$out[$mk][$sk]['children'][] = $temp;
}
echo "<pre>"; print_r($out); echo "</pre>";
$json = json_encode($out);
echo "<pre>"; print_r($json); echo "</pre>"; exit;
I'm making an API for getting some data. My API gives object data like given, given object I wanted to format some data inside object:
{
"data": [
{
"productId": 55,
"productTitle": "Test product",
"variation": {
"Color": "Red",
"Size": "XS",
"din": "10190537",
"product_id": 55,
"name": [
"Color",
"Size"
],
"value": [
"Red",
"XS"
]
},
"din": "10190537",
"markets": [
{
"id": 11,
"name": "paytmmall",
"displayName": "PayTm Mall",
"identifierName": "Product_Id"
}
]
}
]
}
In this object I want data like given
{
"data": [
{
"productId": 55,
"productTitle": "this is test from hariom",
"variation": {
"Color": "Red",
"Size": "XS",
"din": "10190537",
"product_id": 55,
"variationTypes": [
{
"name": "Color",
"value": "Red"
},
{
"name": "Size",
"value": "XS"
}
],
},
"din": "10190537",
"markets": [
{
"id": 11,
"name": "paytmmall",
"displayName": "PayTm Mall",
"identifierName": "Product_Id"
}
]
}
]
}
Here Is my Controller Name
public function MarketMapping(Request $request)
{
$sellerId = Auth::guard('seller-api')->user();
$page = $request->has('pageNumber') ? $request->get('pageNumber') : 1;
$limit = $request->has('perPage') ? $request->get('perPage') : 10;
$variationFromInvTbl = ProductInventory::select('Color', 'Size', 'din', 'product_id')->where('seller_id', $sellerId->id)->where('status', 'active')->limit($limit)->offset(($page - 1) * $limit)->get();
$dataArray = array();
foreach($variationFromInvTbl as $key => $varitionValue)
{
$prodtsFromLivetbl = ProductsLive::select('productTitle', 'product_id')->where('product_id', $varitionValue->product_id)->get();
foreach ($prodtsFromLivetbl as $key => $value)
{
$marketChannelData = DB::table('market_channels')
->join('sellers_market_channels', 'market_channels.name', '=', 'sellers_market_channels.key')
//->join('market_product_mappings', 'market_channels.id', '=', 'market_product_mappings.market_id')
->select('market_channels.id','market_channels.name', 'market_channels.displayName','market_channels.identifierName') //'market_product_mappings.identifierValue'
->where('sellers_market_channels.seller_id', $sellerId->id)
->where('sellers_market_channels.value', 1)
->get();
$maketProductMap = MarketProductMapping::where('seller_id', $sellerId->id)->where('product_id', $varitionValue->product_id)->where('din', $varitionValue->din)->pluck('identifierValue');
if (count($maketProductMap))
{
$marketChannelData[$key]->value = $maketProductMap[0];
}
$varitionValue['name']= array_keys($varitionValue->only(['Color', 'Size']));
$varitionValue['value'] = array_values($varitionValue->only(['Color', 'Size']));
$dataObject = ((object)[
"productId" => $value->product_id,
"productTitle" => $value->productTitle,
"variation" => $varitionValue,
"din" => $varitionValue['din'],
"markets" => $marketChannelData
]);
array_push($dataArray,$dataObject);
}
}
if($variationFromInvTbl)
{
$response['success'] = true;
$response["page"] = $page;
$response["itemPerPage"] = $limit;
$response["totalRecords"] = $this->CountMarketMapping($page, $limit, $sellerId->id);
$response['data'] = $dataArray;
return response()->json($response, 200);
}else{
$response['success'] = false;
$response['data'] = $prodtsFromLivetbl;
return response()->json($response, 409);
}
}
You are using laravel's only() method which returns an associative array.
You wish to convert each key-value pair into a subarray containing two associative elements -- the original key will be the value of the name element
and the original value will be the value of the value element.
By passing the original array keys and array values into array_map(), you can iterate them both synchronously.
compact() is a perfect native function to create the desired associative subarrays from the iterated parameters.
Code: (Demo)
$variations = $varitionValue->only(['Color', 'Size']);
$dataObject = (object)[
// ... your other data
'variations' => array_map(
function($name, $value) {
return compact(['name', 'value']);
},
array_keys($variations),
$variations
),
// ...your other data
];
var_export($dataObject);
Output:
(object) array(
'variations' =>
array (
0 =>
array (
'name' => 'Color',
'value' => 'Red',
),
1 =>
array (
'name' => 'Size',
'value' => 'XS',
),
),
)
This script will help you
<?php
$data = [
"variation" => [
[
"Color" => "Red",
"Size" => "XS",
"din" => "10190537",
"product_id" => 55,
"name" => [
"0" => "Color",
"1" => "Size"
],
"value" => [
"0" => "Red",
"1" => "XS"
]
]
]
];
for ($i=0; $i < count($data["variation"]); $i++) {
$data["variation"][$i]["data"]["name"] = $data["variation"][$i]["name"];
$data["variation"][$i]["data"]["value"] = $data["variation"][$i]["value"];
unset($data["variation"][$i]["name"]);
unset($data["variation"][$i]["value"]);
}
print_r($data);
output
Array
(
[variation] => Array
(
[0] => Array
(
[Color] => Red
[Size] => XS
[din] => 10190537
[product_id] => 55
[data] => Array
(
[name] => Array
(
[0] => Color
[1] => Size
)
[value] => Array
(
[0] => Red
[1] => XS
)
)
)
)
)
I'm trying to convert some array values (group then actually), but I don't know how to do that.
I have something like this:
[
{
"nsr": "000086310",
"type": "3",
"date": "2015-07-18",
"time": "00:06",
"pis": "12138790985"
},
{
"nsr": "000086313",
"type": "3",
"date": "2015-07-18",
"time": "00:33",
"pis": "16073736879"
},
{
"nsr": "000086316",
"type": "3",
"date": "2015-07-18",
"time": "00:58",
"pis": "16634402451"
},
{
"nsr": "000086316",
"type": "3",
"date": "2015-07-19",
"time": "00:58",
"pis": "98127981729"
},
{
"nsr": "000086316",
"type": "3",
"date": "2015-07-19",
"time": "00:58",
"pis": "12398712938"
}
]
And I want to convert to this:
[
"date" : "2015-07-18",
"pis" : [
"12138790985",
"16073736879",
"16634402451"
]
],
[
"date" : "2015-07-19",
"pis" : [
"98127981729",
"12398712938"
]
]
I tried to do something like this:
public function index()
{
$this->setTxtData('../../txt_files/CAP 3 18 07 2015 FABRICA.txt');
$txtdata = $this->getTxtData();
$dataToCompare = array();
foreach($txtdata as $ponto){
$time = $ponto['time'];
$date = $ponto['date'];
$pis = $ponto['pis'];
// $dataToCompare = array();
// if(strpos($ponto['pis'], '00000000000') === false){
// $pis_temp[][''] = $ponto['pis'];
// }
if(isset($dataToCompare)){
foreach($dataToCompare as $dateToSet){
if($dateToSet['data'] == $date){
$dateToSet['pis'][] = $pis;
}
else{
$dateToSet['data'] = $date;
$dateToSet['pis'][] = $pis;
}
}
}
else{
$dataToCompare = array(
[
'data' => $date,
'pis' => array($pis)
]
);
}
$funcionario_id = DB::table('funcionario')
->select('id')
->where('pis_pasep', '=', $pis)
->pluck('id');
if($funcionario_id !== null){
$validate = DB::table('horas_trabalho')
->select('id')
->where('hora', '=', $time)
->where('data', '=', $date)
->where('funcionario_id', '=', $funcionario_id)
->pluck('id');
if($validate === null){
DB::table('horas_trabalho')
->insert([
'hora' => $time,
'data' => $date,
'funcionario_id' => $funcionario_id
]);
}
}
}
//-------------------------------------Lógica para faltas-----------------------------------
/**
* Pega o pis
*/
$db_all_funcionarios = DB::table('funcionario')
->select('pis_pasep')
->where('pis_pasep', '!=', 0)
->get();
foreach($db_all_funcionarios as $pis){
if(strpos($pis->pis_pasep, '00000000000') !== true){
$global_pis[] = $pis->pis_pasep;
}
}
// $faltantes = array_diff($pis_temp, $global_pis);
// foreach($faltantes as $faltante){
// DB::table('falta')
// ->insert([
// 'data' => date('2015-07-16')
// ]);
// }
// $ponto_db[] = DB::table('horas_trabalho')
// ->join('funcionario', 'horas_trabalho.funcionario_id', '=', 'funcionario.id')
// ->select('funcionario.nome', 'horas_trabalho.hora', 'horas_trabalho.data')
// ->get();
return $txtdata;
}
I see the date in your filename and think you want group all entities at all:
$dataToCompare = array(
'date' => $txtData[0]['date'],
'pis' => array_map(function($el){return $el['pis'];}, $txtData)
);
For multiple dates:
$hash = array();
foreach ($txtData as $entity) {
if (!isset($hash[$entity['date']])) $hash[$entity['date']] = array();
$hash[$entity['date']][] = $entity['pis'];
}
$result = array();
foreach($hash as $date=>$pis) {
$result[] = array('date'=>$date, 'pis'=>$pis);
}
It might be a little sloppy, but it does the job. Or either way, it works as you wanted in the example:
<?php
$array = array(
0 => array(
"nsr" => "000086310",
"type" => "3",
"date" => "2015-07-18",
"time" => "00:06",
"pis" => "12138790985"
),
1 => array(
"nsr" => "000086313",
"type" => "3",
"date" => "2015-07-18",
"time" => "00:33",
"pis" => "16073736879"
),
2 => array(
"nsr" => "000086316",
"type" => "3",
"date" => "2015-07-18",
"time" => "00:58",
"pis" => "16634402451"
)
);
$newarray = array();
$pis = array();
foreach($array as $part){
array_push($pis,$part['pis']);
$newarray = array(
"date" => $part['date'],
"pis" => $pis
);
}
var_dump($newarray);
?>
$array = json_decode($str, true);
// make array date => pis
$tmp = array();
foreach ($array as $item) {
if (!isset($tmp[$item['date']])) $tmp[$item['date']]['pis'] = array();
$tmp[$item['date']]['pis'][] = $item['pis'];
}
// Than move date from key to item
$result = array();
foreach($tmp as $k=>$v)
$result[] = array('date' => $k, 'pis' => $v['pis']);
print_r($result);
I want to create a array for the following json code.
{
"homeMobileCountryCode": 310,
"homeMobileNetworkCode": 260,
"radioType": "gsm",
"carrier": "T-Mobile",
"cellTowers": [
{
"cellId": 39627456,
"locationAreaCode": 40495,
"mobileCountryCode": 310,
"mobileNetworkCode": 260,
"age": 0,
"signalStrength": -95
}
],
"wifiAccessPoints": [
{
"macAddress": "01:23:45:67:89:AB",
"signalStrength": 8,
"age": 0,
"signalToNoiseRatio": -65,
"channel": 8
},
{
"macAddress": "01:23:45:67:89:AC",
"signalStrength": 4,
"age": 0
}
]
}
I have tried with the following but it is showing parsing error in google maps geomatic api
$a = array("homeMobileCountryCode" => 310,
"homeMobileNetworkCode" => 260,
"radioType" => "gsm",
"carrier" => "T-Mobile");
$jsonVal = json_encode($a);
can anyone help me?
PHP's json_encode does not wrap integers with double quotes, which is invalid json. Try this:
$a = array("homeMobileCountryCode" => "310",
"homeMobileNetworkCode" => "260",
"radioType" => "gsm",
"carrier" => "T-Mobile");
$jsonVal = json_encode($a);
From json to Array:
$array = json_decode(/* json text /*);
From Array to Json
$json = json_encode(/* array Object */);
explanations here but you can skip to clean final code further down.
$cellTower1 = array( "cellId"=> "39627456",
"locationAreaCode"=> "40495",
"mobileCountryCode"=> "310",
"mobileNetworkCode"=> "260",
"age"=> "0",
"signalStrength"=> "-95" );
$cellTower2 = array( "cellId"=> "2222222",
"locationAreaCode"=> "22222",
"mobileCountryCode"=> "222",
"mobileNetworkCode"=> "222",
"age"=> "22",
"signalStrength"=> "-22" );
Then combine all cell towers
$allCellTowers[] = $cellTower1;
$allCellTowers[] = $cellTower2;
//etc... or could be in a loop
Now for MAC addresses and wifiAccessPoints.
$macAddress1 = array (
"macAddress"=> "01:23:45:67:89:AB",
"signalStrength" => "8",
"age" => "0",
"signalToNoiseRatio" => "-65",
"channel" => "8"
);
$macAddress2 = array (
"macAddress" => "01:23:45:67:89:AC",
"signalStrength" => "4",
"age" => "0"
);
$macAddress3 = etc...
just as for cellTower1, cellTower2 the macaddresses 1 & 2 above can be populated with a loop.
Adding them to wifiAccessPoints also can be done in a loop but it done manually below just so you understand.
$wifiAccessPoints[] = $macAddress1;
$wifiAccessPoints[] = $macAddress2;
finally the other elements all go in the resulting array to encode
$myarray = array( "homeMobileCountryCode"=> "310",
"homeMobileNetworkCode"=> "260",
"radioType"=> "gsm",
"carrier"=> "T-Mobile",
"cellTowers"=>$allCellTowers,
"wifiAccessPoints" => $wifiAccessPoints
);
$json = json_encode($myarray);
IN CLEAN CODE IT IS
$cellTower1 = array( "cellId"=> "39627456",
"locationAreaCode"=> "40495",
"mobileCountryCode"=> "310",
"mobileNetworkCode"=> "260",
"age"=> "0",
"signalStrength"=> "-95" );
$allCellTowers[] = $cellTower1;
$macAddress1 = array (
"macAddress"=> "01:23:45:67:89:AB",
"signalStrength" => "8",
"age" => "0",
"signalToNoiseRatio" => "-65",
"channel" => "8"
);
$macAddress2 = array (
"macAddress" => "01:23:45:67:89:AC",
"signalStrength" => "4",
"age" => "0"
);
$wifiAccessPoints[] = $macAddress1;
$wifiAccessPoints[] = $macAddress2;
$myarray = array( "homeMobileCountryCode"=> "310",
"homeMobileNetworkCode"=> "260",
"radioType"=> "gsm",
"carrier"=> "T-Mobile",
"cellTowers"=>$allCellTowers,
"wifiAccessPoints" => $wifiAccessPoints
);
//note that you have your first key missing though in your example
$json = json_encode($myarray);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Converting an array from one to multi-dimensional based on parent ID values
I am working in PHP.
I have the following array that has relational data (parent child relationships).
Array
(
[5273] => Array
(
[id] => 5273
[name] => John Doe
[parent] =>
)
[6032] => Array
(
[id] => 6032
[name] => Sally Smith
[parent] => 5273
)
[6034] => Array
(
[id] => 6034
[name] => Mike Jones
[parent] => 6032
)
[6035] => Array
(
[id] => 6035
[name] => Jason Williams
[parent] => 6034
)
[6036] => Array
(
[id] => 6036
[name] => Sara Johnson
[parent] => 5273
)
[6037] => Array
(
[id] => 6037
[name] => Dave Wilson
[parent] => 5273
)
[6038] => Array
(
[id] => 6038
[name] => Amy Martin
[parent] => 6037
)
)
I need it to be in this JSON format:
{
"id":"5273",
"name":"John Doe",
"data":{
},
"children":[
{
"id":" Sally Smith",
"name":"6032",
"data":{
},
"children":[
{
"id":"6034",
"name":"Mike Jones",
"data":{
},
"children":[
{
"id":"6035",
"name":"Jason Williams",
"data":{
},
"children":[
{
"id":"node46",
"name":"4.6",
"data":{
},
"children":[
]
}
]
}
]
},
{
"id":"6036",
"name":"Sara Johnson",
"data":{
},
"children":[
]
},
{
"id":"6037",
"name":"Dave Wilson",
"data":{
},
"children":[
{
"id":"6038",
"name":"Amy Martin",
"data":{
},
"children":[
]
}
]
}
]
}
]
}
I know I need to create a multidimensional array and run it through json_encode(). I also believe this method used to do this needs to be recursive because the real world data could have an unknown number of levels.
I would be glad to show some of my approaches but they have not worked.
Can anyone help me?
I was asked to share my work. This is what I have tried but I have not gotten that close to I don't know how helpful it is.
I made an array of just the relationships.
foreach($array as $k => $v){
$relationships[$v['id']] = $v['parent'];
}
I think (based off another SO post) used this relational data to create a the new multidimensional array. If I got this to work I was going to work on adding in the correct "children" labels etc.
$childrenTable = array();
$data = array();
foreach ($relationships as $n => $p) {
//parent was not seen before, put on root
if (!array_key_exists($p, $childrenTable)) {
$childrenTable[$p] = array();
$data[$p] = &$childrenTable[$p];
}
//child was not seen before
if (!array_key_exists($n, $childrenTable)) {
$childrenTable[$n] = array();
}
//root node has a parent after all, relocate
if (array_key_exists($n, $data)) {
unset($data[$n]);
}
$childrenTable[$p][$n] = &$childrenTable[$n];
}
unset($childrenTable);
print_r($data);
<?php
header('Content-Type: application/json; charset="utf-8"');
/**
* Helper function
*
* #param array $d flat data, implementing a id/parent id (adjacency list) structure
* #param mixed $r root id, node to return
* #param string $pk parent id index
* #param string $k id index
* #param string $c children index
* #return array
*/
function makeRecursive($d, $r = 0, $pk = 'parent', $k = 'id', $c = 'children') {
$m = array();
foreach ($d as $e) {
isset($m[$e[$pk]]) ?: $m[$e[$pk]] = array();
isset($m[$e[$k]]) ?: $m[$e[$k]] = array();
$m[$e[$pk]][] = array_merge($e, array($c => &$m[$e[$k]]));
}
return $m[$r][0]; // remove [0] if there could be more than one root nodes
}
echo json_encode(makeRecursive(array(
array('id' => 5273, 'parent' => 0, 'name' => 'John Doe'),
array('id' => 6032, 'parent' => 5273, 'name' => 'Sally Smith'),
array('id' => 6034, 'parent' => 6032, 'name' => 'Mike Jones'),
array('id' => 6035, 'parent' => 6034, 'name' => 'Jason Williams'),
array('id' => 6036, 'parent' => 5273, 'name' => 'Sara Johnson'),
array('id' => 6037, 'parent' => 5273, 'name' => 'Dave Wilson'),
array('id' => 6038, 'parent' => 6037, 'name' => 'Amy Martin'),
)));
demo: https://3v4l.org/s2PNC
Okay, this is how it works, you were actually not too far off as you started, but what you actually look for are references. This is a general procedure:
As there is a relation between parent and child-nodes on their ID, you first need to index the data based on the ID. I do this here with an array ($rows) to simulate your data access, if you read from the database, it would be similar. With this indexing you can also add additional properties like your empty data:
// create an index on id
$index = array();
foreach($rows as $row)
{
$row['data'] = (object) array();
$index[$row['id']] = $row;
}
So now all entries are indexed on their ID. This was the first step.
The second step is equally straight forward. Because we now can access each node based on it's ID in the $index, we can assign the children to their parent.
There is one "virtual" node, that is the one with the ID 0. It does not exists in any of the rows, however, if we could add children to it too, we can use this children collection as the store for all root nodes, in your case, there is a single root node.
Sure, for the ID 0, we should not process the parent - because it does not exists.
So let's do that. We make use of references here because otherwise the same node could not be both parent and child:
// build the tree
foreach($index as $id => &$row)
{
if ($id === 0) continue;
$parent = $row['parent'];
$index[$parent]['children'][] = &$row;
}
unset($row);
Because we use references, the last line takes care to unset the reference stored in $row after the loop.
Now all children have been assigned to their parents. That could it be already, however lets not forget the last step, the actual node for the output should be accessed.
For brevity, just assign the root node to the $index itself. If we remember, the only root node we want is the first one in the children array in the node with the ID 0:
// obtain root node
$index = $index[0]['children'][0];
And that's it. We can use it now straight away to generate the JSON:
// output json
header('Content-Type: application/json');
echo json_encode($index);
Finally the whole code at a glance:
<?php
/**
* #link http://stackoverflow.com/questions/11239652/php-create-a-multidimensional-array-from-an-array-with-relational-data
*/
$rows = array(
array('id' => 5273, 'parent' => 0, 'name' => 'John Doe'),
array('id' => 6032, 'parent' => 5273, 'name' => 'Sally Smith'),
array('id' => 6034, 'parent' => 6032, 'name' => 'Mike Jones'),
array('id' => 6035, 'parent' => 6034, 'name' => 'Jason Williams'),
array('id' => 6036, 'parent' => 5273, 'name' => 'Sara Johnson'),
array('id' => 6037, 'parent' => 5273, 'name' => 'Dave Wilson'),
array('id' => 6038, 'parent' => 6037, 'name' => 'Amy Martin'),
);
// create an index on id
$index = array();
foreach($rows as $row)
{
$row['data'] = (object) [];
$index[$row['id']] = $row;
}
// build the tree
foreach($index as $id => &$row)
{
if ($id === 0) continue;
$parent = $row['parent'];
$index[$parent]['children'][] = &$row;
}
unset($row);
// obtain root node
$index = $index[0]['children'][0];
// output json
header('Content-Type: application/json');
echo json_encode($index, JSON_PRETTY_PRINT);
Which would create the following json (here with PHP 5.4s' JSON_PRETTY_PRINT):
{
"id": 5273,
"parent": 0,
"name": "John Doe",
"data": {
},
"children": [
{
"id": 6032,
"parent": 5273,
"name": "Sally Smith",
"data": {
},
"children": [
{
"id": 6034,
"parent": 6032,
"name": "Mike Jones",
"data": {
},
"children": [
{
"id": 6035,
"parent": 6034,
"name": "Jason Williams",
"data": {
}
}
]
}
]
},
{
"id": 6036,
"parent": 5273,
"name": "Sara Johnson",
"data": {
}
},
{
"id": 6037,
"parent": 5273,
"name": "Dave Wilson",
"data": {
},
"children": [
{
"id": 6038,
"parent": 6037,
"name": "Amy Martin",
"data": {
}
}
]
}
]
}
Following code will do the job.. you may want to tweak a bit according to your needs.
$data = array(
'5273' => array( 'id' =>5273, 'name'=> 'John Doe', 'parent'=>''),
'6032' => array( 'id' =>6032, 'name'=> 'Sally Smith', 'parent'=>'5273'),
'6034' => array( 'id' =>6034, 'name'=> 'Mike Jones ', 'parent'=>'6032'),
'6035' => array( 'id' =>6035, 'name'=> 'Jason Williams', 'parent'=>'6034')
);
$fdata = array();
function ConvertToMulti($data) {
global $fdata;
foreach($data as $k => $v)
{
if(empty($v['parent'])){
unset($v['parent']);
$v['data'] = array();
$v['children'] = array();
$fdata[] = $v;
}
else {
findParentAndInsert($v, $fdata);
}
}
}
function findParentAndInsert($idata, &$ldata) {
foreach ($ldata as $k=>$v) {
if($ldata[$k]['id'] == $idata['parent']) {
unset($idata['parent']);
$idata['data'] = array();
$idata['children'] = array();
$ldata[$k]['children'][] = $idata;
return;
}
else if(!empty($v['children']))
findParentAndInsert($idata, $ldata[$k]['children']);
}
}
print_r($data);
ConvertToMulti($data);
echo "AFTER\n";
print_r($fdata);
http://codepad.viper-7.com/Q5Buaz