Nested JSON from 3 one-to-many Tables - php

I'm building a Sencha-Touch 2 app and I have some trouble with the recuperation of my data from server side (mysql DB).
Here's my data model :
Table1 :
ID:int
description:varchar(100)
Table2 :
ID:int
description:varchar(100)
table1_ID:int
Table3 :
ID:int
name:varchar(100)
info:varchar(100)
table2_ID:int
Table1 is join to Table2 with a one-to-many relationship and same between Table2 and Table3.
What I want from server is a nested JSON who looks like this :
[
Table1_object1_ID: 'id' : {
Table1_object1_description: 'description',
Table2_Objects : [
'Table2_object1': {
Table2_object1_id : 'id',
Table2_object1_description : 'description'
Table3_Objects : [
table3_object1: {
Table3_object1_name : 'name',
Table3_object1_info : 'info',
},
table3_object2: {
Table3_object2_name : 'name',
Table3_object2_info : 'info',
},
table3_object3: {
Table3_object3_name : 'name',
Table3_object3_info : 'info',
},
etc...
],
},
'Table2_object2': {
Table2_object2_id : 'id',
Table2_object2_description : 'description'
Table3_Objects : [
...
]
},
etc....
]
},
Table1_object2_ID: 'id' : {
etc....
]
In my App, I use 3 Models for each Table, and ideally I want to save my data in 3 Stores, but that will be an other problem ;-)
The first Store (based on the Model from Table1) do a JsonP request to get the Nested JSON.
Actually my SQL request in the PHP file is simple :
SELECT *
FROM Table1
INNER JOIN Table2 ON Table1.ID = Table2.table1_ID
INNER JOIN Table3 ON Table2.ID = Table3.table2_ID;
I tried to make an array in PHP from my SQL results but cannot get the expect result.
I also try to change my SQL with GROUP BY and GROUP_CONCAT but same here, cannot get the JSON I want.
Some help would be really appreciate.

Runnable code with some sample data: http://codepad.org/2Xsbdu23
I used 3 distinct SELECTs to avoid the unnecessary repetitions.
Of course you have to customize the $result array to your exact desired JSON format, but I think it is not that hard.
// assume $t1/2/3 will be arrays of objects
$t1 =
SELECT Table1.*
FROM Table1
WHERE Table1.ID = 111
$t2 =
SELECT Table2.*
FROM Table2
WHERE Table2.table1_ID = 111
$t3 =
SELECT Table3.*
FROM Table2
INNER JOIN Table3 ON Table2.ID = Table3.table2_ID
WHERE Table2.table1_ID = 111
function array_group_by( $array, $id ){
$groups = array();
foreach( $array as $row ) $groups[ $row -> $id ][] = $row;
return $groups;
}
// group rows from table2/table3 by their parent IDs
$p2 = array_group_by( $t2, 'table1_ID' );
$p3 = array_group_by( $t3, 'table2_ID' );
// let's combine results:
$result = array();
foreach( $t1 as $row1 ){
$row1 -> Table2_Objects = isset( $p2[ $row1 -> ID ]) ? $p2[ $row1 -> ID ] : array();
foreach( $row1 -> Table2_Objects as $row2 )
$row2 -> Table3_Objects = isset( $p3[ $row2 -> ID ]) ? $p3[ $row2 -> ID ] : array();
$result[] = $row1;
}
echo json_encode( $result );

Related

PDO - get from one to many structure

I have this db structure:
create table article(
id int AUTO_INCREMENT PRIMARY KEY,
title varchar(50),
text text
)
create table comments(
id int AUTO_INCREMENT PRIMARY KEY,
article int not null
username varchar(30) not null,
text text not null,
foreign key(article) references article(id) on delete cascade
)
I would like to get articles with comments and convert to json with this structure:
[
{
id: 1,
title: "article1",
text: "text1",
"comments": [
{
id: 1,
username: "user1",
text: "text"
}
]
}
]
This is my code:
$query = $pdo->query('select * from article as a join comments as c on c.article =a.id');
$query->execute();
var_dump(json_encode($query->fetchAll(PDO::FETCH_ASSOC)));
and result:
[{"id":"1","title":"artile1","text":"comment1","article":"1","username":"user1"}]
It is any way how to get article and comments as inner array? I could do it manually but, I will have a lot of tables with many columns.
Thanks for advices
It looks like it is not possible using PDO fetch modes. They are powerful, but unfortunately, I was not able to get the output you wanted.
You can achieve this outcome using a simple loop. The downside is that you have to create the array manually.
$stmt = $pdo->prepare('SELECT a.id AS aid, a.title, a.text AS atext, c.id AS cid, c.username, c.text AS ctext
FROM article AS a
JOIN comments AS c ON c.article =a.id ');
$stmt->execute();
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$id = null;
$data = [];
foreach ($stmt as $row) {
$comment = [
'id' => $row['cid'],
'username' => $row['username'],
'text' => $row['ctext'],
];
if ($id == $row['aid']) {
// If parent ID still the same append only comment
$data[array_key_last($data)]['comments'][] = $comment;
} else {
// set new id and append a whole new row
$id = $row['aid'];
$data[] = [
'id' => $row['aid'],
'title' => $row['title'],
'text' => $row['atext'],
'comments' => [$comment]
];
}
}
PDO has plenty of fetch modes and you can mix them together, but it looks like none of them can cope with joins the way you would like them too. They are all described here in https://phpdelusions.net/pdo/fetch_modes

MySQL select many tables, each table's results being it's own key/val pair

Hopefully this isn't a stupid question, was hard to explain in the title but...I have a certain amount of tables and most of them don't have the same structure...except startDate. For example, say there's three tables with column structures like this:
t1:
id a b c startDate
t2:
id e f g h i startDate
t3:
id j k startDate
I want to get them all into a result which will look something like this json type format:
[{"t1":array({
//all rows for t1
})
},{"t2":array({
//all rows for t2
})
},{"t3":array({
//all rows for t3
})
}]
I want to be able to do something like this in PHP:
foreach ($results as $key => $val) {
if ($key == "t1") {
//parse t1 $val array
}elseif ($key == "t2") {
//parse t2 $val array
}elseif ($key == "t3") {
//parse t3 $val array
}
}
I know it's possible with UNION ALL but I would have to do NULL values for columns so if I had 6 or 7 tables, the query would be really messy. Is there a way to use GROUP_CONCAT to get the desired result, also with a startDate BETWEEN arg1 AND arg2?
Or...would it be best to do a for loop and query each table, then put it all together in PHP?
Please try this
//db connection
$json = array();
$tables = array("t1","t2","t3");
for($i=0;$i<count($tables);$i++){
$rows = array();$row = array();
$sql = "SELECT * FROM $tables[$i]";
$result = mysql_query($sql);
while($row = mysql_fetch_assoc($result)){
$rows[] = $row;
}
$json[$tables[$i]] = $rows;
}
//you can use print_r($json) to display the array or json like
echo json_encode($json)

mysql join query from 3 tables

i want to the join query from three table 1 tbl_customerinfo , 2 tbl_customerinfo ,3- tbl_cust_ext_pref;
i want add to table 3( tbl_cust_ext_pref ) in this query ..
i have using join query from two table and get all informations and code is working a- tbl_customerinfo feild name ( id ,c_shortcode, c_type)
b - tbl_cust_extinfo
feild name ( cx_id ,cx_gender,cs_createdby)
i have use this code
/* code here */
$this->db->select ( this is a table
'tbl_customerinfo.c_id,
tbl_customerinfo.c_shortcode,
tbl_customerinfo.c_type,
/*this is b table tbl_cust_extinfo.cx_id, tbl_cust_extinfo.cx_gender, tbl_cust_extinfo.cs_createdby*/
');
$this->db->from('tbl_customerinfo');
$this->db->join
(
'tbl_cust_extinfo',
'tbl_customerinfo.c_id = tbl_cust_extinfo.cx_id'
);
$query = $this->db->get();
//$info='tbl_customerinfo';
if($query->num_rows() > 0)
{
foreach ($query->result() as $row)
{
//print_r($row);
//exit();
$info[] = array(
'c_id' => $row->c_id,
'c_shortcode' => $row->c_shortcode,
'c_type'=> $row->c_type
array(
'cx_id'=> $row-> cx_id,
'cx_gender'=> $row -> cx_gender);
Write your sql query once in a single query:
$sql = 'select * from table1 join table2 on table1.c1 = table2.c2 '
. 'join table3 on table1.c1 = table3.c3';
$res = mysql_query($sql);

Combine mysql query with sub query results into one PHP array

I have a table that contains events, to list these events I loop through the event table, check the event type and look up it's value in it's specific table with it's eventId.
At the moment this uses one query to get the 20 events and then up to 3 queries to get the data on those events. I currently have it coded procedurally but need to change it so that at the end it just returns the data in array form.
Here's a Pseduo code example of what I need to achieve:
while(eventQuery):
if commentQueryResult;
$array .= commentQueryResult;
if forumPostQueryResult;
$array .= forumPostQueryResult;
if uploadItemQueryResult;
$array .= uploadItemQueryResult;
endwhile;
return $array; // Combined returned results as one array
I will then be able to access the data and just foreach loop through it.
I'm just not sure the best way to combine multiple result sets into an array?
OR you could try and combine them into one query ...
$eventResult = mysql_query(
'SELECT userevent.event, userevent.eventId, userevent.friendId
FROM userevent
WHERE userevent.userId = 1 OR userevent.friendId = 1
ORDER BY userevent.id DESC
LIMIT 20'
);
while ($eventRow = mysql_fetch_row($eventResult)){
if($eventRow[0] == 1){
$result = mysql_fetch_array(mysql_query("
SELECT forumRooms.id, forumRooms.title
FROM forumPosts
INNER JOIN forumRooms ON forumPosts.`forumTopic` = forumRooms.`id`
WHERE forumPosts.id = '$eventRow[1]'"));
}
elseif($eventRow[0] == 2){
$result = mysql_fetch_array(mysql_query("
SELECT game.id, game.uriTitle, game.title
FROM usergamecomment
INNER JOIN game ON usergamecomment.`gameId` = game.id
WHERE usergamecomment.id = $eventRow[1]"));
}
elseif($eventRow[0] == 4){
$result = mysql_fetch_array(mysql_query("
SELECT usercomment.comment, UNIX_TIMESTAMP(usercomment.TIME), `user`.id, `user`.username, `user`.activate
FROM usercomment
INNER JOIN `user` ON usercomment.`userId` = `user`.id
WHERE usercomment.id = $eventRow[1]
AND `user`.activate = 1"));
}
elseif($eventRow[0] == 5){
$result = mysql_fetch_array(mysql_query("
SELECT game.id, game.title, game.uriTitle
FROM game
WHERE game.id = $eventRow[1]"));
}
// Combined Results as array
}
I'm in the process of converting all of these to PDO, that's the next step after working out the best way to minimise this.
Challenge accepted. ;)
Since you are actually only interested in the results inside the while loop, you could try this single query. Due to the LEFT JOINS it might not be faster, pretty much depends on your database. The final $result contains all elements with their respective fields.
$result = array();
$q = 'SELECT userevent.event AS userevent_event,
userevent.eventId AS userevent_eventId,
userevent.friendId AS userevent_friendId,
forumRooms.id AS forumRooms_id,
forumRooms.title AS forumRooms_title,
game.id AS game_id,
game.uriTitle AS game_uriTitle,
game.title AS game_title,
usercomment.comment AS usercomment_comment,
UNIX_TIMESTAMP(usercomment.TIME) AS usercomment_time,
user.id AS user_id,
user.username AS user_username,
user.activate AS user_activate,
g2.id AS game2_id,
g2.uriTitle AS game2_uriTitle,
g2.title AS game2_title
FROM userevent
LEFT JOIN forumPosts ON forumPosts.id = userevent.eventId
LEFT JOIN forumRooms ON forumPosts.forumTopic = forumRooms.id
LEFT JOIN usergamecomment ON usergamecomment.id = userevent.eventId
LEFT JOIN game ON usergamecomment.gameId = game.id
LEFT JOIN usercomment ON usercomment.id = userevent.eventId
LEFT JOIN user ON usercomment.userId = user.id
LEFT JOIN game g2 ON userevent.eventId = g2.id
WHERE (userevent.userId = 1 OR userevent.friendId = 1)
AND userevent.eventId >= (SELECT userevent.eventId
WHERE userevent.userId = 1 OR userevent.friendId = 1
ORDER BY userevent.id DESC LIMIT 1,20);';
$r = mysql_query($q);
while ( $o = mysql_fetch_row($r) ) {
switch($o['userevent_event']) {
case 1:
$result[] = array(
'id' => $o['forumsRooms_id'],
'title' => $o['forumsRooms_title'],
);
break;
case 2:
$result[] = array(
'id' => $o['game_id'],
'uriTitle' => $o['game_uriTitle'],
'title' => $o['game_title'],
);
break;
case 4:
$result[] = array(
'comment' => $o['usercomment_comment'],
'time' => $o['usercomment_time'],
'id' => $o['user_id'],
'username' => $o['user_username'],
'activate' => $o['user_activate'],
);
break;
case 5:
$result[] = array(
'id' => $o['game2_id'],
'uriTitle' => $o['game2_uriTitle'],
'title' => $o['game2_title'],
);
break;
}
}
Note: Eventually, the query has to be edited slightly, I just wrote that out of my head w/o testing. Optimization can surely be done if I'd knew more about the db structure.
Also, this is merely a proof of concept that it can indeed be done with a single query. ;)

php mysql advanced selecting

table 1 = events -> holds a list of events
table 2 = response -> holds a list of users responses has a foreign key of eid which corresponds with events table
I need to join the tables together so it can return an array similar to this on php.
array(
0 => array(
'title' =>'', //title of events table
'contents' =>'this is a demo', //contents of events table
'users' => array( //users comes from response table
0 = array(
'firstname'=>'John',
),
1 = array(
'firstname'=>'James',
)
)
)
);
can this be done? using mysql only? coz i know you can do it on php.
You can gather all the necessary data in MySQL with a single JOIN query.
However, PHP will not return an array like your example by default. You would have to loop over the query result set and create such an array yourself.
I'm pretty sure the answer is no, since mysql always returns a "flat" resultset. So, you can get all the results you're looking for using:
SELECT e.title, e.contents, r.firstname
FROM events e LEFT JOIN response r ON e.id = r.eid
ORDER BY e.id, r.id
And then massaging it into the array with php, but I imagine this is what you're doing already.
EDIT:
By the way, if you want 1 row for each event, you could use GROUP_CONCAT:
SELECT e.title, e.contents, GROUP_CONCAT(DISTINCT r.firstname ORDER BY r.firstname SEPARATOR ',') as users
FROM events e LEFT JOIN response r ON e.id = r.eid
GROUP BY e.id
Just as Jason McCreary said. For you convenience, here is the query you need (though the field names might not be matching your db structure, as you did not provide this information)
SELECT
*
FROM
events
LEFT JOIN
responses ON (events.id = responses.eid)
The SQL is:
SELECT events.id, events.title, events.contents,
response.id AS rid, response.firstname
FROM events LEFT JOIN response
ON events.id = response.eid
I thought I would show you how to massage the results in to the array as you wished:
$query = "SELECT events.id, events.title, events.contents, response.id AS rid, response.firstname
FROM events LEFT JOIN response ON events.id = response.eid";
$result = mysql_query($query);
$events = array();
while ($record = mysql_fetch_assoc($result)) {
if (!array_key_exists($record['id'], $events)) {
$events[$record['id']] = array('title' => $record['title'], 'contents' => $record['contents'], 'users' => array());
}
if ($record['rid'] !== NULL) {
$events[$record['id']]['users'][$record['rid']] = array('firstname' => $record['firstname']);
}
}
mysql_free_result($result);

Categories