Create nested json object using php mysql - php

I have two tables, table 1 has 2 fields (question_pk, question_name) and table 2 has 4 fields(ans_pk, options, question_fk and right_answer). I want to create json like the following structure
{
"type": "quiz",
"name": "Brand Colors",
"description": "Can you identify these brands by the background color?",
"questions": [
{
"name": "Can you identify this color?",
"description": "#ea4c89",
"answers": [
{
"name": "Dribbble",
"description": "dribbble.png",
"weight": 1
},
{
"name": "Amazon",
"description": "amazon.png",
"weight": 0
},
{
"name": "Apple",
"description": "apple.png",
"weight": 0
}
]
},
{
"name": "Can you identify this color?",
"description": "#3d9ae8",
"answers": [
{
"name": "Youtube",
"description": "youtube.png",
"weight": 0
},
{
"name": "Dropbox",
"description": "dropbox.png",
"weight": 1
},
{
"name": "Wordpress",
"description": "wordpress.png",
"weight": 0
}
]
},
{
"name": "Can you identify this color?",
"description": "#c4302b",
"answers": [
{
"name": "Youtube",
"description": "youtube.png",
"weight": 1
},
{
"name": "Twitter",
"description": "twitter.png",
"weight": 0
},
{
"name": "Vimeo",
"description": "vimeo.png",
"weight": 0
}
]
}
]
}
MY PHP CODE
<?php
include '../config/config.php';
if(isset($_GET['sub_cat_id']))
{
$sub_cat_id = $_GET['sub_cat_id'];
$result = mysql_query("select * from $questions where sub_cat='$sub_cat_id' order by level_fk asc");
$json_response = array(); //Create an array
$i=1;
while ($row = mysql_fetch_array($result))
{
$row_array['qus_pk'] = $row['qus_pk'];
$row_array['question'] = $row['question'];
$qus_pk = $row['qus_pk'];
$option_qry = mysql_query("select * from $qus_ans where qus_pk=$qus_pk");
while ($opt_fet = mysql_fetch_array($option_qry))
{
$row_array['options'] = $opt_fet['options'];
$row_array['right_ans'] = $opt_fet['right_ans'];
array_push($json_response,$row_array); //push the values in the array
}
$i++;
}
echo json_encode($json_response);
}
?>
And My Result I am getting json response like the following
[
{
"qus_pk": "1",
"question": "Ten years ago, P was half of Q in age. If the ratio of their present ages is 3:4, what will be the total of their present ages?",
"options": "45",
"right_ans": "0"
},
{
"qus_pk": "1",
"question": "Ten years ago, P was half of Q in age. If the ratio of their present ages is 3:4, what will be the total of their present ages?",
"options": "40",
"right_ans": "0"
},
{
"qus_pk": "1",
"question": "Ten years ago, P was half of Q in age. If the ratio of their present ages is 3:4, what will be the total of their present ages?",
"options": "35",
"right_ans": "1"
},
{
"qus_pk": "1",
"question": "Ten years ago, P was half of Q in age. If the ratio of their present ages is 3:4, what will be the total of their present ages?",
"options": "50",
"right_ans": "0"
},
{
"qus_pk": "2",
"question": "Father is aged three times more than his son Sunil. After 8 years, he would be two and a half times of Sunil's age. After further 8 years, how many times would he be of Sunil's age?",
"options": "4 times",
"right_ans": "0"
},
{
"qus_pk": "2",
"question": "Father is aged three times more than his son Sunil. After 8 years, he would be two and a half times of Sunil's age. After further 8 years, how many times would he be of Sunil's age?",
"options": "1 times",
"right_ans": "0"
},
{
"qus_pk": "2",
"question": "Father is aged three times more than his son Sunil. After 8 years, he would be two and a half times of Sunil's age. After further 8 years, how many times would he be of Sunil's age?",
"options": "3 times",
"right_ans": "1"
},
{
"qus_pk": "2",
"question": "Father is aged three times more than his son Sunil. After 8 years, he would be two and a half times of Sunil's age. After further 8 years, how many times would he be of Sunil's age?",
"options": "5 times",
"right_ans": "0"
}
]
In my respose each time the question is repeated so how to avoid and if i want to achive the first json structure, in my PHP code what&where i need to make changes?. If any one knows help me.

Hi try this,
<?php
include '../config/config.php';
if(isset($_GET['sub_cat_id']))
{
$sub_cat_id = $_GET['sub_cat_id'];
$result = mysql_query("SELECT * FROM $questions WHERE sub_cat='$sub_cat_id' ORDER BY level_fk ASC");
$json_response = array(); //Create an array
while ($row = mysql_fetch_array($result))
{
$row_array = array();
$row_array['qus_pk'] = $row['qus_pk'];
$row_array['question'] = $row['question'];
$row_array['answers'] = array();
$qus_pk = $row['qus_pk'];
$option_qry = mysql_query("SELECT * FROM $qus_ans WHERE qus_pk=$qus_pk");
while ($opt_fet = mysql_fetch_array($option_qry))
{
$row_array['answers'][] = array(
'options' => $opt_fet['options'],
'right_ans' => $opt_fet['right_ans'],
);
}
array_push($json_response, $row_array); //push the values in the array
}
echo json_encode($json_response);
}
?>

I think this code is easier to figure out and by the way it uses mysqli ...
This is based on my own data structure, I am in the middle of something and I have no time a.t.m. to adapt it to the question but should easy to figure out how to adapt it to other structures :
$usersList_array =array();
$user_array = array();
$note_array = array();
$fetch_users = mysqli_query($mysqli, "SELECT
ID,
Surname,
Name
FROM tb_Users
WHERE Name LIKE 'G%'
ORDER BY ID") or die(mysqli_error($mysqli));
while ($row_users = mysqli_fetch_assoc($fetch_users)) {
$user_array['id'] = $row_users['ID'];
$user_array['surnameName'] = $row_users['Surname'].' '.$row_users['Name'];
$user_array['notes'] = array();
$fetch_notes = mysqli_query($mysqli, "SELECT
id,
dateIns,
type,
content
FROM tb_Notes
WHERE fk_RefTable = 'tb_Users' AND
fk_RefID = ".$row_users['ID'].""
) or die(mysqli_error($mysqli));
while ($row_notes = mysqli_fetch_assoc($fetch_notes)) {
$note_array['id']=$row_notes['id'];
$note_array['dateIns']=$row_notes['dateIns'];
$note_array['type']=$row_notes['type'];
$note_array['content']=$row_notes['content'];
array_push($user_array['notes'],$note_array);
}
array_push($usersList_array,$user_array);
}
$jsonData = json_encode($usersList_array, JSON_PRETTY_PRINT);
echo $jsonData;
Resulting JSON :
[
{
"id": "1",
"surnameName": "Xyz Giorgio",
"notes": [
{
"id": "1",
"dateIns": "2016-05-01 03:10:45",
"type": "warning",
"content": "warning test"
},
{
"id": "2",
"dateIns": "2016-05-18 20:51:32",
"type": "error",
"content": "error test"
},
{
"id": "3",
"dateIns": "2016-05-18 20:53:00",
"type": "info",
"content": "info test"
}
]
},
{
"id": "2",
"cognomeNome": "Xyz Georg",
"notes": [
{
"id": "4",
"dateIns": "2016-05-20 14:38:20",
"type": "warning",
"content": "georg warning"
},
{
"id": "5",
"dateIns": "2016-05-20 14:38:20",
"type": "info",
"content": "georg info"
}
]
}
]

A basic class to handle nesting tables into a php array.
PHP CLASS
// chain data into a php array, filtering by relation to parent, based on a structure definition array
// nest child data by relation to parent data
// assign a array label "arr_label" to child definition to define what key the filtered data will use
// assign a parent key "p_key" and a child key "c_key" to child definition to assign connection points from child to parent
// load array data to filter into "arr" key on child definition
class class_chain_filter
{
var $return_arr;
function __construct()
{
} // CONSTRUCTOR
// input a defined filter tree array and output a processed result
function chain_filter($filter_tree)
{
// can feed either a single record a set of rows...
if(!$this->is_assoc($filter_tree['arr']))
$this->return_arr = $filter_tree['arr']; // root for return array
else
$this->return_arr[] = $filter_tree['arr']; // force a numeric array so return is consistent.
$this->do_chain_filter( $filter_tree['next_arrs'], $this->return_arr );
return $this->return_arr;
} // $this->chain_filter($filter_tree) // public
function is_assoc($arr)
{
return array_keys($arr) !== range(0, count($arr) - 1);
}
function do_chain_filter(&$tree_arr, &$final_arr)
{
$cur_final_node = &$final_arr;
if( !is_array($cur_final_node) )
return false;
// send the next_arrs
foreach($final_arr as $f_key => $f_arr)
{
$cur_final_node = &$final_arr[$f_key];
foreach($tree_arr as $n_key => $n_arr)
{
$cur_tree_node = $tree_arr[$n_key];
// $final_cur_el['arr_label'] = 'true';
$next_final_node = &$cur_final_node[$cur_tree_node['arr_label']];
// data up hombre
// filter out array elements not related to parent array
$result = $this->children_of_parent(
$cur_final_node,
$cur_tree_node['arr'],
$cur_tree_node['p_key'],
$cur_tree_node['c_key']
);
$next_final_node = $result;
// now recurse if we have more depths to travel...
if(!empty($cur_tree_node['next_arrs']))
$this->do_chain_filter($cur_tree_node['next_arrs'], $next_final_node);
}
}
} // this->function chain_filter(&$tree_arr, &$final_arr)
// take 2 arrays
// first array is an associative array.
// second array is an array of associative arrays.
// return children of second array that belong to parent array
function children_of_parent($arr_parent, $arr_children, $key_parent, $key_child )
{
// parent = a record
// child = multiple records
// filter out children that don't apply to parent.
// return the result
$parent_id = $arr_parent[$key_parent];
foreach($arr_children as $arr_child)
{
$child_id = $arr_child[$key_child];
if($child_id == $parent_id)
$return_arr[] = $arr_child;
}
if(!empty($return_arr))
return $return_arr;
} // this->children_of_parent($arr_parent, $arr_children, $key_parent, $key_child )
} // end. class class_chain_filter
LOAD UP SOME TABLES (USE YOUR OWN PREFERRED DB CLASS)
$areas = $db->get("SELECT * FROM areas");
$rooms = $db->get("SELECT * FROM rooms");
$exits = $db->get("SELECT * FROM exits");
DEFINE OUR RETURNED ARRAY TREE STRUCTURE
// predefine tree structure for generation
// structure definition array example...
$tree_arr = array (
"arr" => $areas, // root (can be multiple rows or a single record)
"next_arrs" => array ( // children
0 => array(
"arr" => $rooms, // array to filter against parent
"arr_label" => "rooms", // for the php array label
"p_key" => "id", // field name of parent // eg) id
"c_key" => "areaid", // this array's field name that links it to parent
"next_arrs" => array( // children
0 => array(
"arr" => $exits, // array to filter against parent
"arr_label" => "exits", // for the php array label
"p_key" => "id", // field name of parent / blank if root / eg) id
"c_key" => "roomid" // this array's field name that links it to parent
)
)
)
)
); // $tree_arr
NOW CREATE OBJECT AND PROCESS INTO DESTINATION ARRAY
$c = new class_chain_filter();
$return_arr = $c->chain_filter($tree_arr);
print_r($return_arr);
... AND THE OUTPUT SHOULD LOOK LIKE ...
Array (
[0] => Array
(
[id] => 1
[name] => New World
[author] => anon
[resetfreq] => 3
[rooms] => Array
(
[0] => Array
(
[id] => 1
[areaid] => 1
[name] => Entrance
[description] => The air is humid here.
[exits] => Array
(
[0] => Array
(
[id] => 1
[roomid] => 1
[toroomid] => 2
[direction] => n
[description] => A Hall
[keyid] => 1
)
[1] => Array
(
[id] => 5
[roomid] => 1
[toroomid] => 3
[direction] => s
[description] => Entrance
[keyid] =>
)
)
)
[1] => Array
(
[id] => 2
[areaid] => 1
[name] => A Corridor
[description] => Seems nothing is really going on in this room. Bland tapestry and nothing worth really hanging around for. From the west comes the sound of people training. To the east you can hear people practicing skills and abilities.
[exits] => Array
(
[0] => Array
(
[id] => 2
[roomid] => 2
[toroomid] => 1
[direction] => s
[description] => A Corridor
[keyid] =>
)
[1] => Array
(
[id] => 7
[roomid] => 2
[toroomid] => 4
[direction] => e
[description] => Practice Room
[keyid] =>
)
[2] => Array
(
[id] => 9
[roomid] => 2
[toroomid] => 5
[direction] => w
[description] => Training Room
[keyid] =>
)
[3] => Array
(
[id] => 11
[roomid] => 2
[toroomid] => 8
[direction] => n
[description] => A Bend
[keyid] =>
)
)
)
)
)
)
and then you could just json_encode the array to turn it from a PHP array into a JSON string

Related

Merge 2 Multi-dimensional arrays with some duplicate values

I have two arrays that sometimes have matching keys, and I need to merge these values based on the matching fields (make/model/year) and set accordingly based on a value flag each array object has (in this case either fit: B or fit: R)
The first array looks like this and contains fit B's:
$firstArray = [
{ Model: Aston Martin, Make: V12, Year: 2012, fit: B, front_fit: 52, rear_fit: null}
]
Here is the second array and it contains only fit R's:
$secondArray = [
{ Model: Aston Martin, Make: V12, Year: 2012, fit: R, front_fit: null, rear_fit: 24}
]
Notice how most of the values are the same except fit and the front or rear fit based on that flag (B or R). Ideally I would like to merge these two in a new array that would remove the fit and fill out the front_fit and rear_fit fields accordingly:
$mergedNewArray = [
{ Model: Aston Martin, Make: V12, Year: 2012, front_fit: 52, rear_fit: 24}
]
There is a potential third scenario to happen where there may not be a matching model/make/year object in one of the arrays, if this is the case I still want to include the existing record in the new array
so it would turn into something like this:
$mergedNewArray = [
{ Model: Aston Martin, Make: V12, Year: 2012, front_fit: 52, rear_fit: 24},
//This has no matching object to merge with but should still be in this array
{ Model: Dodge, Make: Charger, Year: 2006, front_fit: null, rear_fit: 80}
]
Any help would be much appreciated, The less verbose the better
Edit: Added clarification for the first array and second array containing specific fits
Probably the easiest way to do this is to re-index the first array by the make/model/year, and then iterate over the second array, looking for the same car and if found, updating the record, otherwise creating a new entry in the output. The result array can then be re-indexed numerically using array_values:
$firstArray = json_decode('[
{ "Model": "Aston Martin", "Make": "V12", "Year": 2012, "fit": "B", "front_fit": 52, "rear_fit": null },
{ "Model": "Aston Martin", "Make": "Vantage", "Year": 2017, "fit": "B", "front_fit": 40, "rear_fit": null }
]', true);
$secondArray =json_decode('[
{ "Model": "Aston Martin", "Make": "V12", "Year": 2012, "fit": "R", "front_fit": null, "rear_fit": 24 },
{ "Model": "Dodge", "Make": "Charger", "Year": 2006, "fit": "R", "front_fit": null, "rear_fit": 80 }
]', true);
$result = array();
// index the first array by the model, make and year
foreach ($firstArray as $car) {
unset($car['fit']);
$key = $car['Model'] . '#' . $car['Make'] . '#' . $car['Year'];
$result[$key] = $car;
}
foreach ($secondArray as $car) {
$key = $car['Model'] . '#' . $car['Make'] . '#' . $car['Year'];
// have we seen this car?
if (isset($result[$key])) {
// if so, update
$result[$key]['rear_fit'] = $car['rear_fit'];
}
else {
// create a new entry
unset($car['fit']);
$result[$key] = $car;
}
}
// reindex to numeric keys
$result = array_values($result);
print_r($result);
Output:
Array
(
[0] => Array
(
[Model] => Aston Martin
[Make] => V12
[Year] => 2012
[front_fit] => 52
[rear_fit] => 24
)
[1] => Array
(
[Model] => Aston Martin
[Make] => Vantage
[Year] => 2017
[front_fit] => 40
[rear_fit] =>
)
[2] => Array
(
[Model] => Dodge
[Make] => Charger
[Year] => 2006
[front_fit] =>
[rear_fit] => 80
)
)
Demo on 3v4l.org

Need help parsing JSON output

I'm making a REST call in PHP and returning JSON. I'm eventually putting this into tables, but trying to output the value for now to get the hang of it.
There are values in the "data" field I would like to get into variables. In my example they would be "201807", 23.43 and "201806", 22.54. It's the first two values of the property "data"
My code looks like this:
<?php
$service_url = "http://api.eia.gov/geoset/?geoset_id=ELEC.PRICE.RES.M&regions=USA-AL,USA-AK,USA-AR&api_key=3a8b92cfaf3a21e2e990f228c9152eeb&out=json&start=2018";
$get_data = callAPI('GET', $service_url, false);
$response = json_decode($get_data);
foreach ($response as $r) {
echo $row->name;
echo $row->geoset_id;
}
?>
And JSON looks like this:
{
"geoset": {
"geoset_id": "ELEC.PRICE.RES.M",
"setname": "Average retail price of electricity : residential : monthly",
"f": "M",
"units": "cents per kilowatthour",
"unitsshort": null,
"series": {
"USA-AK": {
"series_id": "ELEC.PRICE.AK-RES.M",
"name": "Average retail price of electricity : Alaska : residential : monthly",
"region": "USA-AK",
"latlon": null,
"unitsshort": null,
"start": "200101",
"end": "201807",
"data": [
["201807", 23.43],
["201806", 22.54],
["201805", 22.16],
["201804", 21.61],
["201803", 21.47],
["201802", 21.11],
["201801", 21.67]
]
},
"USA-AL": {
"series_id": "ELEC.PRICE.AL-RES.M",
"name": "Average retail price of electricity : Alabama : residential : monthly",
"region": "USA-AL",
"latlon": null,
"unitsshort": null,
"start": "200101",
"end": "201807",
"data": [
["201807", 12.28],
["201806", 12.41],
["201805", 12.49],
["201804", 12.79],
["201803", 12.65],
["201802", 12.29],
["201801", 11.59]
]
},
"USA-AR": {
"series_id": "ELEC.PRICE.AR-RES.M",
"name": "Average retail price of electricity : Arkansas : residential : monthly",
"region": "USA-AR",
"latlon": null,
"unitsshort": null,
"start": "200101",
"end": "201807",
"data": [
["201807", 9.98],
["201806", 9.99],
["201805", 9.89],
["201804", 10],
["201803", 10.47],
["201802", 9.8],
["201801", 9.36]
]
}
}
}
}
So far I'm getting NULL values back.
As you have used json_decode your result will be convert to object type. so you should do as follows:
$geoset = $response->geoset;
$geosetid = $geoset->geoset_id;
foreach ($geoset->series as $row) {
echo $row->name;
$data = $row->data; //this will produce the values as array
}
Your data array will be like this:
Array
(
[0] => Array
(
[0] => 201807
[1] => 23.43
)
[1] => Array
(
[0] => 201806
[1] => 22.54
)
)
...
Hopefully this helps! :)
Your code will never work, because:
You iterate $response as $r, but use $row inside;
The $response has only one child: geoset. There is no $response->name neither $response->geoset_id.
If you want the geoset_id, you must access using: $response->geoset->geoset_id.
To achieve this:
There are values in the "data" field I would like to get into variables
Use the following code:
foreach ($response->geoset->series as $series) {
foreach ($series->data as $data) {
$date = $data[0];
$value = $data[1];
}
}

PHP - Making a nested tree menu structure from a flat array

I am making a nested menu array from the response that I get from WP database. I am getting the data from WP in the controller in Laravel with the help of corcel package, and then making an array with menu data, which is now one level deep. So, when a menu link has a submenu links, the array looks like this:
{
"Hjem": {
"ID": 112,
"title": "Hjem",
"slug": "hjem",
"url": "http://hivnorge.app/?p=112",
"status": "publish",
"main_category": "Hovedmeny",
"submenus": [
{
"ID": 129,
"title": "Lorem ipsum",
"slug": "lorem-ipsum",
"url": "http://hivnorge.app/?p=129",
"status": "publish",
"main_category": "Nyheter"
}
]
},
"Nytt test innlegg": {
"ID": 127,
"title": "Nytt test innlegg",
"slug": "nytt-test-innlegg",
"url": "http://hivnorge.app/?p=127",
"status": "private",
"main_category": "Nyheter",
"submenus": [
{
"ID": 125,
"title": "Test innlegg",
"slug": "test-innlegg",
"url": "http://hivnorge.app/?p=125",
"status": "publish",
"main_category": "Nyheter"
},
{
"ID": 129,
"title": "Lorem ipsum",
"slug": "lorem-ipsum",
"url": "http://hivnorge.app/?p=129",
"status": "publish",
"main_category": "Nyheter"
}
]
},
"Prosjektsamarbeidets verdi": {
"ID": 106,
"title": "Prosjektsamarbeidets verdi",
"slug": "prosjektsamarbeidets-verdi",
"url": "http://hivnorge.no.wordpress.seven.fredrikst/?p=106",
"status": "publish",
"main_category": "Prevensjon"
}
}
This is how I am creating this response:
$menu = Menu::slug('hovedmeny')->first();
$res = [];
foreach ($menu->nav_items as $item) {
$item->makeHidden($hiddenAttributes)->toArray();
$parent_id = $item->meta->_menu_item_menu_item_parent;
if ($parent_id == '0') {
if ($item->title == '') {
$item = $this->findPost($item);
}
$parentItem = $item;
$res[$parentItem->title] = $parentItem->makeHidden($hiddenAttributes)->toArray();
}
else {
$childItem = $this->findPost($item);
$res[$parentItem->title]['submenus'][] = $childItem->makeHidden($hiddenAttributes)->toArray();
}
}
return $res;
The problem I have is that the response from WP only returns parent_id for each $item and no data about if an item has some children, so this is the meta data of the parent item for example:
#attributes: array:4 [
"meta_id" => 209
"post_id" => 112
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "0"
]
And this is the meta data of the child item:
#attributes: array:4 [
"meta_id" => 326
"post_id" => 135
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "112"
]
How can I make this flexible and enable deeper nesting, so that I can have submenus inside submenus?
I have tried to look for the solution here, because that is pretty much the same problem as mine, but wasn't able to implement it.
In my array menu items also have only parent_id, and the parent_id that is 0 is considered as a root element. Also the parent_id if the menu item is a post, points to the meta id, and not the id of the post that I need, so I need to get that additionaly from meta->_menu_item_object_id.
UPDATE
I have managed to make a tree like structure, but the problem I have now is that I don't know how to get the title for the menu elements that are posts. I did that in the previous example by checking if the title is empty then I would search for that post by id:
if ($item->title == '') {
$item = $this->findPost($item);
}
But, with the new code, where I am making a tree like structure I am not sure how to do that, since then I am not able to make the tree structure, since I am comparing everything with the id, and the ids of the menu element is different from the id of the post that is pointing to, so then I am not able to make the tree structure:
private function menuBuilder($menuItems, $parentId = 0)
{
$hiddenAttributes = \Config::get('middleton.wp.menuHiddenAttributes');
$res = [];
foreach ($menuItems as $index => $item) {
$itemParentId = $item->meta->_menu_item_menu_item_parent;
if ($itemParentId == $parentId) {
$children = self::menuBuilder($menuItems, $item->ID);
if ($children) {
$item['submenu'] = $children;
}
$res[$item->ID] = $item->makeHidden($hiddenAttributes)->toArray();
unset($menuItems[$index]);
}
}
return $res;
}
So, then the data I get is:
{
"112": {
"ID": 112,
"submenu": {
"135": {
"ID": 135,
"title": "",
"slug": "135",
"url": "http://hivnorge.app/?p=135",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "Hjem",
"slug": "hjem",
"url": "http://hivnorge.app/?p=112",
"status": "publish",
"main_category": "Hovedmeny"
},
"136": {
"ID": 136,
"submenu": {
"137": {
"ID": 137,
"submenu": {
"138": {
"ID": 138,
"title": "",
"slug": "138",
"url": "http://hivnorge.app/?p=138",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "",
"slug": "137",
"url": "http://hivnorge.app/?p=137",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "",
"slug": "136",
"url": "http://hivnorge.app/?p=136",
"status": "publish",
"main_category": "Hovedmeny"
},
"139": {
"ID": 139,
"title": "",
"slug": "139",
"url": "http://hivnorge.app/?p=139",
"status": "publish",
"main_category": "Hovedmeny"
}
}
One way to solve this to make use of variable aliases. If you take care to manage a lookup-table (array) for the IDs you can make use of it to insert into the right place of the hierarchical menu array as different variables (here array entries in the lookup table) can reference the same value.
In the following example this is demonstrated. It also solves the second problem (implicit in your question) that the flat array is not sorted (the order is undefined in a database result table), therefore a submenu entry can be in the resultset before the menu entry the submenu entry belongs to.
For the example I created a simple flat array:
# some example rows as the flat array
$rows = [
['id' => 3, 'parent_id' => 2, 'name' => 'Subcategory A'],
['id' => 1, 'parent_id' => null, 'name' => 'Home'],
['id' => 2, 'parent_id' => null, 'name' => 'Categories'],
['id' => 4, 'parent_id' => 2, 'name' => 'Subcategory B'],
];
Then for the work to do there are tow main variables: First the $menu which is the hierarchical array to create and second $byId which is the lookup table:
# initialize the menu structure
$menu = []; # the menu structure
$byId = []; # menu ID-table (temporary)
The lookup table is only necessary as long as the menu is built, it will be thrown away afterwards.
The next big step is to create the $menu by traversing over the flat array. This is a bigger foreach loop:
# build the menu (hierarchy) from flat $rows traversable
foreach ($rows as $row) {
# map row to local ID variables
$id = $row['id'];
$parentId = $row['parent_id'];
# build the entry
$entry = $row;
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
# register the entry in the menu structure
if (null === $parentId) {
# special case that an entry has no parent
$menu[] = &$entry;
} else {
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
}
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
# unset foreach (loop) entry alias
unset($entry);
}
This is where the entries are mapped from the flat array ($rows) into the hierarchical $menu array. No recursion is required thanks to the stack and lookup-table $byId.
The key point here is to use variable aliases (references) when adding new entries to the $menu structure as well as when adding them to $byId. This allows to access the same value in memory with two different variable names:
# special case that an entry has no parent
$menu[] = &$entry;
...
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
It is done with the = & assignment and it means that $byId[$id] gives access to $menu[<< new key >>].
The same is done in case it is added to a submenu:
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
...
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
Here $byId[$id] points to $menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ].
This is solves the problem to always find the right place where to insert a new entry into the hierarchical structure.
To deal with the cases that a submenu comes in the flat array before the menu entry it belongs to, the submenu when initialized for new entries needs to be taken out of the lookup table (at [1]):
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
This is a bit of a special case. In case that $byId[$id]['submenus'] is not yet set (e.g. in the first loop), it is implicitly set to null because of the reference (the & in front of &$byId[$id]['submenus']). In case it is set, the existing submenu from a not yet existing entry will be used to initialize the submenu of the entry.
Doing so is enough to not depend on any specific order in $rows.
This is what the loop does.
The rest is cleanup work:
# unset ID aliases
unset($byId);
It unsets the look ID table as it is not needed any longer. That is, all aliases are unset.
To complete the example:
# visualize the menu structure
print_r($menu);
Which then gives the following output:
Array
(
[0] => Array
(
[id] => 1
[parent_id] =>
[name] => Home
[submenus] =>
)
[1] => Array
(
[id] => 2
[parent_id] =>
[name] => Categories
[submenus] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 2
[name] => Subcategory A
[submenus] =>
)
[1] => Array
(
[id] => 4
[parent_id] => 2
[name] => Subcategory B
[submenus] =>
)
)
)
)
I hope this is understandable and you're able to apply this on your concrete scenario. You can wrap this in a function of it's own (which I would suggest), I only kept it verbose for the example to better demonstrate the parts.
Related Q&A material:
Php: Converting a flat array into a tree like structure
Convert a series of parent-child relationships into a hierarchical tree?
Build a tree from a flat array in PHP
So you would need to write a recursive function see What is a RECURSIVE Function in PHP?
So something like
function menuBuilder($menuItems){
foreach($menuItems as $key => $item)
{
if(!empty($item->children)){
$output[$key] = menuBuilder($item->children);
}
}
return $output;
}

Build new custom Array from Wordpress $wpdb->get_results array

I'm currently taking the results of a table and using wp_send_json to using it as a JSON response. The data is encoded as expected, however I'd like to tweak the output a bit by changing the keys, formating, and order. I'm not sure how to rebuild the array and encode as json after so I'm looking for a little bit of help.
$stuff= $wpdb->get_results( $wpdb->prepare("SELECT * FROM wp_table"), ARRAY_A);
wp_send_json($stuff);
As of now the results I get via print_r look as follows.
Array(
[0] => Array(
[id] => 1[gender] => Male[email] => test#loas . com[lat] => 38[long] => - 97[country_srt] => USA[country_long] => UnitedStates
) [1] => Array(
[id] => 2[gender] => Female[email] => femal#test . com[lat] => 38[long] => - 97[country_srt] => USA[country_long] => UnitedStates
)
)
When encoded I get:
[{
"id": "1",
"gender": "Male",
"email": "test#loas.com",
"lat": "45",
"long": "-76",
"country_srt": "USA",
"country_long": "United States"
}, {
"id": "2",
"gender": "Female",
"email": "femal#test.com",
"lat": "98",
"long": "-34",
"country_srt": "USA",
"country_long": "United States"
}]
Thing is, I don't really need some of these values and also need to format some things to output for easy map plotting. For instance the country longform and gender go into an html formatted string. What I'm looking to do is transform this array to result in:
[ idhere: {
"value": "1",
"latitude": "45",
"longitude": "-76",
"tooltip": {"content":"HTML Showing gender variable and country variable"}
}, idhere: {
"value": "2",
"latitude": "98",
"longitude": "-34",
"tooltip": {"content":"HTML Showing gender variable and country variable"}
}]
I think what you need to do is break down the process down into steps (so you can change the data around) instead of sending your sql data to json directly.
build your own array
iterate over your sql result set while adding your own markup
send the output to json
something like:
$preJSON = array();
// select only columns you need
$sql = "SELECT id, gender, country_srt, lat, long
FROM wp_table"
$count = 0; // this is for $preJSON[] index
foreach( $wpdb->get_results( $sql ) as $key => $row ) {
// each column in your row will now be accessible like this:
// $my_column = $row->column_name;
// now we can do:
$value = $row->id;
$latitude = $row->lat;
$longitude = $row->long;
$gender = $row->gender;
$country = $row->country_srt;
$tooltip = array(
"content" => "HTML and stuff" . $gender . "more HTML and stuff" . $country
);
// now we can build a row of this information in our master array
$preJSON[$count] = array(
"value" => $value,
"latitude" => $latitude,
"longitude" => $longitude,
"tooltip" => $tooltip
);
// increment the index
++$count;
}
// after foreach
// send the whole array to json
$json = json_encode( $preJSON );
I believe this should be the basic gist of what you need

How do I remove nested object from an object in CakePHP?

CakePHP API returns result like this:
{
"status": "OK",
"themes": [
{
"Theme": {
"id": "20",
"user_id": "50",
"name": "dwdwdw",
"language_code_from": "cz",
"language_code_to": "en",
"type": "CUSTOM",
"created": "2014-10-19 15:36:05",
"count_of_cards": 0
}
}
]
}
I would like to ask, how can in remove nested Theme object to get result like this?:
{
"status": "OK",
"themes": [
{
"id": "20",
"user_id": "50",
"name": "dwdwdw",
"language_code_from": "cz",
"language_code_to": "en",
"type": "CUSTOM",
"created": "2014-10-19 15:36:05",
"count_of_cards": 0
}
]
}
Here is my CakePHP code:
$this->Theme->recursive = -1;
// GET USER ID
$themeData['user_id'] = $isSessionValid;
// GET ALL THEMES RELATED TO USER
$foundThemes = $this->Theme->find('all', array(
'conditions' => array(
'Theme.user_id' => $themeData['user_id'])
)
);
$themes = array();
// FOREACH THEMES AND GET COUNT FOR CARDS FOR EACH THEME
foreach($foundThemes as $foundTheme) {
// GET COUNT OF QUESTIONS FOR ACTUAL THEME
$countOfCards = $this->Theme->Card->find('count', array(
'conditions' => array(
'Card.theme_id' => $foundTheme['Theme']['id'])
)
);
// APPEND TO ACTUAL ARRAY
$foundTheme['Theme']['count_of_cards'] = $countOfCards;
array_push($themes,$foundTheme);
}
// SET SUCCESS RESPOSNSE
$this->set(array(
'status' => 'OK',
'themes' => $themes,
'_serialize' => array(
'status',
'themes',
)
));
Many thanks for any advice.
You can manipulate CakePHP's array formats using its built in Hash utility: http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash
What I would do would be to flatten the results:
$results = Hash::flatten($results);
Your data array will end up as a single dimensional array looking like this:
$results = array(
'status' => 'OK'
'themes.0.Theme.id' => 20,
...
'themes.1.Theme.id' => 21,
...
);
You can then use string replace to remove "Theme" from your keys:
$keys = array_keys($results);
$keys = str_replace('Theme.', '', $keys);
Then you can use Hash::expand to get your original array, now formatted how you want:
$results = Hash::expand(array_combine($keys, array_values($results)));
I dont think CakePHP supports this. if you want to do this with an easy way check the Set Utility.
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html

Categories