I'm using PHP with PDO for a project
I have the following two tables in my database.
Category
CategoryID | Name | CategoryCode
1 | Fixed | FA
2 | Consumable | CA
3 | Intangible | IA
Type
TypeID | CategoryID | Name | TypeCode
1 | 1 | Furniture | FU
2 | 1 | Computers & Computer Peripherals | CP
3 | 1 | Electrical Appliances | EA
4 | 1 | Machinery | MA
5 | 2 | Computer Peripherals | PE
6 | 3 | Software | SW
I need to get a output like below from the select operation. I have tried it with group concat and then fecthAll(), but it doesn't give me the result in correct JSON
Output
[
{
"CategoryID": 1,
"Name": "Fixed",
"CategoryCode": "FA",
"Types": [
{
"TypeID": 1,
"Name": "Furniture",
"TypeCode": "FU"
},
{
"TypeID": 2,
"Name": "Computers & Computer Peripherals",
"TypeCode": "CP"
},
{
"TypeID": 3,
"Name": "Electrical Appliances",
"TypeCode": "EA"
}
]
},
{
"CategoryID": 2,
"Name": "Consumable",
"CategoryCode": "CA",
"Types": [
{
"TypeID": 5,
"Name": "Computer Peripherals",
"TypeCode": "PE"
}
]
}
]
Figured it out this way to get my intended output
protected function getCategories(){
$dbConnection = $this->connect();
$catSql = 'SELECT * FROM `category`';
$stmt = $dbConnection->prepare($catSql);
$stmt->execute();
$result = $stmt->fetchAll();
$output = array();
foreach ($result as $row) {
$line = array("CategoryID" => $row['CategoryID'], "CategoryName" => $row['Name'], "CategoryCode" => $row['CategoryCode']);
$typeSQL = 'SELECT
TypeID,
Name,
TypeCode
FROM type
WHERE
CategoryID = ' . $row['CategoryID'];
$stmt2 = $dbConnection->prepare($typeSQL);
$stmt2->execute();
$types = $stmt2->fetchAll();
$typeArray = array();
foreach ($types as $type) {
$typeLine = array("TypeID" => $type['TypeID'], "TypeName" => $type['Name'], "TypeCode" => $type['TypeCode']);
array_push($typeArray, $typeLine);
}
$line['Types'] = $typeArray;
array_push($output, $line);
}
return json_encode($output);
}
Related
I hope you guys can help me here, because I guess my code is not made correctly.
I have 2 mysql tables:
table: checks
+-----------+-------------+------------+
| id | name | host |
+-----------+-------------+------------+
| 1 | demo 1 | 1.1.1.1 |
+-----------+-------------+------------+
| 2 | demo 2 | 1.1.1.2 |
+-----------+-------------+------------+
| 3 | demo 3 | 1.1.1.3 |
+-----------+-------------+------------+
table: checks_history
+-----------+-------------+------------+------------+
| id | check_id | status | timestamp |
+-----------+-------------+------------+------------+
| 1 | 1 | 0 | 3451245 |
+-----------+-------------+------------+------------+
| 2 | 1 | 0 | 3451245 |
+-----------+-------------+------------+------------+
| 3 | 2 | 0 | 3451245 |
+-----------+-------------+------------+------------+
| 4 | 1 | 1 | 3451245 |
+-----------+-------------+------------+------------+
| 5 | 2 | 0 | 3451245 |
+-----------+-------------+------------+------------+
I want create a json file per id (table: checks) with this structure:
{
"info": { // Associated to table "checks"
"id": "1",
"name": "Demo 1",
"host": "1.1.1.1"
},
"data": { // associated to table check_history according with the id on table check
"1": { // associated to Column "id" on table checks_history
"status": "0",
"timestamp": "3451245"
},
"2": {
"status": "0",
"timestamp": "3451245"
},
"4": {
"status": "1",
"timestamp": "3451245"
}
}
}
There is my code PHP:
$info = array();
$history = array();
$incidents = $database->select("app_checks","*", false);
foreach ($incidents as $key => $value) {
$id = $value['id'];
$name = $value['name'];
$host = $value['host'];
$check_history = $database->select("app_checks_history", "*", [ "checkid" => $id, "ORDER" => ['id' => 'DESC'], "LIMIT" => 30 ]);
foreach ($check_history as $k => $v) {
$history = array(
$v['id'] => array(
'timestamp' => $v['timestamp'],
'status' => $v['status']
)
);
}
$info = array(
'info'=> array(
'id'=> $id,
'name'=> $name,
'host'=> $host
),
'data' => $history
);
$json_data = json_encode($info, JSON_PRETTY_PRINT);
$fileName = 'json/server_'.$id.'.json';
file_put_contents($fileName, $json_data);
}
When I try run the code, im getting the first value on "data" instead all loop:
{
"info": {
"id": "1",
"name": "Demo 1",
"host": "1.1.1.1"
},
"data": {
"1": {
"status": "0",
"timestamp": "3451245"
}
}
}
I searched in the forum and I did not found any similar issue related to my code.
I appreciate any help here.
Thanks in advance.
br
Well, I don't know PHP; however, I believe if you get your data model right, it should get you there. I would have my data model something like below in C#, an instance of Check class represent a check as per your table, serialize and save each instance in its own JSON file.
namespace Stackoverflow
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Check
{
[JsonProperty("info")]
public Info Info { get; set; }
[JsonProperty("data")]
public Dictionary<string, History> Data { get; set; }
}
public partial class History
{
[JsonProperty("status")]
[JsonConverter(typeof(ParseStringConverter))]
public long Status { get; set; }
[JsonProperty("timestamp")]
[JsonConverter(typeof(ParseStringConverter))]
public long Timestamp { get; set; }
}
public partial class Info
{
[JsonProperty("id")]
[JsonConverter(typeof(ParseStringConverter))]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("host")]
public string Host { get; set; }
}
}
In my WordPress database I have three tables wp_companies which stores some company info and wp_product_types which stores a bunch of product types that a company can assign them selves then I have wp_company_products which is used to assign product types to a company using ids.
Heres an example of how the database looks:
wp_companies
id | company_name | member_type | logo
-----------------------------------------------------------------
1 | Google | full | https://via.placeholder.com/150
-----------------------------------------------------------------
2 | Crunchyroll | full | https://via.placeholder.com/150
wp_products
id | product_name |
----------------------
1 | Car Insurance |
----------------------
2 | House Insurance |
----------------------
3 | Life Insurance |
wp_company_products
id | company_id | product_id
----------------------------
1 | 1 | 2
----------------------------
2 | 2 | 1
----------------------------
3 | 1 | 3
Here's my current MySQL query that simply shows the data in wp_companies
add_action('rest_api_init', function() {
register_rest_route('custom-routes/v1', 'members', array(
'methods' => 'GET',
'callback' => 'get_members'
) );
});
function get_members($data) {
global $wpdb;
$query = $wpdb->get_results( "SELECT company_name, member_type, logo, site_url FROM {$wpdb->prefix}companies ORDER BY RAND()" );
foreach ( $query as $member ) {
$member_data[] = array(
"company_name" => $member->company_name,
"member_type" => $member->member_type,
"logo" => $member->logo,
);
}
return $member_data;
}
This displays my data like so on my api end point:
[
{
"company_name":"Google",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
},
{
"company_name":"Crunchyroll",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
}
]
But what I want is to combine the wp_companies and wp_company_products tables so that my data is displayed something like this on the api end point:
[
{
"company_name":"Google",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
"products": [
"House Insurance",
"Life Insurance"
]
},
{
"company_name":"Crunchyroll",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
"products": [
"Car Insurance",
]
}
]
How can I structure my MySQL query to be able to achieve this?
Please try this
function get_members($data) {
global $wpdb;
$query = $wpdb->get_results( "SELECT company_name, member_type, logo, site_url FROM {$wpdb->prefix}companies ORDER BY RAND()" );
foreach ( $query as $member ) {
$productQuery = $wpdb->get_results( "SELECT product_name FROM wp_products WHERE id IN (SELECT product_id from wp_company_products WHERE compony_id = '{$member->id}') " );
$products = array();
foreach ( $productQuery as $prduct ) {
array_push($products ,$prduct->product_name);
}
$member_data[] = array(
"company_name" => $member->company_name,
"member_type" => $member->member_type,
"logo" => $member->logo,
"products" => $products
);
}
return $member_data;
}
I would like to select all the posts and their attachments from my database.
This is the structure with dummy data:
Posts table
id | post | userId |
---------|---------------|----------|
1 | "hello" | 1 |
2 | "world" | 1 |
3 | "ouch" | 2 |
4 | "test" | 1 |
Attachments table
id | postId | fileName | time |
---------|---------------|----------|----------|
1 | 1 |"hey.jpg" | 0 |
2 | 1 |"test.png"| 53252354 |
3 | 2 |"asd.png" | 0 |
4 | 4 |"asd2.png"| 0 |
My code looks like this so far, but I doesn't really get what I'd like to.
$qry = $db->prepare('
SELECT p.id
, p.post
, p.userId
, att.fileName
, att.time
FROM posts p
LEFT
JOIN attachments att
ON att.postId = p.id
');
$qry->execute();
$postsArray = $qry->fetchAll(PDO::FETCH_ASSOC);
I would like to have something like this:
[{'id': 1,
'post': 'hello',
'userId': 1,
'attachments': [{'fileName': 'hey.jpg', 'time:' 0}, ... ]
}, ... ]
How could I achieve this?
Your query will give you the right data for the results you want, you can post-process in PHP to get the format you want:
foreach ($postArray as $post) {
$fixed_part = array('id' => $post['id'], 'post' => $post['post'], 'userId' => $post['userId']);
$key = serialize($fixed_part);
if (!isset($out[$key])) $out[$key] = $fixed_part;
$out[$key]['attachments'][] = array('fileName' => $post['fileName'], 'time' => $post['time']);
}
$out = array_values($out);
echo json_encode($out, JSON_PRETTY_PRINT);
Output is too long to post but can be seen in this demo. Query results can be seen on dbfiddle.
Usually I would do something like this:
$result = $db->query("select id, post, userId from posts");
$posts = [];
while ($post = $result->fetch(PDO::FETCH_OBJECT)) {
$post->attachments = [];
$posts[$post->id] = $post;
}
$result = $db->query("select postId, fileName, time from attachments");
while ($att = $result->fetch(PDO::FETCH_OBJECT)) {
$posts[$att->postId]->attachments[] = $att;
unset($att->postId); // optional
}
$posts = array_values($posts); // optional
echo json_encode($posts);
Note that the $posts array is id-indexed. I would keep it that way. But if you need exactly the same result as in your question (zero-indexed), you can add this line:
$posts = array_values($posts);
In newer MySQL versions you can also get the JSON result with a single SQL query:
select json_arrayagg(post_json) as json
from (
select json_object(
'id', p.id,
'post', p.post,
'userId', p.userId,
'attachments', json_arrayagg(
json_object('fileName', a.fileName, 'time', time)
)
) as post_json
from posts p
left join attachments a on a.postId = p.id
group by p.id
) x
Result:
[{"id": 1, "post": "hello", "userId": 1, "attachments": [{"time": 0, "fileName": "hey.jpg"}, {"time": 53252354, "fileName": "test.png"}]}, {"id": 2, "post": "world", "userId": 1, "attachments": [{"time": 0, "fileName": "asd.png"}]}, {"id": 3, "post": "ouch", "userId": 2, "attachments": [{"time": null, "fileName": null}]}, {"id": 4, "post": "test", "userId": 1, "attachments": [{"time": 0, "fileName": "asd2.png"}]}]
db-fiddle demo
I am trying to create the following JSON (much simplified...) from database results using PHP:
{
"name": "Bob",
"children": [{
"name": "Ted",
"children": [{
"name": "Fred"
}]
},
{
"name": "Carol",
"children": [{
"name": "Harry"
}]
},
{
"name": "Alice",
"children": [{
"name": "Mary"
}]
}
]
}
The database tables:
Table 'level_1':
level_1_pk| level_1_name
-------------------------
1 | Bob
Table 'level_2':
level_2_pk| level_2_name | level_1_fk
-------------------------
1 | Ted | 1
2 | Carol | 1
3 | Alice | 1
Table 'level_3':
level_3_pk| level_3_name | level_2_fk
-------------------------
1 | Fred | 1
2 | Harry | 2
3 | Mary | 3
The code:
$query = "SELECT *
FROM level_1
LEFT JOIN level_2
ON level_1.level_1_pk = level_2.level_1_fk";
$result = $connection->query($query);
while ($row = mysqli_fetch_assoc($result)){
$data[$row['level_1_name']] [] = array(
"name" => $row['level_2_name']
);
}
echo json_encode($data);
Produces:
{"Bob":[{"name":"Ted"},{"name":"Carol"},{"name":"Alice"}]}
Question:
How can I get the next level, level_3, and include the text "children" and level_3 children in the JSON as required in the JSON defined above?
I imagine I will need the PHP to be recursive given more children in the JSON.
SQL
This doesn't look like a decent design for hierarchical data. Consider another approach like adjacency list.
Solution #1 - MySQL 8 JSON support:
With MySQL 8 you can use JSON_ARRAYAGG() and JSON_OBJECT() to get the JSON result with SQL only:
select json_object(
'name', l1.level_1_name,
'children', json_arrayagg(json_object('name', l2.level_2_name, 'children', l2.children))
) as json
from level_1 l1
left join (
select l2.level_2_name
, l2.level_1_fk
, json_arrayagg(json_object('name', l3.level_3_name)) as children
from level_2 l2
left join level_3 l3 on l3.level_2_fk = l2.level_2_pk
group by l2.level_2_pk
) l2 on l2.level_1_fk = l1.level_1_pk
group by level_1_pk
The result is:
{"name": "Bob", "children": [{"name": "Ted", "children": [{"name": "Fred"}]}, {"name": "Carol", "children": [{"name": "Harry"}]}, {"name": "Alice", "children": [{"name": "Mary"}]}]}
db-fiddle demo
Formatted:
{
"name": "Bob",
"children": [
{
"name": "Ted",
"children": [
{
"name": "Fred"
}
]
},
{
"name": "Carol",
"children": [
{
"name": "Harry"
}
]
},
{
"name": "Alice",
"children": [
{
"name": "Mary"
}
]
}
]
}
Solution #2 - Constructing JSON with GROUP_CONCAT():
If the names don't contain any quote carachters, you can manually construct the JSON string in older versions using GROUP_CONCAT():
$query = <<<MySQL
select concat('{',
'"name": ', '"', l1.level_1_name, '", ',
'"children": ', '[', group_concat(
'{',
'"name": ', '"', l2.level_2_name, '", ',
'"children": ', '[', l2.children, ']',
'}'
separator ', '), ']'
'}') as json
from level_1 l1
left join (
select l2.level_2_name
, l2.level_1_fk
, group_concat('{', '"name": ', '"', l3.level_3_name, '"', '}') as children
from level_2 l2
left join level_3 l3 on l3.level_2_fk = l2.level_2_pk
group by l2.level_2_pk
) l2 on l2.level_1_fk = l1.level_1_pk
group by level_1_pk
MySQL;
The result would be the same (see demo)
Solution #3 - Constructing nestet structure with PHP objects:
You can also write a simpler SQL query and construct the nested structure in PHP:
$result = $connection->query("
select level_1_name as name, null as parent
from level_1
union all
select l2.level_2_name as name, l1.level_1_name as parent
from level_2 l2
join level_1 l1 on l1.level_1_pk = l2.level_1_fk
union all
select l3.level_3_name as name, l2.level_2_name as parent
from level_3 l3
join level_2 l2 on l2.level_2_pk = l3.level_2_fk
");
The result is
name | parent
----------------
Bob | null
Ted | Bob
Carol | Bob
Alice | Bob
Fred | Ted
Harry | Carol
Mary | Alice
demo
Note: The name should be unique along all tables. But I don't know what result you would expect, if duplicates were possible.
Now save the rows as objects in an array indexed by the name:
$data = []
while ($row = $result->fetch_object()) {
$data[$row->name] = $row;
}
$data will now contain
[
'Bob' => (object)['name' => 'Bob', 'parent' => NULL],
'Ted' => (object)['name' => 'Ted', 'parent' => 'Bob'],
'Carol' => (object)['name' => 'Carol', 'parent' => 'Bob'],
'Alice' => (object)['name' => 'Alice', 'parent' => 'Bob'],
'Fred' => (object)['name' => 'Fred', 'parent' => 'Ted'],
'Harry' => (object)['name' => 'Harry', 'parent' => 'Carol'],
'Mary' => (object)['name' => 'Mary', 'parent' => 'Alice'],
]
We can now link the nodes in a single loop:
$roots = [];
foreach ($data as $row) {
if ($row->parent === null) {
$roots[] = $row;
} else {
$data[$row->parent]->children[] = $row;
}
unset($row->parent);
}
echo json_encode($roots[0], JSON_PRETTY_PRINT);
The result:
{
"name": "Bob",
"children": [
{
"name": "Ted",
"children": [
{
"name": "Fred"
}
]
},
{
"name": "Carol",
"children": [
{
"name": "Harry"
}
]
},
{
"name": "Alice",
"children": [
{
"name": "Mary"
}
]
}
]
}
demo
If multiple root nodes are possible (multiple rows in level_1_name), then use
json_encode($roots);
I will recommend the following recursive:
function getPeople($levelNum = 1, $parent = 0) {
if ($levelNum > 3) return array(); // break recursion condition
global $connection;
$level = 'level_' . $levelNum; // also can check here if the table exist by this name
$query = "SELECT * FROM ". $level;
if ($parent) // if there is parent add him to query
$query .= "WHERE " . $level . "_fk = " . $parent;
$result = $connection->query($query);
while ($row = mysqli_fetch_assoc($result)) { // for each row:
$idCol = $level . "_pk"; // get the primary ID key
$id = $row[$idCol]; // get the ID
$localResult[$id] = array("Name" => $row[$level . "_name"]); // set local array with key as ID and name
}
foreach ($localResult as $id => $elem) { // elem is array with only name
$children = getPeople($levelNum + 1, $id); // recursively get all children
if ($children)
$elem["children"] = $children;
$data[] = $elem; // append the new elem to origin array
}
return $data;
}
Initial call should be like getPeople() or json_encode(getPeople());
Notice - I used max depth as recursive break assuming you know max depth - you can also (and I recommend you do) skip the break condition and just check if the table name exist ! (as $level string)
I wrote it as pseudo code as I didn't actually build the table - it may have syntax error but the logic should be solid...
I'm trying to create a datatable that groups items based on a column name. In order to populate the table correctly I need to format the JSON with parent and child relationships but am having troubles. I'll be grabbing the data from MSSQL via PHP.
+---------------+-------+--------------+------------+------------+--------+
| ACTIVITY_NAME | GROUP | START_DATE | END_DATE | COMPLETED | TOTAL |
+---------------+-------+--------------+------------+------------+--------+
| Test | 1 | 04/30/2015 | 05/01/2015| 10 | 15 |
| Test | 2 | 04/30/2015 | 05/01/2015| 20 | 25 |
| Test2 | 1 | 05/2/2015 | 05/03/2015| 30 | 35 |
| Test2 | 2 | 05/2/2015 | 05/03/2015| 40 | 45 |
| Test2 | 3 | 05/2/2015 | 05/03/2015| 50 | 55 |
+---------------+-------+--------------+------------+------------+--------+
I need the JSON formatted like this with the parent objects being under the "data" and the children being under the "children". I'm having a hard time figuring out if I need to do two seperate queries, one for the rollup data in the parent and the second for the child rows. Any help would be appriecated.
Edit to add PHP:
<?php
include("connect.php");
if( $conn === false ) {
echo "Could not connect.\n";
die( print_r( sqlsrv_errors(), true));
}
/* Set up and execute the query. */
$sql = "<QUERY>";
$stmt = sqlsrv_query( $conn, $sql);
do {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$json[] = $row;
}
} while ( sqlsrv_next_result($stmt) );
foreach ($json as $result) {
$data[$result['ACTIVITY_NAME']]['children'] = $result;
}
echo json_encode($data);
?>
I need this format:
JSON blob: https://jsonblob.com/554b958be4b05c281ae9707e
{
"data": [
{
"ACTIVITY_NAME": "Test",
"children": [
{
"ACTIVITY_NAME": "Test",
"MAINTENANCE_GROUP": "1",
"START_DATE": "04/30/2015",
"END_DATE": "05/01/2015",
"COMPLETED": "10",
"TOTAL": "15"
},
{
"ACTIVITY_NAME": "Test",
"MAINTENANCE_GROUP": "2",
"START_DATE": "04/30/2015",
"END_DATE": "05/01/2015",
"COMPLETED": "20",
"TOTAL": "25"
}
]
},
{
"ACTIVITY_NAME": "Test2",
"children": [
{
"ACTIVITY_NAME": "Test2",
"MAINTENANCE_GROUP": "1",
"START_DATE": "05/2/2015",
"END_DATE": "05/03/2015",
"COMPLETED": "30",
"TOTAL": "35"
},
{
"ACTIVITY_NAME": "Test2",
"MAINTENANCE_GROUP": "1",
"START_DATE": "05/2/2015",
"END_DATE": "05/03/2015",
"COMPLETED": "40",
"TOTAL": "45"
},
{
"ACTIVITY_NAME": "Test2",
"MAINTENANCE_GROUP": "1",
"START_DATE": "05/2/2015",
"END_DATE": "05/03/2015",
"COMPLETED": "50",
"TOTAL": "55"
}
]
}
]
}
I will recommend you to use two queries, then put the data in an array that matches your format and json_encode it to produce the required result.
You may want to take a look at SQL Server recursive query
It seems that you are grouping by ACTIVITY_NAME, if that's the case and you don't want to make additional queries, you can first make an associative array:
foreach ($results as $result) {
$data[$result['ACTIVITY_NAME']]['children'] = $result;
}
Then you could use that array to iterate over the children to calculate data like MAINTENANCE_GROUP, COMPLETED and TOTAL;
After you've done all this, you can then use array_values to get a non-associative array.
Here is what the working script looks like:
<?php
include("connect.php");
if( $conn === false ) {
echo "Could not connect.\n";
die( print_r( sqlsrv_errors(), true));
}
/* Set up and execute the query. */
$sql = "<query> ";
$stmt = sqlsrv_query($conn, $sql);
// This is where the data will be organized.
// It's better to always initialize the array variables before putting data in them
$data = array();
// Get the rows one by one
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
// Extract the activity name; we want to group the rows by it
$name = $row['ACTIVITY_NAME'];
$group = '';
$sdate = '';
$edate = '';
$completed = '';
$total = '';
$perc = '';
// Check if this activity was encountered before
if (! isset($data[$name])) {
// No, this is the first time; we will make room for it, first
$data[$name] = array(
// Remember the name
'ACTIVITY_NAME' => $name,
'MAINTENANCE_GROUP' => $group,
'START_DATE' => $sdate,
'END_DATE' => $edate,
'COMPLETED' => $completed,
'TOTAL_CLUSTERS' => $total,
'COMPLETE_PERC' => $perc,
// No children yet
'children' => array(),
);
}
// Put the row into the list of children for this activity
$data[$name]['children'][] = $row;
}
// Here, the entries in $data are indexed by the values they also have in 'ACTIVITY_NAME'
// If you want them numerically indexed, all you have to do is:
$data = array_values($data);
echo json_encode(array('data' => $data));
//echo json_encode($data);
?>