How do I get MySQL query result as nested array using PHP? - php

Let's say I have this simple table where pos_level is the level of the position and pos_under is that the PID of the position on top of it.
In level 1, I have 'General Manager' and 'Supervisor'.
In level 2, under 'General Manager(PID: 1)' : 'Asst. Manager', under 'Supervisor(PID: 2)' : 'Marketing'.
In level 3, under 'Asst. manager(PID: 3)' : 'Sales' & 'Purchase', under 'Marketing(PID:2) : none.
+-----+-----------------+-----------+-----------+
| PID | pos_name | pos_level | pos_under |
+-----+-----------------+-----------+-----------+
| 1 | General Manager | 1 | 0 |
| 2 | Supervisor | 1 | 0 |
| 3 | Asst. Manager | 2 | 1 |
| 4 | Sales | 3 | 3 |
| 5 | Purchase | 3 | 3 |
| 6 | Marketing | 2 | 2 |
+-----+-----------------+-----------+-----------+
Now how do I make the query so I get a nested array as the result like this:
Array
(
[0] => Array
(
[pos_level] => 1
[pos_name] => General Manager
[pos_under] => Array
(
[0] => Array
(
[pos_level] => 2
[pos_name] => Asst. Manager
[pos_under] => Array
(
[0] => Array
(
[pos_level] => 3
[pos_name] => Sales
)
[1] => Array
(
[pos_level] => 3
[pos_name] => Purchase
)
)
)
)
)
[1] => Array
(
[pos_level] => 1
[pos_name] => Supervisor
[pos_under] => Array
(
[0] => Array
(
[pos_level] => 2
[pos_name] => Marketing
[pos_under] => Array
(
)
)
)
)
)
I have tried using multiple queries and using array_push using the results, but I have like around 100+ pos_name and I think it is messy, I have also tried using loop to keep running queries for each level and under, also tried using multiple tables for each level, but I am hoping I can use only 1 table and able to query the result as the nested array above for further use in my application.
All answers, comments, and suggestions are very welcomed. Thank you.

As mentioned above in the comments I doubt in general that the approach you are following is a good one. That is because it will not scale! You try to take all entries from your database and pack them into a single, hierarchical array. That means you create several copies of each entry in memory. What will happen with a growing number of entries? You face a constantly raising memory consumption of your script which will obviously make it fail ultimately.
Nevertheless I accepted the challenge and implemented a small demonstration of how to cleverly construct such nested structures with the help of references. Such references not only simplify the creation of nested structures, they also reduce the memory footprint by preventing that entries are copied by value again and again. This however will not solve the general scaling issue with this approach mentioned above, it can only help to reduce it.
I also took the liberty to make a small modification to your approach and introduce a new property "pos_over" to hold entries under an entry. Two reasons for that:
it usually is not a good idea to remove original available data and especially not to replace it with an altered meaning
the meaning of the term "pos_under" is to describe under what other entry an given entry is (to be) placed. But from the "upper" entries point of view the ones "below" it should not be referenced by "pos_under", right? That would contradict the original meaning of that term.
The following code does not depend on a database for demonstration purpose. Instead it reads the raw data from a CSV string and parses that. The actual building of the desired structure structure is marked further down by a comment. Also it should be mentioned that the code expects the original data to reference other entries only after they have been declared. So if processed from top to bottom each entry under another one can expect that other one to already exist.
Note, that the actual code to build that nested structure consists of only a mere 12 clever lines...
<?php
// the raw CSV data
$csvText = <<<CSV
PID|pos_name|pos_level|pos_under
1|General Manager|1|0
2|Supervisor|1|0
3|Asst. Manager|2|1
4|Sales|3|3
5|Purchase|3|3
6|Marketing|2|2
CSV;
// praparation of CSV data
foreach (explode("\n", $csvText) as $csvRow) {
$csvData[] = str_getcsv($csvRow, '|');
}
$csvTitle = array_shift($csvData);
$table = [];
foreach ($csvTitle as $titleKey=>$titleValue){
foreach ($csvData as $csvRow=>$csvColumn) {
foreach ($csvColumn as $csvKey=>$csvValue) {
$table[$csvRow][$csvTitle[$csvKey]] = $csvValue;
}
}
}
// creation of the structure
$catalog = [];
$structure = [];
foreach ($table as $key=>&$entry) {
$entry['pos_over'] = [];
if ($entry['pos_under'] == 0) {
$structure[] = &$entry;
$catalog[$entry['PID']] = &$structure[count($structure)-1];
} else {
$catalog[$entry['pos_under']]['pos_over'][] = &$entry;
$catalog[$entry['PID']] = &$catalog[$entry['pos_under']]['pos_over'][count($catalog[$entry['pos_under']]['pos_over'])-1];
}
}
// demonstration output
print_r($structure);
The output of above experiment is:
Array
(
[0] => Array
(
[PID] => 1
[pos_name] => General Manager
[pos_level] => 1
[pos_under] => 0
[pos_over] => Array
(
[0] => Array
(
[PID] => 3
[pos_name] => Asst. Manager
[pos_level] => 2
[pos_under] => 1
[pos_over] => Array
(
[0] => Array
(
[PID] => 4
[pos_name] => Sales
[pos_level] => 3
[pos_under] => 3
[pos_over] => Array
(
)
)
[1] => Array
(
[PID] => 5
[pos_name] => Purchase
[pos_level] => 3
[pos_under] => 3
[pos_over] => Array
(
)
)
)
)
)
)
[1] => Array
(
[PID] => 2
[pos_name] => Supervisor
[pos_level] => 1
[pos_under] => 0
[pos_over] => Array
(
[0] => Array
(
[PID] => 6
[pos_name] => Marketing
[pos_level] => 2
[pos_under] => 2
[pos_over] => Array
(
)
)
)
)
)

Related

How to create a three dimensional array from sql query PHP

I have 2 tables in a database, 1 of them are linked with a foreign key to the first one. Each row on table 1 is linked to multiple rows in table 2. I am trying to make a query that looks at a WHERE from table 2 and returns multiple rows from table 2 which are sorted into the rows they linked with in table 1 and then put this all into one big multi dimensional array, so it should work something like this:
$array[0][column_name][0] this would use row 1 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][0] this would use row 2 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][3] this would use row 2 from table 1 and give me a the 4th result in the column called column_name
etc
How can I query this and store it in a 3 dimensional array using PHP.
I have tried to word this in as clear manner as possible, if you are unsure what I am asking, please comment and I will update my question to make it clearer.
Assume that we have two tables, Company and Employee:
Company
------------------
ID Company_Name
1 Walmart
2 Amazon.com
3 Apple
Employee
---------------------------------
ID Company_Id Employee_Name
1 1 Sam Walton
2 1 Rob Walton
3 1 Jim Walton
4 1 Alice Walton
5 2 Jeff Bezos
6 2 Brian T. Olsavsky
7 3 Steve Jobs
8 3 Tim Cook
The easiest way to envision a multi-dimensional (nested) array is to mimic the looping required to get it: outer loop is the company, inner loop is the employees:
// ignoring database access, this is just pseudo code
$outer = [];
// select id, company_name from company
foreach $companyResult as $companyRow {
// select * from employee where company_id = ? {$companyRow['id']}
$inner= [];
foreach $employee_result as $employeeRow {
$inner[] = $employeeRow; // ie, ['id'=>'1','Company_Id'=>'1','Employee_Name'=>'Sam Walton']
}
$outer[] = $inner;
}
print_r($outer);
// yields ====>
Array
(
[0] => Array
(
[0] => Array
(
[id] => 1
[Company_Id] => 1
[Employee_Name] => Sam Walton
)
[1] => Array
(
[id] => 2
[Company_Id] => 1
[Employee_Name] => Rob Walton
)
[2] => Array
(
[id] => 3
[Company_Id] => 1
[Employee_Name] => Jim Walton
)
[3] => Array
(
[id] => 4
[Company_Id] => 1
[Employee_Name] => Alice Walton
)
)
[1] => Array
(
[0] => Array
(
[id] => 5
[Company_Id] => 2
[Employee_Name] => Jeff Bezos
)
[1] => Array
(
[id] => 6
[Company_Id] => 2
[Employee_Name] => Brian T. Olsavsky
)
)
[2] => Array
(
[0] => Array
(
[id] => 7
[Company_Id] => 3
[Employee_Name] => Steve Jobs
)
[1] => Array
(
[id] => 8
[Company_Id] => 3
[Employee_Name] => Tim Cook
)
)
)
It is also possible to do if you use associative arrays. Consider the flat file that this query produces:
select company.id company_id, company.name company_name,
emp.id employee_id, emp.employee_name
from company
inner join employee on company.id = employee.company_id
-----
company_id company_name employee_id employee_name
1 Walmart 1 Sam Walton
1 Walmart 2 Rob Walton
1 Walmart 3 Jim Walton
1 Walmart 4 Alice Walton
2 Amazon.com 5 Jeff Bezos
2 Amazon.com 6 Brian T. Olsavsky
3 Apple 7 Steve Jobs
3 Apple 8 Tim Cook
Just use the primary IDs as the keys for your arrays:
$employeeList = [];
foreach($result as $row) {
$cid = $row['company_name'];
$eid = $row['employee_name'];
// avoid uninitialized variable
// $employeeList[$row['company_name']] = $employeeList[$row['company_name']] ?? [];
// easier to read version of above
$employeeList[$cid] = $employeeList[$cid] ?? [];
// assign it...
$employeeList[$cid][$eid] = $row;
}
Or, if you simply want each company row to hold an array of employee names,
$employeeList[$cid][] = $row['employee_name'];
The way that I've shown you is useful if you know the company_id and want to find the associated rows:
foreach($employeeList[2] as $amazon_guys) { ... }
But it's not at all useful if you're trying to group by employee, or some other field in the employee table. You'd have to organize the order of your indexes by your desired search order.
In the end, it's almost always better to simply do another query and let the database give you the specific results you want.

reorder, join and remove elements of an array php

so I have an array that looks like this (easier to see then the raw output)
0 | from:2 page | to:1 split
1 | from:1 split | to:3 page
2 | from:1 split | to:4 page
3 | from:1 split | to:5 page
4 | from:3 page | to:0 join
5 | from:4 page | to:0 join
6 | from:5 page | to:0 join
7 | from:8 page | to:0 join
8 | from:0 join | to:12 page
9 | from:1 split | to:8 page
10 | from:12 page | to:10 end
11 | from: start | to:2 page
what I would like to do is to somehow parse through it and come up with the following:
0 | start
1 | page
2 | split 3, 4, 5, 8
3 | page
4 | end
so essentially I'm trying to organize this into a final array that follows the path from start to finish.
Rules of engagement:
Where there is a split: combine the "to page" into a sub array, and where there is a "to join", remove the reference. and where there is a "from join to page" just make the page. so final output should look something like this:
array(
'start',
'page',
'split' => array (
3, 4, 5, 8
),
'page',
'end'
)
UPDATE:
here is the raw format I'm working with
Array
(
[operators] => Array
(
[0] => Array
(
[type] => join
)
[1] => Array
(
[type] => split
)
[2] => Array
(
[type] => page
)
[3] => Array
(
[type] => page
)
[4] => Array
(
[type] => page
)
[5] => Array
(
[type] => page
)
[8] => Array
(
[type] => page
)
[9] => Array
(
[type] => start
)
[10] => Array
(
[type] => end
)
[12] => Array
(
[type] => page
)
)
[links] => Array
(
[0] => Array
(
[fromOperator] => 2
[toOperator] => 1
)
[1] => Array
(
[fromOperator] => 1
[toOperator] => 3
)
[2] => Array
(
[fromOperator] => 1
[toOperator] => 4
)
[3] => Array
(
[fromOperator] => 1
[toOperator] => 5
)
[4] => Array
(
[fromOperator] => 3
[toOperator] => 0
)
[5] => Array
(
[fromOperator] => 4
[toOperator] => 0
)
[6] => Array
(
[fromOperator] => 5
[toOperator] => 0
)
[7] => Array
(
[fromOperator] => 8
[toOperator] => 0
)
[8] => Array
(
[fromOperator] => 0
[toOperator] => 12
)
[9] => Array
(
[fromOperator] => 1
[toOperator] => 8
)
[10] => Array
(
[fromOperator] => 12
[toOperator] => 10
)
[11] => Array
(
[fromOperator] => 9
[toOperator] => 2
)
)
)
``
Boo Yahh!!
I had to nap on it, but now I got that 4 sure.
<?php
$array = array (
'operators' =>
array (
0 =>
array (
'type' => 'join'
),
1 =>
array (
'type' => 'split'
),
2 =>
array (
'type' => 'page'
),
3 =>
array (
'type' => 'page'
),
4 =>
array (
'type' => 'page'
),
5 =>
array (
'type' => 'page'
),
8 =>
array (
'type' => 'page'
),
9 =>
array (
'type' => 'start'
),
10 =>
array (
'type' => 'end'
),
12 =>
array (
'type' => 'page'
),
),
'links' =>
array (
0 =>
array (
'fromOperator' => 2,
'toOperator' => 1
),
1 =>
array (
'fromOperator' => 1,
'toOperator' => 3
),
2 =>
array (
'fromOperator' => 1,
'toOperator' => 4
),
3 =>
array (
'fromOperator' => 1,
'toOperator' => 5
),
4 =>
array (
'fromOperator' => 3,
'toOperator' => 0
),
5 =>
array (
'fromOperator' => 4,
'toOperator' => 0
),
6 =>
array (
'fromOperator' => 5,
'toOperator' => 0
),
7 =>
array (
'fromOperator' => 8,
'toOperator' => 0
),
8 =>
array (
'fromOperator' => 0,
'toOperator' => 12
),
9 =>
array (
'fromOperator' => 1,
'toOperator' => 8
),
10 =>
array (
'fromOperator' => 12,
'toOperator' => 10
),
11 =>
array (
'fromOperator' => 9,
'toOperator' => 2
)
)
);
Code:
//Flatten to simplify [$op_id => $array['operators'][n]['type']]
//array_combine and array_keys, just makes sure the keys match
//the original array, because we are missing a few keys (7,8 and 11)
//we have to do this or we lose those references.
$arr_op = array_combine(array_keys($array['operators']), array_column($array['operators'], 'type'));
//print_r($arr_op);
//get our instruction list - combine data to simplify.
//this saves us a bit of work doing the lookup and managing multiple arrays
$instructions = [];
foreach($array['links'] as $link){
$instructions[] = [
'fromOperator' => $link['fromOperator'],
'fromOp' => $arr_op[$link['fromOperator']], // we need them keys to match here
'toOperator' => $link['toOperator'],
'toOp' => $arr_op[$link['toOperator']] //and here
];
}
//print_r($instructions);
$opp_id = array_search('start',$arr_op); //9 start
//print_r($opp_id);
$i=0;
$output = [];
//loop as long as we have some instructions to process
while(count($instructions)){
//get the current key of the array
$key = key($instructions);
//is this our instruction?
//we are forced to {potentially} loop the whole array to find it
//we cannot use array search (even after flattening it out) because of the duplicates
if($instructions[$key]['fromOperator'] == $opp_id){
//get and remove the instruction - array reduction
$instruction = array_splice($instructions, $key, 1)[0];
//print_r($instruction);
//print_r($instructions);
//just for sanity checks.
echo "{$i} | from:{$instruction['fromOp']} {$instruction['toOperator']} | to:{$instruction['fromOperator']} {$instruction['toOp']}\n";
//if the last operation is end, use it as there is no fromOp = end
$opperation = 'end' == $instruction['toOp'] ? 'end' : $instruction['fromOp'];
//process instruction
switch($opperation){
case 'join':
//skip
break;
case 'split':
//split has to be done as a group
$output['split']=array_column(array_filter($array['links'],function($ins)use($opp_id){return $ins['fromOperator']==$opp_id;}),'toOperator');
break;
default: //start, page, end
$output[] = $opperation;
//when we find the end, break the switch, break the while
if($opperation == 'end') break 2;
break;
}//end switch
//get the operation key for the next instruction as we consumed it, Yum!
$opp_id = $instruction['toOperator'];
++$i;
}//end if
//move the array pointer forward, or reset on false (start over when we hit the end of the array)
if(false === next($instructions)) reset($instructions);
}//end while
echo "\n";
print_r($output);
Output
0 | from:start 2 | to:9 page
1 | from:page 1 | to:2 split
2 | from:split 4 | to:1 page
//Some "magic" happens here and we warp to 0
3 | from:page 0 | to:4 join
4 | from:join 12 | to:0 page
5 | from:page 10 | to:12 end
Array
(
[0] => start
[1] => page
[split] => Array
(
[0] => 3
[1] => 4
[2] => 5
[3] => 8
)
[2] => page
[3] => end
)
Sandbox
Limitations
Because PHP array keys are unique, your forced to group all the "split" operations into one lump. With your current input array structure there is no "real" way around it, even without "using split as a key". (hopefully this illiterates the issues)
//The #{n} in the first column, is the (apx) order this thing runs in.
//#(hashtag) will be used for all numbers with a # (in my explanation below)
//if they don't have a # and are code, assume they are the second column value (in my explanation below)
#2 - 0 | from:2 page | to:1 split //--> start of split
#3 - 1 | from:1 split | to:3 page //--> go to 3
#dup - 2 | from:1 split | to:4 page // -> go to 4
#4 - 3 | from:1 split | to:5 page // -> go to 5
#dup - 4 | from:3 page | to:0 join //<-- if you go to 0 -> 12 -> end
#5 - 5 | from:4 page | to:0 join //<-- if you go to 0 -> 12 -> end
#dup - 6 | from:5 page | to:0 join //<-- if you go to 0 -> 12 -> end
#6 - 7 | from:8 page | to:0 join
#7 - 8 | from:0 join | to:12 page
#dup - 9 | from:1 split | to:8 page //--> go to 8 end of split
#8 - 10 | from:12 page | to:10 end // {exit}
#1 - 11 | from: start | to:2 page
As you can see above, the #dup ones all come from opp:1 or $array['operators'] = [1 =>['type' => 'split']], but there is only 1 to:1 at #2 and 4 from:1 items. So we are forced to split the execution of these because you cant go to 4 items at the same time for 1 item. We can do this with a loop, or with this big huge mess I did there.
For example take #3 our last good path:
#3 - 1 | from:1 split | to:3 page //--> go to 3
~4 - 4 | from:3 page | to:0 join //<-- if you go to 0 -> 12 -> end
~5 - 8 | from:0 join | to:12 page // way out of wack now.
~6 - 10 | from:12 page | to:10 end {premature termination}
As you see we cannot follow those paths, so we are forced to process these as a group. If we did that (follow the path) we would have only processed #1, #2, #3, ~4, ~5 and ~6 and then the program ends. This is what happened in my previous answer (basically) and how I knew it wasn't correct. Also due to the duplicated 0 we can't process any of those or well wind up at the end shortly. I don't see any {other} way to solve that given the structure of the data.
How I did it (briefly)
The "trick" here was manually mapping out how it runs, above.
Using that knowledge I built an instruction list (to reduce complexity), so we are dealing with one array without the lookups for the operation names, I called that list $instructions (creative I know).
Next if($instructions[$key]['fromOperator'] == $opp_id)
TRUE: we just eat that $instruction = array_splice($instructions, $key, 1)[0]; which removes and returns a portion of the array. So our list of instructions is constantly being reduced as we are able to process them.
FALSE: we continue to the next instruction and reset the array if the array pointer is at the end. if(false === next($instructions)) reset($instructions); and start back at 1 (in this list) on the next instruction.
Because there is no "real" way to order this, we may have to loop over the array several time, this is easier to do by removing each instruction as we process it.
Then it's pretty trivial to do a switch and collect our output, with a bit of "finagling" with the "split stuff" and the end operation.
the "finagling" is just pulling an array of all the toOperator from the main array where the fromOperator has the same int as the split has for it's key (or 1 in this case)
Lastly we either run out of array items, or we hit this if($opperation == 'end') break 2; which breaks out of both the switch and the while loop. Who knew you could do that with break {this guy}...
I left a bunch of comments in there, hopeful this explains how I did it and what the main issue was.
Improvements
Where I have this monstrosity, which just gets a flat array of all the toOperator values for all items that are "Splits" (all dynamic like). I can come up with at least 5 ways to do this, this was just the way I picked.
$output['split']=array_column(array_filter($instructions,function($ins)use($opp_id){return $ins['fromOperator']==$opp_id;}),'toOperator');
You could iterate over each "split" and recursively call the switch "somehow". Because I am removing each instruction, the first "Split" gets pulled out of the instruction list before we can check if it's a split. This makes it a bit difficult to work with. However you can do this (if you need to fix that):
$instructions = array_splice($instructions, $key, 0, [instructions]);
Which should put it back in the instruction list. The magic of array_splice even if that is a bit counter productive. Then you just need to loop over them, and if they don't have a goto 0 you could follow that path where it leads. Which may solve some issues with ordering, that may or may not happen. One other issue here is you will get multiple page values in the output, as I just skip over them (pun intended). So keep that in mind if you try to iterate over it. The way I did it wasn't necessary to deal with that. But the basic idea is to functionality (make part or all of it a function and then) call it multiple times for the splits as they happen.
One other issue, that I didn't bother fixing, is that not all the split items are are actually removed from the instruction list. In this example this doesn't matter because we have the end tag. However it would be pretty easy to clean that up when doing the above improvements.
In any case I will leave that as an exercise for the reader.
Final words
PS: if you numbered it like above, it would have saved me a TON of time. It was a fun challenge though.
Hope it helps, this is about the best you can get with the data you provided due to the above issues. There may be some minor performance things to be gained etc, but that pretty much covers it.
Cheers!!
*PPS: yes I know my spelling and grammar suck. Oh the joys of dyslexia. I supose one cannot be good at everything.... It takes me about a billion edits to get it somewhat readable.

php (PDO) simple way to turn rows in lookup table into simple array

given a very simple table structure thus:
mysql> describe songpart;
+----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | MUL | NULL | auto_increment |
| partName | text | NO | | NULL | |
+----------+---------+------+-----+---------+----------------+
which results in an array like this in php (when queried)
Array ( [0] => Array ( [id] => 1 [0] => 1 [partName] => Lead Guitar [1] => Lead Guitar )
[1] => Array ( [id] => 2 [0] => 2 [partName] => Bass Guitar [1] => Bass Guitar )
[2] => Array ( [id] => 3 [0] => 3 [partName] => Drums [1] => Drums )
[3] => Array ( [id] => 4 [0] => 4 [partName] => Keyboard [1] => Keyboard ) )
Am I missing some simple trick to turn this into a simple array with id as the key like so:
Array ( [1] => Lead Guitar
[2] => Bass Guitar
[3] => Drums
[4] => Keyboard )
or is it possible to get PDO to deliver an array like this?
TiA
You can simply use the PDO::FETCH_KEY_PAIR is you have only 2 columns in your result.
You can also use the PDO::FETCH_GROUP and PDO::FETCH_ASSOC together if you have more. Example :
$result = $db->query('select * from channels')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
This will yield an array indexed with the first column containing at each index an array of the result for this key. You can fix this by using array_map('reset', $result) to get your goal.
Example :
$result = array_map('reset', $db->query('select * from channels')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC));
Try this:
$records = $pdo->query('SELECT id, partName FROM myTable');
$records->setFetchMode(PDO::FETCH_KEY_PAIR);
print_r($records->fetchAll());
For that you need to set only your desired fields in iteration.
$part_name = [];
$records = $pdo->query('SELECT * FROM your_table');
foreach ($records as $row) {
$part_name[$row['id']] = $row['partName'];
}

Optimizing multiple queries in MySQL and PHP

Questions
How should I do the query(ies) to get this results?
Should I use a different structure for database tables?
Details
I want to get results from 3 tables:
+------------------------------+-------------------+
| courses | id | <-------+
| | name | |
| | | |
+------------------------------+-------------------+ |
| sections | id | <-------|----------+
| | course_id | <- FK(courses.id) |
| | name | |
+------------------------------+-------------------| |
| resources | id | |
| | section_id | <- FK(sections.id)-+
| | name |
+------------------------------+-------------------+
I want to store results in a PHP Array like this:
Array
(
[courses] => Array
(
[id] => 1
[name] => course 1
[sections] => Array
(
[0] => Array
(
[id] => 1
[course_id] => 1
[name] => course 1 section 1
[resources] => Array
(
[0] => Array
(
[id] => 1
[section_id] => 1
[name] => resource 1
)
)
)
)
)
)
EDIT
What I did:
$cources = DB::query(Database::SELECT,
'select * from courses')->execute($db,false)[0]; // Get all courses as array
foreach($courses as &$course) {
$sections = DB::query(Database::SELECT,
'select * from sections where course_id = '.$courses['id']);
$course['sections'] = $sections;
foreach($course['sections'] as &&section) {
$resources = DB::query(...); // Get array of resources
$section['resources'] = $resources;
}
}
The database structure is normalized - this is correct and should not be changed.
However, SQL returns de-normalized or "flattened" data for an N+ join: only a set of homogenous records can be returned in a single result-set. (Some databases, like SQL Server, allow returning structure by supporting XML generation.)
To get the desired array structure in PHP will require:
Separate queries/result-sets (as shown in the post): ick!
There will about one query/object. While the theoretical bounds might be similar, the practical implementation will be much less efficient and the overhead will be much more than for single query. Remember that every query incurs (at the very least) a round-trip penalty - as such, this is not scalable although it will likely work just fine for smaller sets of data or for "time insensitive" operations.
Re-normalize the resulting structure:
This is very trivial to do with support of a "Group By" operation, as found in C#/LINQ. I am not sure how this would be approached [easily] in PHP1. This isn't perfect either, but assuming that hashing is used for the grouping, this should be able to scale fairly well - it will definitely be better than #1.
Instead of the above, consider writing the query in such a way that the "flat" result can be used within the current problem/scope, if possible. That is, analyze how the array is to be used - then write the queries around that problem. This is often a better approach that can scale very well.
1 Related to re-normalizing the data, YMMV:
SQL result to PHP multidimensional array
PHP array to multidimensional array
Group a multidimensional array by a particular value?
You can try something like this
SELECT * FROM (
select c.id, c.name from courses c
union
select s.id, r.name,s.course_ID from sections s
union
select r.id, r.name,r.section_ID from resources r
)
You cant get multi dimensional result from mysql. The query for getting the elements should be like this:
select courses.id as coursesId,courses.name as coursesName,sections.id as sectionsId,sections.name as sectionsName,resources.id as resourcesId, resources.name as resourcesName
from courses
left join sections on courses.id=sections.course_id
left join resources on sections.id=resources.section_id;
But ofcourse it will not give you the array as you like.
if you are familiar with php then you can use this code i am writing only 2nd level you can write same way with third label
$final=array();
$c=-1;
$cid=false;
$cname=false;
$query = "SELECT c.*,s.*,r.* FROM courses AS c LEFT JOIN sections AS s ON c.id=s.course_id LEFT JOIN resources AS r ON r.section_id =s.id";
$result=mysql_query($query, $this->con) or die(mysql_error());
while($row= mysql_fetch_array($result)){
if($cid!=$row[2]){
$final['cources'][++$c]['id']=$cid=$row[0];
$final['cources'][$c]['name']=$cname=$row[1];
$s=-1;
}
$final['cources'][$c]['sections'][++$s]['id']=$row[2];
$final['cources'][$c]['sections'][$s]['course_id']=$row[3];
$final['cources'][$c]['sections'][$s]['name']=$row[4];
}
echo "<pre>";
print_r($final);
echo "</pre>";
//Outpur
Array
(
[cources] => Array
(
[0] => Array
(
[id] => 1
[name] => c1
[sections] => Array
(
[0] => Array
(
[id] => 1
[course_id] => 1
[name] => s1-1
)
[1] => Array
(
[id] => 1
[course_id] => 1
[name] => s1-1
)
)
)
[1] => Array
(
[id] => 2
[name] => c2
[sections] => Array
(
[0] => Array
(
[id] => 2
[course_id] => 2
[name] => s1-2
)
)
)
)
)

Insert Array values, multiple rows

I'm trying to record some information on a database. My post looks like this:
Array
(
[Action] => 1000
[Date_Stat] => 07/02/2013
[Date_Final] => 07/02/2013
[Product_Id] => Array
(
[0] => 2
[1] => 6
[2] => 1
)
[Conversion] => Array
(
[0] => 1,20
[1] => 1,00
[2] => 2,03
)
[ValueMin] => Array
(
[0] => 2,00
[1] => 1,58
[2] => 2,70
)
[ValueMax] => Array
(
[0] => 2,50
[1] => 1,98
[2] => 2,90
)
[ValueMedio] => Array
(
[0] => 2,20
[1] => 1,68
[2] => 2,80
)
)
HOW can I insert all this on database the right way?
I'm not sure about the best way to design the tables and store the information. I'm using this to make a PRICE TABLE with starting date, final date and list all products with prices.
Also I'm thinking what is the best method. There are 2 possibilities I think about
Date_Start | Date_End |Product_Id | ValueMin | ValueMax | ValueMedio | Conversion
02-02-2013 02-03-2013 1 1.00 2.00 3.00 4.00
02-02-2013 02-03-2013 2 1.00 2.00 3.00 4.20
02-02-2013 02-03-2013 3 1.00 2.00 2.00 4.40
OR (using implode and putting all values on the same row)
Date_Start | Date_End |Product_Id | ValueMin | ValueMax | ValueMedio | Conversion
02-02-2013 02-03-2013 1,2,3 1,1,1 2, 2,2 3,3,2 4, 4.3, 4.4
Thanks a lot!
Choose the option mentioned first. Selecting Rows will become much easier if you do it that way.
To insert the records, use a simple prepared Statement (PHP Manual) and use a for-Loop.
I'd suggest a little tweak over the first option. If you create a separate table (Something along the lines of "Prices") like this:
id|DateStart|DateEnd|
1|02-02-2013|02-02-2013|
And then, in the table you suggested, you'd replace the date range for the PriceId (Foreign Key to this table). That way it'll be easier for you to maintain the consistency over changes on date ranges.

Categories