Complicated nested array issue - php

I want to query data from a database into a json array that ultimalty produces a force directed graph in javascript. Here is what the Json array should be like. However nodes can have mulitiple adjacencies or none, how can I query a json array where the adjacencies section varies from node to node and is able to adjust according to the number of adjacencies a node has?
Var JSON =[
{
"adjacencies": [
{
"nodeTo": "graphnode9",
"nodeFrom": "graphnode5",
"data": {}
}
],
"data": {
"$color": "#416D9C",
"$type": "star"
},
"id": "graphnode5",
"name": "graphnode5"
},
];
or they can have
Var JSON =[
{
"adjacencies": [
{
"nodeTo": "graphnode9",
"nodeFrom": "graphnode5",
"data": {}
},
{
"nodeTo": "graphnode9",
"nodeFrom": "graphnode5",
"data": {}
},
{
"nodeTo": "graphnode9",
"nodeFrom": "graphnode5",
"data": {}
}
],
"data": {
"$color": "#416D9C",
"$type": "star"
},
"id": "graphnode5",
"name": "graphnode5"
},
];
or they can not have any
Var JSON =[
{
"adjacencies": [],
"data": {
"$color": "#416D9C",
"$type": "star"
},
"id": "graphnode5",
"name": "graphnode5"
},
];
Here is my attempt so far, however this only produces a json that only allows one adjacencies, How can I setup a Json query that will adjust the the number of adjacencies a node have? while just loading the data and id section once but allowing the adjacenies to be varied?
Here is my Database structure
nodes Relationships
----- -------------
id int(11), id int(11),
name varchar(35), goingto int(11), //this is the destination node from the id relation
color varchar(7), data varchar(0) null
type varchar (12), Foreign key (id) references nodes(id)
Primary key (id)
engine = innodb
And here is my attempt that
function getjson(){
$db = adodbConnect();
$query = "SELECT nodes.*, relationships.* FROM nodes inner JOIN relationships ON nodes.id = relationships.id";
$result = $db -> Execute($query);
while($row=$result->FetchRow())
{
$id= (float)$row['id'];
$name = $row['name'];
$color1 = $row['color'];
$type1 = $row['type'];
$to= (float)$row['goingto'];
$thumb =$row['thumb']; //image path
$array[] = array(
"adjacencies" => array( array(
"nodeTo" => "$to",
"nodeFrom" => "$id",
"data" => array() )),
"data" => array(
"$"."color" => $color1,
"$"."type" => $type1 ),
"id" => $id,
"name" => "<img src='".$thumb."' height='25' width='25' alt='root'/><label>".$name."</label>");
}
$json = json_encode($array);
print "$json";
//return $json;
}

If you want to return the result in a single query, then you will end up with duplicated data for the node, in each separate row where there's a distinct adjacency from that node... Which is fine, that's how it works.
But as it sits, you won't get nodes returned if there's no adjacency on that node (because you're using an INNER join. You should use a LEFT join to include nodes that have no results from the related adjacency table).
By sorting by node id, we explicitly ensure that all nodes and their adjacencies appear grouped together. This is probably happening already because id is your pk and hence the sort is happening this way "automatically". But an ORDER BY nodes.id ensures this happens, and makes your intention clear to anyone looking at the code.
Also, because you're returning everything * from both tables, you're going to have column name conflicts, on node.id and relationship.id. Ideally you'd explicitly name your columns to avoid this so that you have predictable results back in PHP.
So your SQL could look more like:
SELECT
n.id as n_id,
n.name,
n.color,
n.type,
r.id as r_id,
r.goingto,
r.data
FROM
nodes n
LEFT JOIN relationships r
ON n.id = r.id
ORDER BY
n.id
This returns a result set that looks something like:
n_id | name | color | type | r_id | goingto | data
------+-------+--------+-------+------+---------+-----------
1 | node1 | red | type1 | 1 | 5 | stuff
1 | node1 | red | type1 | 2 | 6 | morestuff
2 | node2 | blue | type2 | 3 | 10 | whatever
3 | node3 | green | type3 | null | null | null
4 | node4 | orange | type4 | 4 | 20 | xxx1
4 | node4 | orange | type4 | 5 | 21 | xxx2
4 | node4 | orange | type4 | 6 | 22 | xxx3
etc...
(ie this assumes node 1 has two relationships, node 2 has 1 relationship, node 3 has no relationships, and node 4 has 3).
And then, your code that builds the array just needs to iterate the results, building a new node only when the current record's node is not the same as the previous one (ie we're relying on the ORDER BY node.id to "gather" all the info for a particular node, sequentially).
This code hasn't been tested, but I think the intent is clear, you should be able to bend this as required - but it basically just implements the above.
Replace your while loop with all of this.
$previd = -1;
while($row=$result->FetchRow())
{
$id= (float)$row['n_id']; // <--- note change from 'id' to 'n_id'
$name = $row['name'];
$color1 = $row['color'];
$type1 = $row['type'];
$to= (float)$row['goingto'];
$thumb =$row['thumb']; //image path
// Is this row the start of a new node?
if ($previd != $id) {
// Yes, new node. Record our new node id, for future new node checks.
$previd = $id;
// Store the previous node we've already built, now that it's complete (but only if there was a previous node!)
if ($previd != -1) {
$array.push($node);
}
// Start our new node off, ready to accept adjacencies
$node = array(
"adjacencies" => array(),
"data" => array(
"$"."color" => $color1,
"$"."type" => $type1
),
"id" => $id,
"name" => "<img src='".$thumb."' height='25' width='25' alt='root'/><label>".$name."</label>");
}
// Any adjacency for this node, on this row?
if ($to != null) { // <-- Not sure about this line!
// Yes there is, so create the new adjacency record and add it to the current node's adjacency array.
$node["adjacencies"].push(
array(
"nodeTo" => "$to",
"nodeFrom" => "$id",
"data" => array()
)
);
}
}
I'm not sure how "no adjacency" will be represented in $to - ie if this will be "null" or what. I'll leave that to you to test, but suffice to say you'll need to reflect this in the line if ($to != null) { // <-- Not sure about this line!

Related

(Laravel/PHP) SQL Query that selects all orders from table that matches product_id and variation_id in array

Im trying to build a SQL Query that will select all orders from a table that matches options that i defined.
Databse i use: Mysql
Language: PHP
Basicly i have a array that looks like this.
[
[
"user_id" => 1,
"product_id" => 5548,
"variation_id" => 14
],
[
"user_id" => 1,
"product_id" => 5548,
"variation_id" => 15
],
[
"user_id" => 1,
"product_id" => 4422,
"variation_id" => 4
]
]
This means that the user(id: 1) has one product with the "id" of 5548, and then he also has 2 variations of that product that are "id" 14 and 15. You can also see that the same user owns the product(id:4422) that has variation(id:4).
I then have a "order_lines" table that looks like this
order_lines
+----+-----+---------+-----------------------------+
| id | uid | user_id | product_id | variation_id |
+----+-----+---------+-----------------------------+
| 1 | 1 | 1 | 5548 | 14 |
+----+-----+---------+-----------------------------+
| 2 | 2 | 1 | 5548 | 15 |
+----+-----+---------+-----------------------------+
| 3 | 3 | 1 | 4422 | 4 |
+----+-----+---------+-----------------------------+
| . | . | . | .... | .. |
+----+-----+---------+-----------------------------+
I now need a SQL Query that selects all the rows where there is a match between the user_id, product_id and variation_id that are defined in the array.
The output should contain all rows that meet these conditions.
I hope someone can pin me in the right direction.
I'm building in Laravel if you got the query builder just at your hand. Else i very much appreciate an SQL Query.
if I am getting you right, below code will help you, using just Core PHP
foreach($array as $arr){
$user_id = $arr['user_id'];
$prodct_id = $arr['prodct_id'];
$variation_id = $arr['variation_id'];
$query = "SELECT * FROM order_lines WHERE user_id = $userId AND product_id = $productId AND variation_id = $variationId";
$queryResult = mysql_fetch_assoc($query);
$yourCollection[] = $queryResult;
}
print_r($yourCollection);
Try below code to use Laravel Query Builder, below code will help you to get results for multiple users based on product and variation.
$qb_order_lines = DB::table('order_lines');
$where_condition = [
['user_id' => '', 'product_id' => '', 'variation_id' => ''],
];
foreach ($where_condition as $condition) {
$qb_order_lines->orWhere(function($query) use ($condition) {
$query->where('user_id', $condition['user_id'])
->where('product_id', $condition['product_id'])
->where('variation_id', $condition['variation_id']);
});
}
$obj_result = $qb_order_lines->get();
If you want to get it for only one user, use below code
$obj_result = DB::table('order_lines')
->where('user_id', $condition['user_id'])
->where('product_id', $condition['product_id'])
->where('variation_id', $condition['variation_id'])
->get();
You can modify the above query builders based on your requirements like select fields or group by.
Let me know if you need any help.
For anyone interesting.
My problem was that i needed to count of many matches that were between my array and my database.
Instead of selecting and outputting. I eneded up using sql count() function in a query, that did the job.

create array using two foreach

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

MySQL select multi dimensional?

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

How to get data from mysql table , if i have four kinds on records in a table

|id | name | status | updated |
|1 |John |Active |2018-04-12 |
|2 |Peter |Active |2018-04-12 |
|3 |Kenny |Inactive|2018-04-13 |
|4 |Mike |Active |2018-04-14 |
|5 |Neeth |Pending |2018-04-14 |
|6 |Kenith|Inactive|2018-04-15 |
I need the array or records something like, as I mentioned below.
1st answer:
'Active' : [{
id:1,
name:John
},{
id:2,
name:Peter
},{
id:4,
name:Mike
}],
'Inactive' : [{
id:3,
name:Kenny
},{
id:6,
name:Neeth
}],
'Pending' : [{
id:5,
name:Kenith
}]
2nd Answer:
'2018-04-12':[{
id:1,
name:John
},{
id:2,
name:Peter
}],
'2018-04-13' : [{
id:3,
name:Kenny
}],
'2018-04-14' : [{
id:5,
name:Mike
},{
id:5,
name:Neeth
},
'2018-04-15' : [{
id:5,
name:Kenith
}]
You cannot produce the nested array data as shown in your question directly in a query.
You can only get a flat array. You will have to create the array in PHP (or any other server language)
by looping through the results of the query.
SELECT
`status`,
`id`,
`name`
FROM table
ORDER BY `status`,`id`;
Pseudo code to create the array:
Assumes query result is in an associated array $allrows. Output array $rslt will be structured as requested in the first part of output in the question.
// Assumes query result is in an associated array ```$allrows```
$rslt = array();
foreach($allrows as $rowno => $row) {
if(!isset($rslt[$row['status']])) {
$rslt[$row['status']] = array();
}
$rslt[$row['status']][] = array('id' => $row['id'],'name' => $row['name']);
}
For the 2nd answer:
SELECT
`updated`,
`id`,
`name`
FROM table
ORDER BY `updated`,`id`;
PHP Pseudo code:
Assumes query result is in an associated array $allrows. Output array $rslt will be structured as requested in the second part of output in the question.
// Assumes query result is in an associated array ```$allrows```
$rslt = array();
foreach($allrows as $rowno => $row) {
if(!isset($rslt[$row['updated']])) {
$rslt[$row['updated']] = array();
}
$rslt[$row['status']][] = array('id' => $row['id'],'name' => $row['name']);
}

Pivoting mysql result with dates and sums

I have some data in mysql that I need to create some reports from.
My data are coming from the following query :
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl GROUP BY StoreNbr,StoreName,Date;
This results in the following data (just a small subset for my example):
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | Date | SUM(Sales) |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 2016-01-16 | 115.09 |
| 1112 | Store1 | 2016-01-17 | 81.00 |
| 1113 | Store2 | 2016-01-16 | 112.44 |
| 1113 | Store2 | 2016-01-17 | 56.61 |
I would like to transform my data to be this way :
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | 2016-01-16 | 2016-01-17 |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 115.09 | 81.00 |
| 1113 | Store2 | 112.44 | 56.61 |
Obviously there might be thousands of rows (stores) and unknown number of dates to be returned in the query as my query might be run like this (this will need to return 120+ number of columns for dates):
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10') GROUP BY StoreNbr,StoreName,Date;
There are a few ways to do this, none very simple. I did some research and there are some that mention that mysql does not support pivoting. I am running mariadb though, and saw that mariadb supports pivoting through the connect engine. I was unable to make it work though (adjust their official examples on my data).
Another way is lots of IFs and Cases, but most of the answers I am finding are very difficult to adapt or are tailored only to the data the guy that asks provides.
Another approach would be to process the data as they come out on my array as I have a json response in the end that feeds a datatable. - This is another think I have not managed to figure out yet.
I am looking for a way to get the desired output independent on the amount of dates (and I guess dates could be replaced by weeks or whatever else). Can anyone help?
Select all distinct dates
SELECT DISTINCT Date
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
ORDER BY Date;
and initialize an array which is indexed by that dates storing zeros:
$dateIndexedArray = array();
while($row = $stmt1->fetch(PDO::FETCH_ASSOC) {
$dateIndexedArray[$row['Date']] = 0;
}
The arry will look like
[
'2016-01-16' => 0,
'2016-01-17' => 0
]
Then execute your query
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date;
And store the "Sales" in a date indexed array per store
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = array(
'StoreNbr' => $row['StoreNbr'],
'StoreName' => $row['StoreName'],
'Sales' => $dateIndexedArray
);
}
$report[$storeIndex]['Sales'][$row['Date']] = $row['Sales'];
}
The $report array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1',
'Sales' => [
'2016-01-16' => 115.09,
'2016-01-17' => 81.00
]
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'Sales' => [
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
]
Update:
If you need all data to be in one row for each store you can change the code to:
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
The resulting array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1'
'2016-01-16' => 115.09,
'2016-01-17' => 81.
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
Update 2: To get the total sales per store you can use WITH ROLLUP
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date WITH ROLLUP;
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
if ($row['StoreName'] === null) {
// Skip this row.
// It contains total sales grouped by StoreNbr
// (or not grouped if StoreNbr === null).
continue;
}
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
if ($row['Date'] === null) {
// This row contains total sales grouped by StoreNbr & StoreName
$report[$storeIndex]['TotalSales'] = $row['Sales']
} else {
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
}
Please note that i've never used WITH ROLLUP. So you might need to adjust the code.

Categories