ok, a bit of background,
just into codeigniter
not a fan of sql and server-side scripts
i know what joins are
i have a many-to-many database for the first time
it's because joins typically have the following example as a result. but i wanted to parse this without having to build code to ignore repetitions. it's a 3-table join sample. the issue of repeating values increases as i join more tables:
table1.authorid table1.authorname table2.books table3.favorited
1 john john's book 1 jean
1 john john's book 1 joe
1 john john's book 2 ken
1 john john's book 2 mark
2 mark mark's book 1 alice
2 mark mark's book 1 ted
2 mark mark's book 2 sarah
2 mark mark's book 2 denise
is there a way in codeigniter (or plain PHP) that i can get this array form and turn it into something like json (and parse it like json)
$result = [
{
'authorid':1,
'authorname':'john',
'books':['john's book1','john's book2'],
'favorited':['jean','joe','ken','mark']
},
{
'authorid':2,
'authorname':'mark',
'books':['mark's book1','mark's book2'],
'favorited':['alice','ted','sarah','denise']
}
]
Update: this is not limited to this depth of objects/arrays (like in the example). it can go deeper (arrays in arrays, arrays in objects, objects in arrays, objects in objects)
// first, we need the SQL results in the $result_array variable
$sql = 'SELECT ...'; // your SQL command
$result_array = $this->db->query($sql)->result_array(); // codeigniter code
// here the real answer begins
$result = array();
foreach ($result_array as $row)
{
if (!isset($result[$row['authorid']])
{
$author = new StdClass();
$author->authorid = $row['authorid'];
$author->authorname = $row['authorname'];
$author->books = array($row['books']);
$author->favorited = array($row['favorited']);
$result[$row['authorid']] = $author;
}
else
{
if (!in_array($row['books'], $result[$row['authorid']]->books))
{
$result[$row['authorid']]->books[] = $row['books'];
}
if (!in_array($row['favorited'], $result[$row['authorid']]->favorited))
{
$result[$row['authorid']]->favorited[] = $row['favorited'];
}
}
}
$result = array_values($result);
echo json_encode($result);
Related
I have this code that will perform an insert to the question, choice and multichoice table. I managed to accomplish in inserting question and choice table, what I failed to do is the multichoice table.
require("dbOption/Db.class.php");
$question = new Db();
$choice = new Db();
$multichoice = new Db();
$entries = array(
0 => '1. What foo?',
1 => 'a. foo1',
2 => 'b. foo2',
3 => 'c. foo3',
4 => 'd. foo4',
5 => '2. 2 + 2 is?',
6 => 'a. test1',
7 => 'b. test2',
8 => 'c. test3',
9 => 'd. test4',
);
$answerIDs = "";
$questionID = "";
$multipleChoice = array();
foreach ($entries as $entry) {
if(is_numeric(substr($entry, 0, 1)) === true) {
echo "<pre>";
var_dump($entry);
echo "<pre>";
$question->query("INSERT INTO question(q_name) VALUES(:question)",array("question"=>$entry));
$questionID = $question->lastInsertId();
} else {
echo "<pre>";
var_dump($entry);
echo "<pre>";
$answer->query("INSERT INTO choice(choices,question) VALUES(:choices, :question)",array("choices"=>$entry, "question"=>$questionID));
if ($answerIDs === "")
$answerIDs = $choice->lastInsertId();
else
// store last inserted ids in choice table and separate it with ","
$answerIDs .= ("," . $choice->lastInsertId());
}
}
This is the sample output in the db.
question table
id q_name
1 1. What foo?
2 2. 2 + 2 is?
choice table
id choices question correct
1 a. foo1 1 0
2 b. foo2 1 0
3 c. foo3 1 0
4 d. foo4 1 0
5 a. test1 2 0
6 b. test2 2 0
7 c. test3 2 0
8 d. test4 2 0
question in choice table is the id from question table
What I want to achieve in multichoice table.
multichoice table
id question mc_answers
1 1 1,2,3,4
2 2 5,6,7,8
question in multichoice table is the id from question table
I’m confused on how to do this, I would like to consult it to you guys. What should I do to achieve this?
Like I said, I don't think you should store those lists at all. Storing comma separated values is considered bad design in general, and besides you already have all the information you need in the database, so that information would be redundant too.
If you want to do this anyway, I think there are four solutions.
1) Insert the answers when you start a new question. Disadvantage is that you have to do the same thing again after the loop to save the choices of the last question. Structure of the code would look like this (stripped down for brevity).
$answerIDs = "";
foreach ($entries as $entry) {
if(is_numeric(substr($entry, 0, 1)) === true) {
if ($answerIDs !== "") {
// First insert multi select answers
// Reset the answers for the new question.
$answerIDs = "";
}
// Then insert question as you do now.
} else {
// Insert answers and store ID in $answerIDs as you do now.
}
}
if ($answerIDs !== "") {
// Store answers (again).
}
2) Keep IDs in a big array, and store those afterwards.
You can save the answers in a nested array. The key of the main level is the question ID. Per question you store an array (or a string, but I think array is easier) of answers. After inserting the questions and answers, you can loop through the array and insert all the multiple choices.
This should work fine, unless you've got millions of questions. Eventually $allAnswerIDs might become too big for memory, but I don't think it will be a problem soon in this scenario.
$allAnswerIDs = array();
foreach ($entries as $entry) {
if(is_numeric(substr($entry, 0, 1)) === true) {
// Insert question as you do now.
// Store the question ID in the array.
$allAnswerIDs[$questionId] = array();
} else {
// Insert answer.
// Store ID in $answerIDs as an array (or as a string, if you like).
$allAnswerIDs[$questionID][] = $answerID;
}
}
foreach ($allAnswerIDs as $questionID => $questionAnswerIDs) {
// Build a string of the answers per question.
$answerIDs = implode(',', $questionAnswerIDs);
// Insert the multiple choice row using $questionID and $answerIDs.
}
3) Use a bulk insert after the loop.
You can write a single, slightly more complex SQL statement that inserts all of them at once, like:
INSERT INTO multichoice(question, mc_answers)
SELECT
question,
GROUP_CONCAT(choices) as AnswerIDs
FROM
Choice
You can execute this statement once at the end of your script, and it will make the database generate the entire content for the multichoice table at once.
4) Make a view.
As you can see, the (relatively simple) select in method 3 shows all the information you want to have in the multichoice table, so instead of inserting it in the table, you may consider to make it a view instead in the database. That way, you don't have to store the information at all. Your PHP code will be easier, your database cleaner, and you can query from the view as you would from the table.
I'd like to SELECT rows from a database table and group them using PHP instead of SQL based on a parameter (in this case by item).
SQL:
Clothes table
id item owner
1 shoes joe
2 pants joe
3 hat joe
4 pants joe
5 hat tom
SELECT * from Clothes where owner='joe'
1 shoes joe
2 pants joe
3 hat joe
4 pants joe
Here's how I'd like the results to look after using PHP instead of SQL's GROUP BY item
PHP :
1 shoes joe
2 pants joe //count 2
3 hat joe
I'm sure there is a PHP array function for this I'm just not familiar, thoughts?
The easiest way is to exploit the uniqueness of array keys:
$grouped = array();
while ($row = $db->fetchResult()) { // or however you get your data
if (isset($grouped[$row['item']])) {
$grouped[$row['item']]['count']++;
} else {
$grouped[$row['item']] = $row + array('count' => 1);
}
}
Using pseucode for the database access functions, I believe this should work:
$sql = "SELECT * from Clothes where owner='joe'";
$res = query($sql);
$arr = array();
while ($row = $res->fetch())
{
$arr[] = $row['item'];
}
$arr = array_unique($arr);
You should note that this might give you a "sparse array" (in other words, there may be gaps in the keys). And as said in the comments, it's usually better to do this in SQL if you have that option. Even if that means executing two similar queries.
function group($items, $field) {
$return = array();
foreach ($items as $item) {
$key = $item[$field];
if (isset($return[$key])) {
$return[$key]['count']++;
} else {
$return[$key] = $item;
$return[$key]['count'] = 1;
}
}
return $return;
}
print_r(group($results, "item"));
Say I have a query the following query run:
Edit
Added order clause because the real sql statement has one.
SELECT description, amount, id FROM table ORDER BY id
In this instance, the ID is not unique to the dataset. It would return something like this.
Description Amount ID
----------- ------ --
1 Hats 45 1
2 Pants 16 1
3 Shoes 3 1
4 Dogs 5 2
5 Cats 6 2
6 Waffles 99 3
What I need to do is enclose each section of IDs in it's own div (So rows 1,2,3 in one div, 4,5 in another div and 6 in it's own div).
There are tons of solutions to this but I just can't think of one that isn't overly complicated.
I want to be able to keep the SQL how it is and somehow sort the data set in PHP so that I can loop through each section of the dataset while looping through the dataset as a whole.
Some kind of array would work but the structure of it is stumping me.
How can I get this to work? PHP solutions would be idea but theoretical will help too.
See if something like this works for you.
// execute query: Select description, amount, id from table
$results = array();
while ($row = $query_result->fetch_array()) {
if (!isset($results[$row['id']])) $results[$row['id']] = array();
$results[$row['id']][] = $row; // push $row to results for this id
}
// later on
foreach($results as $id => $data) {
// output div based on $id
foreach($data as $datum) {
// output individual data item that belongs to $id
}
}
A simple serial solution might look something like this:
$curId = ''; // track working id
$firstDiv = true; // track if inside first div
// open first div
echo '<div>';
// foreach $row
{
// when id changes, transition to new div, except when in first div
if ($row->$id != $curId) {
if ($firstDiv) {
$firstDiv = false;
} else {
// start new div
echo '</div>';
echo '<div>';
}
$curId = $row->$id; // track new current id
}
// display contents of current row
}
// close last div
echo '</div>';
Just store the id in temp variable, if the next one is different close the div and open new div
Assuming associative arrays for the db results:
$final = array();
foreach($results as $result)
{
$final[$result['id']][] = $result;
}
This leaves you with an associative array $final that groups the entries by ID
I've got a data schema like this:
cp
id te su
1 7 2
2 7 1
3 6 8
cl
id cp st
1 2 5
2 2 6
3 1 6
us
id na
.. ..
5 Joe
6 Mike
7 Tina
.. ..
I want to run a php function foo($te) with foo(7) to output data like this from mySQL
[0]
su: 1
st_list:
[0]
id:6
na:Mike
[1]
su: 2
st_list:
[0]
id:5
na:Joe
[1]
id:6
na:Mike
I want to know how best to do this. Right now I'm able to use JOINs, but the result looks like this
[0]
su: 1
st_id:6
st_na:Mike
[1]
su: 2
st_id:5
st_na:Joe
[3]
su: 2
id:6
na:Mike
The su:2 is repeated... obviously that isn't a problem in this example, the problem is that in the real thing that su represents a lot more data, that i'll be getting through "this" (whatever answer I choose) method.
EDIT:
I hope you understand what I'm asking... I know a lot of you guys are way more knowledgable in mySQL and development in general than me, so that's why i'm asking in such a plain (I HAVE THS----> WANT THIS) way, because I think if I asked any more specifically I would end up making assumptions about the way it should run). I want an effecient solution, because this data is being used to populate search results.
Thanks!!!!!!!!!!
You will need to loop over the results yourself and build the array in the format you want. MySQL can't give you the results in the nested format you want. Here's a rough draft (untested):
$su = 0;
$st_list = array();
$nested = array();
foreach ($results as $row) {
if ($su != 0 && $row['su'] != $su) {
$nested[] = compact('su', 'st_list');
}
if ($su != $row['su']) {
$su = $row['su'];
$st_list = array();
}
$st_list[] = array(
'id' => $row['st_id'],
'na' => $row['st_na'],
);
}
$nested[] = compact('su', 'st_list');
Edit: Usually it's best to simply fetch all the results you want using a single SQL query. That is almost always the most efficient way to do it. But if it really matters to you (and this is a performance critical part of your application), the only way to know for sure is benchmark both methods. A lot depends on your database layout, number of rows, SQL indexes, etcetera.
SELECT distinct us.id,na FROM cp
LEFT JOIN cl ON cp.su = cl.cp
LEFT JOIN us ON us.id = cl.st
where te = "7"
Does this provide the results you are looking for?
Alternately,
SELECT su,us.id,na FROM cp
LEFT JOIN cl ON cp.su = cl.cp
LEFT JOIN us ON us.id = cl.st
where te = "7"
ORDER BY su
Is closer to the formatting you asked for in your post.
In your model, you can have something like these too...
function get_te($te)
{
$qry = $this->db->get_where('cp', array('te' => $te));
$raw_te = $qry->result_array();
$te = array();
$i = 0;
while($i < count($raw_te))
{
$te[$i]['su'] = $raw_te[$i]['su'];
$qry = $this->db->select('*')
->from('cl')
->join('us', 'us.id = cl.id')
->where('cp', $raw_te[$i]['id'])
->get();
$te[$i]['st_list'] = $qry->result_array();
$i++;
}
return $te;
}
I am going to select a list of items from a table, and pass it using json-framework.
Example, I want to select friends from my "shirts" table, from "players_shirts" table
pid | sid
================
1 | 2
2 | 3
1 | 5
Lets say, I get 30++ result (rows).
I assume (not yet tested this code), in php, I assign it by:
$array;
$count = 0;
while($r = mysql_fetch_assoc($exe){
$array[$count] = $r['sid'];
// EDIT START: I forgot to add counter
$count++;
// EDIT END
}
echo json_encode($array)
Is this method efficient/good enough?
I am new to php/database/manipulating data from database.
There is no need to specify an array keys in your case, so your code could be rewritten as:
$array = array();
while($r = mysql_fetch_assoc($exe){
$array[] = $r['sid'];
// or you may use array_push($array, $r['sid']); instead of the line above.
}