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

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;
}

Related

How can I build an object/array?

I am rather new to PHP so I don't know how to work with these datasets. I make a MySQL select and get back an object like this:
{
"membername": "NAME",
"bookingdate": "2020-02-03",
"categoryid": 1,
"dailyworkhourssum": "7.70"
},
{
"membername": "NAME",
"bookingdate": "2020-02-03",
"categoryid": 3,
"dailyworkhourssum": "1.2"
},
{
"membername": "NAME",
"bookingdate": "2020-02-05",
"categoryid": 3,
"dailyworkhourssum": "7.70"
},
I want to iterate through this and in the end it should look like this:
{
"membername": "NAME",
"bookingdate": "2020-02-03",
"categoryid1": true,
"categorid3": true,
"dailyworkhourssum1": "7.70",
"dailyworkhourssum3": "1.2"
},
{
"membername": "NAME",
"bookingdate": "2020-02-05",
"categoryid": 3,
"dailyworkhourssum": "7.70"
},
What this does is that it merges tow fields together (if they have the same bookingdate )into one so that I can display it in a table without reoccurring dates.
My problem:
I don't know what this type of data is called.
I don't know how to create something like this.
I can add fields to this type of data with $data->newField = example so I think that this is an object.
In JS it's called an object, but in PHP you will use an associative array instead.
In your case, I think, you have an array of associative arrays. It looks like this:
$books = [
[
"membername" => "NAME",
"bookingdate" => "2020-02-03",
"categoryid" => 1,
"dailyworkhourssum" => "7.70"
],
[
"membername" => "NAME",
"bookingdate" => "2020-02-03",
"categoryid" => 3,
"dailyworkhourssum" => "1.2"
],
[
"membername" => "NAME",
"bookingdate" => "2020-02-05",
"categoryid" => 3,
"dailyworkhourssum" => "7.70"
]
];
If you wanna merge an arrays with the same "bookingdate" then I recommend you to loop through this array and add its elements to another associative array with bookingdates as keys, and check, in case if there is such key already, then merge the arrays, like this:
$merged = [];
foreach ($books as $book) {
$date = $book['bookingdate'];
if (isset($merged[$date])) {
$merged[$date] = $merged[$date] + $book;
} else {
$merged[$date] = $book;
}
}
I think that it is not a valid code (no time, sorry), but I hope, you cautch the idea.
If you want a 'list' instead of an associative array, than you can do this:
$mergedList = array_values($merged);
Thus you will rid of string keys.
If I understood correctly, you obtain a table with 4 columns an a variable number of rows and you want to transform it to a table with a variable number of columns. For that, using a data structure where every item is different from the previous one can make everything harder than it needs. I'd suggest you use a fixed structure:
// I'm assuming you have a PHP array as starting point
$input = [
[
'membername' => 'NAME',
'bookingdate' => '2020-02-03',
'categoryid' => 1,
'dailyworkhourssum' => '7.70',
],
[
'membername' => 'NAME',
'bookingdate' => '2020-02-03',
'categoryid' => 3,
'dailyworkhourssum' => '1.2',
],
[
'membername' => 'NAME',
'bookingdate' => '2020-02-05',
'categoryid' => 3,
'dailyworkhourssum' => '7.70',
],
];
$output = [];
foreach ($input as $data) {
// We'll group by booking date
if (!isset($output[$data['bookingdate']])) {
$output[$data['bookingdate']] = [
'membername' => $data['membername'],
'bookingdate' => $data['bookingdate'],
'categoryid' => $data['categoryid'],
'dailyworkhourssum' => [],
];
}
// A single date may have several daily work hours
$output[$data['bookingdate']]['dailyworkhourssum'][] = $data['dailyworkhourssum'];
}
// We discard array keys (we only needed them to group)
echo json_encode(array_values($output));
[{
"membername": "NAME",
"bookingdate": "2020-02-03",
"categoryid": 1,
"dailyworkhourssum": ["7.70", "1.2"]
}, {
"membername": "NAME",
"bookingdate": "2020-02-05",
"categoryid": 3,
"dailyworkhourssum": ["7.70"]
}]
Wherever you consume this JSON you just need to loop the dailyworkhourssum array. You may also want to loop the entire structure before printing the table and keep a counter in order to determine the maximum number of columns so you can draw empty cells where needed (tables are rectangular).

PHP Mongo aggregate match regex

Performing a query that simulates a 'like/mysql' searching for teams on the name of the team
Team document structure
{
"_id": 9,
"name": "azerty",
"tag": "dsfds",
"desc": "ggdfgsdfgdfgdf",
"captain": 8,
"coach": 8,
"members": [{
"date_joined": "2016-03-31 15:22:09",
"user_id": 8
}, {
"date_joined": "2016-03-31 19:22:35",
"user_id": 9
}],
"current_invites": [{
"invite_id": 21,
"username": "Nikki",
"user_id": "9",
"status": 1,
"date_invited": "2016-03-31 18:32:40"
}, {
"invite_id": 22,
"username": "Nikki",
"user_id": "9",
"status": 2,
"date_invited": "2016-03-31 18:33:16"
}]
}
PHP Code =
$q = '/.*'.$q.'*./';
$result = $this->coll->aggregate(
array('$match' => array('name' => $q)),
array('$project' => array('name' => 1,'members' => array('$size' => '$members'))));
Feels like I'm going mad not knowing how to fix this.
Have used regex before after migrating to mongo but not with the combination of agg-match.
in my case i am finding the aggregated result in which you can not set the where clause so use the aggregated functions like $sort $unwind $orderby and so on i am using the all of the above mention and have the problem with the like stuff to match the string like %str% here my code in which i implement the like using $match with MongoRegex
public function getRecords($table,$where = array(),$like_key = false,$like_value = false,$offset = 1,$limit = 10,$order_column = false,$order_type = false, $isAggregate=false,$pipeline=array()){
$temp = $this->getMongoDb()->where($where);
if($like_key && $like_value){
$temp = $temp->like($like_key,$like_value);
// this like filter is for aggregated result work both on normal get record or by aggregated result
$pipeline[]=array(
'$match' => array( $like_key => new MongoRegex( "/$like_value/i" ) )
);
}
if($order_column && $order_type){
$order_by = array();
$order_by[$order_column] = $order_type;
$temp = $temp->order_by($order_by);
$pipeline[]=array(
'$sort'=>array($order_column => ($order_type =="desc")? -1 : 1)
);
}
I got the solution when I read the following aggregation framework go the aggregation framework
I hope you will get your solution to resolve your issue.

getting relational results from three tables into one nested array

i have googled for solution to my problem but nun helped me.
here i have three tables items, feeds and images. each item has one feed and one or more images.
i have 3 functions. one is to return records from items table the second one receives feeds_id (foreign key in items table) then return records from feeds table. the third function is to return all images related to items_id.
those functions are :
* To get all items in database:
function get_items(){
return $query = Database::getInstance('db')
->table('items')
->columns(
'id',
'items.rowid',
'items.feed_id as feed_id',
'title' )
->findAll();
}
* To get feed data from feeds table :
function get_feeds($id){
return $query = Database::getInstance('db')
->table('feeds')
->eq('id',$id)
->findAll();
}
* To get image data from images table :
function get_images($id){
return $query = Database::getInstance('db')
->table('images')
->columns('items_id','src as image_url',
'title as image_title',
'alt')
->eq('items_id',$id)
->findAll();
}
Then i have the following code to call those function and display the result in jsonformat:
$response['items'] = array();
$response['feeds'] = array();
$response['images'] = array();
foreach ($items = get_items() as $item) {
$response['items'][] = array(
'id' => (int)$item['rowid'],
'feed_id' => (int)$item['feed_id'],
'title' => $item['title'],
);
foreach ($feeds = get_feeds((int)$item['feed_id']) as $feed) {
$response['feeds'][] = array(
'title' => $feed['title'],
'logo_url' => $feed['logo_url'],
'site_url' => $feed['site_url'],
);
}
foreach ($images = get_images($item['id']) as $image) {
$response['images'][] = array(
'id' => $image['items_id'],
'url' => $image['image_url'],
'thumb' => $_SERVER['SERVER_NAME'] . /myServer/images/thumbs/'. 'thumb_'.basename($image['image_url']),
'title' => $image['image_title'],
'alt' => $image['alt']
);
}
}
echo json_encode($response, JSON_PRETTY_PRINT);
so, my expectation is to get json output like:
"items": [
{
"id": ,
"feed_id":
"title":
"feeds": [
{
"title": ,
"logo_url": ,
"site_url": "
}
]
"images": [
{
"id": ,
"url": ",
"thumb":
"title": "",
"alt": ""
},
{
....
}
]
}]
i mean each item array should include nested arrays of its related data coming from get_feeds and get_images functions.
instead of that, i get response like :
//here i select two items from my db
"items": [
{ //first_item
"id": ,
"feed_id":
"title":
},
{ //second_item
"id": ,
"feed_id":
"title":
}
],
"feeds": [
{ // feed data for first item
"title": ,
"logo_url": ,
"site_url": "
},
{ // feed data for second item
"title": ,
"logo_url": ,
"site_url": "
}
],
"images": [
{ // image data for first item
"id": ,
"url": ",
"thumb":
"title": "",
"alt": ""
},
{ // other images data
....
}
]
}]
as you see i am getting output without keeping relation between items, feeds and images, all of them are shown independently.
my queries are fine but i am suspecting error in my foreach statements.
i could fix this issue by joining those tree tables in one query, but i don't want to do that because i need to do validation and other operations to output comes from each table.
i appreciate your help
i found the solution. it is very easy :)
it is just like:
$response['items'][] = array(
'id' => (int)$item['rowid'],
'feed_id' => (int)$item['feed_id'],
'title' => $item['title'],
'feeds' => array(
)
'images' => array(
)
);

Create nested json object using php mysql

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

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