PHP joining row from database - php

I need an advice how to display data from array (result of sql query) in PHP.
Here's the problem:
My query result is 2 row (top figure). Currently, I'm using foreach method to display all data, thus, the foreach will iterate 2 times, right?
Now, I want to ask:
In my web page, how I can display like the bottom figure??
How to join the second row (the theory's score is 0, and the lab's score is 75 and 80) to the first row? So it'll be ONLY 1 row for ID 112.
EDIT:
The SQL query is not my part. I've just received that data (top figure), and my job only to display it to web page.

Well as you used to display rows with foreach you could do it the same way:
* create new array
* loop through your current array
* save to new array by ID and do SUMs
* you can do output from newly created array.

create new array by loop through your current array which will filter out 0 values probably by using array_walk_recursive() or array_map() or array_filter()
then at this stage you got filtered array
you can do output from filtered array

I'm wondering why you have two rows with same 'id'. Usually 'id' should be the primary key...
Anyway, you could simply combine the rows with same 'id', adding the equally named fields casting them like integers.
EDIT 1-2:
Supposing code is in associative arrays:
$rows = array(
0 => array(
'id' => 112,
'theory' => array('score1' => 70, 'score2' => 80),
'lab' => array('score1' => 0, 'score2' => 0)
),
1 => array(
'id' => 112,
'theory' => array('score1' => 0, 'score2' => 0),
'lab' => array('score1' => 75, 'score2' => 80)
),
2 => array(
'id' => 110,
'theory' => array('score1' => 0, 'score2' => 0),
'lab' => array('score1' => 75, 'score2' => 80)
)
);
// New buffer for data
$newArray = array();
// Loop to combine buffer
foreach ($rows as $row) {
if (array_key_exists($row['id'], $newArray))
{
$newArray[$row['id']]['theory']['score1'] += $row['theory']['score1'];
$newArray[$row['id']]['theory']['score2'] += $row['theory']['score2'];
$newArray[$row['id']]['lab']['score1'] += $row['lab']['score1'];
$newArray[$row['id']]['lab']['score2'] += $row['lab']['score2'];
} else {
$newArray[$row['id']]['theory']['score1'] = $row['theory']['score1'];
$newArray[$row['id']]['theory']['score2'] = $row['theory']['score2'];
$newArray[$row['id']]['lab']['score1'] = $row['lab']['score1'];
$newArray[$row['id']]['lab']['score2'] = $row['lab']['score2'];
}
}
// Loop to show data
foreach ($newArray as $key => $value) {
echo $key." ".$value['theory']['score1']." ".$value['theory']['score2']." ".$value['lab']['score1']." ".$value['lab']['score2']."\n";
}
Output:
112 70 80 75 80
110 0 0 75 80

Let's say that you receive your data as Arrays:
$data=array();
$data[0]=[112,70,80,0,0];
$data[1]=[112,0,0,75,80];
$data[2]=[113,71,81,0,0];
$data[3]=[113,0,0,76,81];
If you know in advance that each ID will have exactly two records:
echo '<table>';
echo '<thead>';
echo '<tr>';
echo '<th rowspan="2">ID</th><th colspan="2">theory</th><th colspan="2">lab</th>';
echo '</tr>';
echo '<tr>';
echo '<th>score1</th><th>score2</th><th>score1</th><th>score2</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';
for($i=0;$i<count($data);$i++)
{
$d=$data[$i];
echo '<tr>';
echo '<td>'.$d[0].'</td>'; //ID
echo '<td>'.$d[1].'</td><td>'.$d[2].'</td>';
$d=$data[++$i]; //take next element from array
echo '<td>'.$d[3].'</td><td>'.$d[4].'</td>';
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
Code on PHPfiddle.
But generally, your SQL query should be changed in order to represent pivoted data and then you will not have this issue. However, you emphasized that this is out of your power.

Related

3 Dimensional Associative Array to HTML table

This is what my array looks like.
(int) Year 1 => array(
'Department 1' => array(
'Sales' => '12345'
),
'Department 2' => array(
'Sales' => '12345'
),
),
(int) Year 2 => array(
'Department 1' => array(
'Sales' => '12345'
),
'Department 2' => array(
'Sales' => '12345'
),
)
I want to be able to create a table where the headers are the Years on the X axis.
The Y axis needs to start with the Department name. The adjoining cells need to have sales, revenue, and total in one cell.
It should look like this...
Department____| ___YEAR1_______ | __Year2
Department 1 |Sales [dept 1][year 1] | Sales[dept 1][year 2]
Department 2 |Sales [dept 2][year 1] | Sales [dept 2][year 2]
I used nested foreach loops
foreach($List as $key=>$row) {
echo "<tr><td>".$key."</td>";
foreach($row as $key2=>$col){
echo "<td>" . 'Sales: '.round($col['sales'],2) ."</td>";
}
echo "</tr>";
}
echo "</table>"; ?>
but it printed the table with departments names on top on the x axis and years on the y axis. How do I get years on the x axis and departments on the y axis.
The problem is that the dimensions of your array don't correspond with the order that you want to iterate through them for your table. You need to pull out the keys explicitly to iterate through them in the nested loops.
$years = array_keys($List);
// Print column headings
echo "<table><thead><tr><th>Department</th>";
foreach ($years as $year) {
echo "<th>$year</th>";
}
echo "</tr></thead>";
// Print data rows
echo "<tbody>"
foreach (array_keys($List[$years[0]]) as $dept) {
echo "<tr><th>$dept</th>";
foreach ($years as $year) {
echo "<td>{$List[$year][$dept]['Sales'}]</td>";
}
echo "</tr>";
}
echo "</tbody></table>";
As I mentioned, this depends on all years having the same departments. If you add new departments over time, it won't work. You can fix this by using array_reduce to merge all the department lists, I'll leave that as an exercise for the reader.

Recursive table which child parent relationship

I have a problem with a recursive function that takes too many resources to display a child-parent relation in a dropdown list. For example:
home
-menu 1
-menu 2
home 1
-menu 3
-menu 4
I've written some code for a recursive call to the database each time, so that's the reason why my code takes so many resources to run.
Below is my code:
--call recursive
$tmp = $this->get_nav_by_parent(0);
$a_sel = array('' => '-Select-');
$a_sel_cat = array('home' => 'home');
$this->get_child_nav_cat($tmp, 0, $a_sel);
--
public function get_nav_by_parent($parent) {
$all_nav = $this->db
->select('id, title, parent')
->where('parent',$parent)
->order_by('position')
->get('navigation_links')
->result_array();
$a_tmp = array();
foreach($all_nav as $item)
{
if($parent != 0){
$item['title'] = '--' . $item['title'];
}
$a_tmp[] = $item;
}
return $a_tmp;
}
-- Recursive function
public function get_child_nav_cat($a_data, $parent, &$a_sel) {
foreach($a_data as $item) {
$a_sel[$item['page_slug_key']] = $item['title'];
$atmp = $this->get_nav_by_parent($item['id']);
$this->get_child_nav_cat($atmp, $item['id'], $a_sel);
}
return $a_sel;
}
Please give me suggestions for the best solution to display the data as child-parent relationship in select box.
Thanks in advance!
Best way to display parent child relationship is mentain parent and child flag in Database instead of
fetching value using loop.
In your case Home, Home 1 is parent flag and menus belong on child flag.
fetch data from db and your loop look like this:-
$arr = array(0 => array('name' => 'home','parent' => 0),
1 => array('name' => 'menu 1 ','parent' => 1),
2 => array('name' => 'menu 2 ','parent' => 1),
3 => array('name' => 'home 1','parent' => 0),
4 => array('name' => 'menu 3 ','parent' => 2),
5 => array('name' => 'menu 4','parent' => 2)
);
$dd_html = '<select>';
foreach($arr as $k => $v){
if($v['parent'] == 0 )
$dd_html .='<option>'.$v['name'].'</option>';
else
$dd_html .='<option>--'.$v['name'].'</option>';
}
$dd_html .= '</select>';
echo $dd_html;
Output :-
home
-menu 1
-menu 2
home 1
-menu 3
-menu 4
Set ParentID=0 for detecting root item
then execute this:
SELECT * FROM table ORDER BY ParentID, ID
Then iterate through result and when ParentID changed, create new level.

PHP multidimensional array counter

Im trying to make a multidimensional array with two columns. Name and Counter. I can do a single array with all the names. But I dont know how to make it multidimensional and be able to still update the counters. Code i got so far is
if (!in_array($prodname, $da)){
array_push($da, $prodname);
}
and then I can dump it back out with a foreach. How do I make it two dimensional? How can I say alright this exists update the old value? etc.
If you only need name and counter then you should just be able to use a normal array:
$nameCountArray = array();
foreach($names as $name){
if(!array_key_exists($name,$nameCountArray)){
$nameCountArray[$name] = 1;
}else{
$nameCountArray[$name] = $nameCountArray[$name] + 1;
}
}
If you do need multidimensional arrays these are just arrays of arrays and can be accessed as such. A good example of this is using a 2d array to store locations (say on a 3 by 3 grid):
$twoDArray = array(
0 => array(0 => 1,
1 => 4,
2 => 7),
1 => array(0 => 2,
1 => 5,
2 => 8),
2 => array(0 => 3,
1 => 6,
2 => 9)
);
//Grab the item at 1,2
$item = $twoDArray[1][2];//Will give '8'
Supposing you want $da to look like this:
Array(
"name1" => array("score1" => 80, "score2" => 100),
"name2" => array("score1" => 50, "score2" => 60),
"name3" => array("score1" => 90, "score2" => 80),
...
)
Then all you need to do is something like:
function setScore($prodName, $scoreName, $score)
{
global $da;
if (!array_key_exists($prodName, $da)) {
$da[$prodName] = array();
}
$da[$prodName][$scoreName] = $score;
}
setScore("name1", "score1", 80);
setScore("name1", "score2", 100);
setScore("name2", "score1", 50);
...
Unless I'm misunderstanding your question, which is very possible.

adding elements at identical keys in multiple multidimensional arrays

I'm trying to do a web application with php and MySQL that can graph the estimate effort of a person based on the data stored in the data base
$luna_start = date('n', strtotime($row_task['data_i']));
$luna_end = date('n', strtotime($row_task['data_s']));
$luna_curenta=$luna_start;
$ultimazi_luna_curenta= cal_days_in_month(CAL_GREGORIAN, $luna_curenta,"2012");
while ($luna_curenta<=$luna_end){
//construieste vectorul
for ($i = 1; $i <= $ultimazi_luna_curenta; $i++) {
$data_curenta= date("Y-m-d", mktime(0, 0, 0, $luna_curenta, $i, 2012));
if (($data_curenta>=$row_task['data_i'])&&($data_curenta<=$row_task['data_s'])){
$efort_a['a'.$row_task['task_id']][$data_curenta]=$efort_mediu;
}
}
$luna_curenta++;
}
all that trouble for getting an array so that i can graph. the array $efort_a looks like this:
Array(
[a19] => Array(
[2012-09-20] => 2.84
[2012-09-21] => 2.84
.......
[2012-10-21] => 2.84
[2012-10-22] => 2.84
)
[a22] => Array
(
[2012-10-01] => 0.1
[2012-10-02] => 0.1
.....
[2012-11-05] => 0.1
[2012-11-06] => 0.1
[2012-11-07] => 0.1
......
[2012-11-25] => 0.1
......
[2012-11-30] => 0.1
)
[a16] => Array
(
[2012-10-08] => 4
[2012-10-09] => 4
[2012-10-10] => 4
[2012-10-11] => 4
......
[2012-10-18] => 4
[2012-10-19] => 4
)
)
further more I'm doing a little more array processing.
extract($efort_a);
$graf_data= array_add($a19,$a22,$a16);
$data = new GoogleChartData($graf_data);
$chart->addData($data);
the problem with all this code is that it isn't a dynamic one. the arrays a19, a22, a16 are named statically and in this particular case the user had only 3 tasks (task id 19, 22 and 16) ... but what if the user has multiple tasks ?.
so the questions is can this code be reorganized, maybe as a function i don't know how, to be a dynamic one ?
could it be possible to do a function that graph's all the users effort (one graph, multiple lines- one for each user)
ps: the function to do the adding of arrays based on keys is:
function array_add($a1, $a2) {
// adds the values at identical keys together
$aRes = $a1;
foreach (array_slice(func_get_args(), 1) as $aRay) {
foreach (array_intersect_key($aRay, $aRes) as $key => $val) $aRes[$key] += $val;
$aRes += $aRay;
}
return $aRes;
}
========================================= day 2 =======================
after some thinking i thought to give another try... so in order to name the keys of array effort_a dynamically i need to know where to stop with the incrementation so :
$sql_nrtask='SELECT *, COUNT(user) AS nr_task
FROM task
WHERE user="ASD"
ORDER BY data_i ASC
';
$query_nrtask= mysql_query($sql_nrtask) or die ('rrr');
while ($row_nrtask= mysql_fetch_array($query_nrtask)) {
$nr_task=$row_nrtask['nr_task']; // no we know how many sub-arrays we'll have
}
for ($j++;$j<=$nr_task;$j++){
while ($luna_curenta<=$luna_end){
//construieste vectorul
for ($i = 1; $i <= $ultimazi_luna_curenta; $i++) {
$data_curenta= date("Y-m-d", mktime(0, 0, 0, $luna_curenta, $i, 2012));
if (($data_curenta>=$row_task['data_i'])&&($data_curenta<=$row_task['data_s'])){
$efort_a['$a'.$j][$data_curenta]=$efort_mediu;
}
}
$luna_curenta++;
}
}
the problem is know that i don't know hot to merge the arrays within $efort_a with 2 conditions:
keeping indexes where they are the same and adding values
adding a new index where it is a new element

PHP/mySQL: Import data and store in hierarchical nested set for use with jsTree

I'm using jsTree to view hierarchical data that is stored in a mySQL database as a nested set (left, right, level, etc.). This is working fine, but I need to allow users to import data by uploading a CSV file. When they do so, any existing data in the table will be removed so I don't have to worry about updating the left/right fields.
The data they will be uploading will be in this format:
"Code","Title"
"100","Unit 100"
"200","Unit 200"
"101","Task 101: This is a task"
"102","Task 102: Another task"
"201","Task 201: Yet another"
"300","Unit 300"
"301","Task 301: Another one"
Everything will be a child of a main "Group" that is a level 1 node. All of the "codes" divisible by 100 (ie. 100, 200, 300) will be level 2 (parent nodes.. children of "Group"). All others will be level 3 (child) nodes of their respective parent nodes (ie. 101 and 102 are children of 100, 201 is a child of 200, etc.)
The resulting table in mySQL should look like this:
id parent_id position left right level title
1 0 0 1 18 0 ROOT
2 1 0 2 17 1 Group
3 2 0 3 8 2 Unit 100
4 2 1 9 12 2 Unit 200
5 3 0 4 5 3 Task 101: This is a task
6 3 1 6 7 3 Task 102: Another task
7 4 0 10 11 3 Task 201: Yet another
8 2 2 13 16 2 Unit 300
9 8 0 14 15 3 Task 301: Another one
The tree would then look like this:
My question is: using PHP, what is the best method to accomplish this? I already have code in place that pulls the data contained in the uploaded CSV file and stores it in an array, but I'm not sure what the logic to convert this to a nested set should look like.
Right now, the data is stored in a 2-dimensional array called $data (in the format $data[$col][$row]):
$data[0][0] = "Code";
$data[0][1] = "100";
$data[0][2] = "200";
$data[0][3] = "101";
$data[0][4] = "102";
$data[0][5] = "201";
$data[0][6] = "300";
$data[0][7] = "301";
$data[1][0] = "Title";
$data[1][1] = "Unit 100";
$data[1][2] = "Unit 200";
$data[1][3] = "Task 101: This is a task";
$data[1][4] = "Task 102: Another task";
$data[1][5] = "Task 201: Yet another";
$data[1][6] = "Unit 300";
$data[1][7] = "Task 301: Another one";
Array ( [0] => Array ( [0] => Code [1] => 100 [2] => 200 [3] => 101 [4] => 102 [5] => 201 [6] => 300 [7] => 301 ) [1] => Array ( [0] => Title [1] => Unit 100 [2] => Unit 200 [3] => Task 101: This is a task [4] => Task 102: Another task [5] => Task 201: Yet another [6] => Unit 300 [7] => Task 301: Another one ) )
Any help would be very much appreciated. I now have the parent_id, position, and level being calculated correctly... I just need to figure out the left/right part. Here is the code I'm currently using (thanks for getting me started Matteo):
$rows = array();
// insert ROOT row
$rows[] = array(
'id' => 1,
'parent_id' => 0,
'position' => 0,
'left' => 1,
'right' => 10000, // just a guess, will need updated later
'level' => 0,
'title' => 'ROOT',
);
echo "<br>";
print_r($rows[0]);
// insert group row
$rows[] = array(
'id' => 2,
'parent_id' => 1,
'position' => 0,
'left' => 2,
'right' => 9999, // just a guess, will need updated later
'level' => 1,
'title' => 'Group',
);
echo "<br>";
print_r($rows[1]);
// next ID to be used
$id = 3;
// keep track of code => ID correspondence
$map = array();
// parse data
for ($i = 1, $c = count($data[0]); $i < $c; ++$i) {
// save ID in the map
$map[$data[0][$i]] = $id;
// initialize the current row
$row = array(
'id' => $id,
'parent_id' => 1,
'position' => 0,
'left' => 0,
'right' => 0,
'level' => 1,
'title' => $data[1][$i],
);
// if the code is multiple of 100
if ($data[0][$i] % 100 == 0) {
$row['parent_id'] = 2;
$row['level'] = 2;
$row['position'] = (floor($data[0][$i] / 100)) - 1;
} else {
// get parent id from map
$row['parent_id'] = $map[floor($data[0][$i] / 100) * 100];
$row['level'] = 3;
$row['position'] = $data[0][$i] % 100;
}
// add the row
$rows[] = $row;
++$id;
echo "<br>";
print_r($row);
}
Given your $data array, you could parse it like this:
// this will contain all the rows to be inserted in your DB
$rows = array();
// insert ROOT row
$rows[0] = array(
'id' => 1,
'parent_id' => 0,
'position' => 0,
'level' => 0,
'left' => 1,
'right' => 10000,
'title' => 'ROOT',
);
// insert group row
$rows[1] = array(
'id' => 2,
'parent_id' => 1,
'position' => 0,
'level' => 1,
'left' => 2,
'right' => 9999,
'title' => 'Group',
);
// keep trace of code => ID correspondence
$map = array();
// next ID to be used
$id = 3;
// keep father => sons relationship
$tree = array();
// keep trace of code => row index correspondence
$indexes = array();
// next row index
$index = 2;
// parse your data
for ($i = 1, $c = count($data[0]); $i < $c; ++$i) {
// current code
$code = $data[0][$i];
// save ID in the map
$map[$code] = $id;
// update the indexes map
$indexes[$code] = $index;
// prepare the current row
$row = array(
'id' => $id,
'title' => $data[1][$i],
)
// get the value of code mod 100
$mod = $code % 100;
// if the code is multiple of 100
if ($mod == 0) {
// the parent_id is 2
$row['parent_id'] = 2;
// it is level two
$row['level'] = 2;
// compute position
$row['position'] = floor($code / 100) - 1;
}
else {
// get the parent code
$parent = floor($code / 100) * 100;
// get parent id from map using parent code
$row['parent_id'] = $map[$parent];
// it is level three
$row['level'] = 3;
// save position
$row['position'] = $mod;
// save in relationship tree
$tree[$parent][] = $code;
}
// add the row
$rows[$index] = $row;
// prepare next id
++$id;
// update row index
++$index;
}
// sort the relationship tree base on the parent code (key)
ksort($tree, SORT_NUMERIC);
// next left value
$left = 3;
// now, using the relationship tree, assign left and right
foreach ($tree as $parent => $sons) {
// calculate parent left value
$parentLeft = $left;
// prepare next left value
++$left;
// to be sure that the sons are in order
sort($sons, SORT_NUMERIC);
// assign values to sons
foreach ($sons as $son) {
// index in the rows array
$index = $indexes[$son];
// set left value
$rows[$index]['left'] = $left;
// set right value
$rows[$index]['right'] = $left + 1;
// increment left value
$left += 2;
}
// calculate parent right value
$parentRight = $left;
// prepare next left value
++$left;
// index of parent in the rows array
$index = $indexes[$parent];
// set the values
$rows[$index]['left'] = $parentLeft;
$rows[$index]['right'] = $parentRight;
}
// update group row right value
$rows[1]['right'] = $left;
// prepare next left value
++$left;
// update root row right value
$rows[0]['right'] = $left;
At this point, you can insert all the rows one at a time.
EDIT: now the script should handle all the required values correctly.
I would use Doctrine2 with a Nested Set Extension. You could use a nice and convenient API and don't have to worry about the nested set implementation:
See
http://www.gediminasm.org/article/tree-nestedset-behavior-extension-for-doctrine-2
or http://wildlyinaccurate.com/simple-nested-sets-in-doctrine-2
There are several extensions on github. Actually, i don't know which one is best.
https://github.com/l3pp4rd/DoctrineExtensions
https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior
https://github.com/blt04/doctrine2-nestedset
List item
If your data is flat, you could parse for keywords like 'Unit' or 'Task' to arrange your elements to the needed hierarchical order.

Categories