construct a complex JSON object from SQL result in PHP - php

I have a db with many tables. One table represents products, one other table represents categories. One product can belong to many categories. So when I request my db to display the products with their categories I of course retrieve many rows as many categories each product has.
For instance the result would be
product_name|category |city
Test1 |cinema |paris
Test1 |entertainment |paris
Test1 |Other |paris
Test2 |Food |new york
Test2 |Restaurant |new york
Test2 |Night |new york
What I am trying to do is to create a JSON object using a PHP script for each product name which looks like this :
[
{
"product_name": "Test1",
"categorie": [
"cinema",
"entertainment",
"Other"
],
"city": "paris"
},
{
"product_name": "Test2",
"categorie": [
"Food",
"Restaurant",
"Night"
],
"city": "new york"
}
]
When I tried to use json_encode but unsuccessfully I got duplicate rows.
thanks for your help

You will need to reorder your data before the json_encode
If $rows is the response of your db, you have to go throw
$data = [];
foreach($rows as $row) {
$name = $row['product_name'];
if(!isset($data[$name])) {
$data[$name] = [
'product_name' => $name,
'city'=>$row['city'],
'categories'=>[]
];
}
$data[$name]['categories'][] = $row['category'];
}
json_encode(array_values($data)); //this is what you want
Thats it!
Maybe you need while ($row=mysqli_fetch_row($result)) instead of foreach, depending on your sql query builder you use. But the idea is the same

Related

in laravel, is there any good way to plunk object properties in array of objects & remove unnecessary properties in request

{
"city_name": "new cityName",
"country_id": 3,
"localities": [
{
"name": "locality 11",
"postal-code": 123456,
"locality-specialized-in": "FOOD,TECH",
"extra_data" : "unwanted, unnecessary data"
},
{
"name": "locality 21",
"postal-code": "1234567",
"extra_data" : "unwanted, unnecessary data",
"extra_data_two" : "unwanted, unnecessary data"
}
]
}
in above request just want to plunklocalities with only fields(name,postal-code) and want to remove extra fields (like as per above request locality-specialized-in).
just thing is without using loop or any good manner
expected filter data from above request
$localities_array = [
[
"name"=> "locality 11",
"postal-code"=> 123456
],
[
"name"=> "locality 21",
"postal-code"=> 1234567
]
]
currently i am using below method in controller to archive this.
$localities = null;
foreach (request()->get('localities') as $locality) {
$localities[] = ["name"=>$locality['name'],"postal-code"=>$locality['postal-code'] ];
}
Suggestions are appreciated.
If you want to get value from array in request use the below code.
$localities = null;
$localities['name'] = request()->get('localities.name');
$localities['postal-code'] = request()->get('localities.postal-code');

Give name to array in Json/PHP

In AJAX I retrieve in database the last date of a document by category (for example the category invoice, orders, etc.) thanks to this request:
  // In each category there can be several documents
$stmt = $bdd->prepare('SELECT file_name, max(file_creation) as file_creation, file_category
FROM all_files
WHERE ...
GROUP BY file_category');
$stmt ->execute(array(
...
));
$arr = $stmt->fetchAll();
echo json_encode($arr);
So I get it back in JSON:
[
   {
      "File_name": "order_18"
      "0": "order_18"
      "File_creation": "2018-11-28"
      "1": "2018-11-28"
      "File_category": invoice "
      "3": invoice "
   }
   {
      "File_name": "order_18"
      "0": "order_18"
      "File_creation": "2018-11-25"
      "1": "2018-11-25"
      "File_category": "order"
      "3": "order"
   }
]
I then want to place each data in the right places with jquery, like this:
$ ('#label-order').text('') // the text will have to be: 2018-11-25
$ ('#label-invoice').text('') // the text will have to be: 2018-11-28
The problem is that I do not know how to recover the data that interests me to put it in the right places because the number of categories will increase over time
So I thought of doing something like that, to get the data back to data ["invoice"] ["file_creation"] and data ["order"] ["file_creation"]:
[
   "invoice": {
      "File_name": "order_18"
      "0": "order_18"
      "File_creation": "2018-11-28"
      "1": "2018-11-28"
      "File_category": invoice "
      "3": invoice "
   }
   "order": {
      "File_name": "order_18"
      "0": "order_18"
      "File_creation": "2018-11-25"
      "1": "2018-11-25"
      "File_category": "order"
      "3": "order"
   }
]
Is that possible? If so, how can I do?
Is there a better solution?
Here is a code to have in a result list of invoices and orders separately.
After receiving data on front-end you can use simple code to group all items:
var data = [
{
"File_name": "order_18",
"0": "order_18",
"File_creation": "2018-11-28",
"1": "2018-11-28",
"File_category": "invoice",
"3": "invoice "
},
{
"File_name": "order_18",
"0": "order_18",
"File_creation": "2018-11-25",
"1": "2018-11-25",
"File_category": "order",
"3": "order"
}
]
var categories = {
invoices: [],
orders: []
}
data.map((item) => {
if(item['File_category'] === 'order') {
categories.orders.push(item)
} else if(item['File_category'] === 'invoice') {
categories.invoices.push(item)
}
})
console.log(categories)
Than you can just loop over specific categories like categories.invoices or categories.orders and easily append it to body.
Doing this using the PHP code:
<?php
//$arrays = $stmt->fetchAll();
$arrays=
[
[
"File_name"=>"order_18",
"File_creation"=>"2018-11-28",
"File_category"=>"invoice",
],
[
"File_name"=>"order_18",
"File_creation"=>"2018-11-25",
"File_category"=>"order",
]
];
foreach($arrays as $index=>$array)
{
if(isset($array["File_category"]))
{
$key=$array["File_category"];
unset($array["File_category"]);
$arrays[$key][] = $array;
unset($arrays[$index]);
}
}
print_r($arrays);
//echo json_encode($arrays);
?>
The result will be:
Array
(
[invoice] => Array
(
[0] => Array
(
[File_name] => order_18
[File_creation] => 2018-11-28
)
)
[order] => Array
(
[0] => Array
(
[File_name] => order_18
[File_creation] => 2018-11-25
)
)
)

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

MySQL INSERT...SELECT for one column only

I want to insert multiple row in my table, the data for this query is fetched from front end. One column in this query is not passed from front end, its value is determined at query execution time from another table. I can put these array in loop & perform one SELECT query & one INSERT query for each item, but I want to avoid it, thus I created one large INSERT query! Till here everything fine, but now that one value should be from another table!
I can use INSERT...SELECT method
https://dev.mysql.com/doc/refman/5.6/en/insert-select.html
It allows insertion of multiple rows, all rows matched are inserted & I can pass a dummy column value in SELECT part to customise the query. But now problem is these 'faked columns' are same for all rows, they should me in loop, different for each loop! how to achieve that?
Scenario
$products = array(
"1" => array(
"product_name" => "apple",
"units" => "1",
),
"2" => array(
"product_name" => "mango",
"units" => "3",
),
);
Suppose this is the array I get from front end, each key is product id which contains other description for product in cart. Now I'll insert this in Orders table but price is missing, which I have to fetch from Products table! For which I can perform select using product id.
Similar Question:
This answer uses faked columns:
MYSQL INSERT SELECT problem
Copied from accepted answer:
INSERT INTO website (url,fid) SELECT $site,id FROM users WHERE name = $user
Here $site will be same for all inserted records, I want it different for each record as per my array!
I research on this topic but can't find desired answer :(
Try this code if it helps,
<?php
$products = array(
"1" => array(
"product_name" => "apple",
"units" => "1",
),
"3" => array(
"product_name" => "mango",
"units" => "3",
),
"7" => array(
"product_name" => "mango",
"units" => "3",
),
);
$result = $conn->query("SELECT product_id, product_price FROM product_table WHERE product_id IN (".implode(',', array_keys($products)).")");
while($row = $result->fetch_assoc()) {
$prod_price[$row['product_id']] = $row['product_price'];
}
$qry = array();
foreach ($products as $key => $value) {
$qry[] = "('".$value['product_name']."', '".$value['units']."', '".$prod_price[ $key]."')";
}
$insert = "INSERT INTO orders (`product_name`, `units`, `product_price`) VALUES ".implode(', ', $qry);
I come up with this code based on my understanding of your question. Let me know if it works.
This approach just hits the DB twice only.

Build JSON object using database relations/foreign key PHP

I need some help.
What I need to do basically, it's a simple mapping relation (FK) between to databases and bring this as JSON (using php).
For example, I have those two abstract databases:
1: Costumer
+-------+-----------+----------+
| id | name | car_id |
+-------+-----------+----------+
2: Car
+-------+-----------+----------+
| id | model | price |
+-------+-----------+----------+
And I need a JSON like:
{
"customers": [{
"id": "1",
"name": "First Customer",
"car": {
"id": "1",
"model": "sampleCar",
"price:": "19.33"
}
}, {
"id": "2",
"name": "second Customer",
"car": {
"id": "2",
"model": "betterCar",
"price:": "99.33"
}
}]
}
I'm using MySQL to save this data, but I can change the database - The only thing I can't change is the PHP.
My question is:
There is a performative way to do this without using frameworks?
(Or maybe a framework who do just it?)
Thanks!
It's a simple JOIN query, and then the fetching loop creates the appropriate data.
$sql = "SELECT c.id AS cus_id, c.name, car.id AS car_id, car.model, car.price
FROM customer AS c
LEFT JOIN car ON car.id = c.car_id";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$customers = array();
while ($row = $stmt->fetch) {
if ($row['car_id'] != null) {
$car = array('id' => $row['car_id'],
'model' => $row['model'],
'price' => $row['price']);
} else {
$car = null;
}
$customers[] = array('id' => $row['cus_id'],
'name' => $row['name'],
'car' => $car);
}
$result = array('customers' => $customers);

Categories