I'm working on programming a gradebook, and I've ran into a bit of an issue I'm trying to figure out how to approach.
I have three tables which are at play in this script: (1) the "assignments" table which contains info about each assignment, (2) the "assignGrades" table which contains student scores on assignments (3) the "students" table which contains student information.
Now, the problem is coming whenever I add a new student to the class. Logically, if a student joins a class mid-semester, they would not be assigned "past work" from earlier in the year. With this in mind, there would be no "connection" for an INNER JOIN statement. I have already tried using "LEFT JOIN" and "RIGHT JOIN" in this instance, but I am not having any luck.
Whenever I go to build a PHP array with the SQL statement below, I am running into a problem. If a student was added mid year, they are not properly alphabetizing into the array, which comes from the SELECT statement and the way this is organized. See the example below for student "Amy Appleton" which was added mid year, and is not in proper alphabetical order.
HOW I NEED HELP / DESIRED END RESULT:
I am trying to determine how to alphabetize my $array to be organized in order of last name. I have determined I will either accomplish this through rewriting my SQL statement or through using some sort of PHP usort, although I would much rather organize data on the SQL side if possible. My best guess would be to accomplish this through a LEFT JOIN, but I have tried every variation possible within my SQL statement and have not gotten any desired results.
SQL statement used:
SELECT students.firstName, students.lastName, assignments.assID, assignments.assEmoji, assignments.points, assignments.title, assignments.assigned, assignments.due, assignGrades.*
FROM students
LEFT JOIN assignGrades ON students.usid = assignGrades.usid
LEFT JOIN assignments ON assignGrades.assID = assignments.assID
WHERE subID=? OR subID IS NULL ORDER BY due, lastName, firstName
Snipet from PHP that is building the $array
while ($row = mysqli_fetch_assoc($results)) {
$array['assignments'][$row['assID']]['assEmoji'] = $row['assEmoji'];
$array['assignments'][$row['assID']]['title'] = $row['title'];
$array['assignments'][$row['assID']]['points'] = $row['points'];
$array['assignments'][$row['assID']]['assigned'] = $row['assigned'];
$array['assignments'][$row['assID']]['due'] = $row['due'];
$array['students'][$row['usid']]['firstName'] = $row['firstName'];
$array['students'][$row['usid']]['lastName'] = $row['lastName'];
$array['students'][$row['usid']]['fullName'] = $row['firstName']." ". $row['lastName'];
if ($row['status'] == 'graded' || $row['status'] == 'missing') {
$array['students'][$row['usid']]['earned'] = $array['students'][$row['usid']]['earned'] + $row['score'];
$array['students'][$row['usid']]['maxpts'] = $array['students'][$row['usid']]['maxpts'] + $row['points'];
}
$array['students'][$row['usid']]['submissions'][$row['assID']]['workID'] = $row['workID'];
$array['students'][$row['usid']]['submissions'][$row['assID']]['status'] = $row['status'];
if (isset($row['submitted'])) {$array['students'][$row['usid']]['submissions'][$row['assID']]['submitted'] = $row['submitted'];}
if (isset($row['method'])) {$array['students'][$row['usid']]['submissions'][$row['assID']]['method'] = $row['method'];}
if (isset($row['score'])) {$array['students'][$row['usid']]['submissions'][$row['assID']]['score'] = $row['score'];}
if (isset($row['score'])) {$array['students'][$row['usid']]['submissions'][$row['assID']]['points'] = $row['points'];}
if (isset($row['graded'])) {$array['students'][$row['usid']]['submissions'][$row['assID']]['graded'] = $row['graded'];}
if (isset($row['method'])) {$array['students'][$row['usid']]['submissions'][$row['assID']]['method'] = $row['method'];}
}
return $array;
Example print_r($array) from the SQL statement (Amy Appleton should appear on the top of $array['students'] since she is alphabetically before the other two students. However, since there is no entry on the assignGrades table for assignment #9 for her, she appears at the bottom)
Array
(
[assignments] => Array
(
[9] => Array
(
[assEmoji] => ✏️
[title] => Beginning of Year Activities
[points] => 10
[assigned] => 2021-08-16
[due] => 2021-08-20 15:00:00
)
[10] => Array
(
[assEmoji] => ✏️
[title] => Mid Year Project
[points] => 10
[assigned] => 2021-09-23
[due] => 2021-09-30 15:00:00
)
)
[students] => Array
(
[11] => Array
(
[firstName] => Zeke
[lastName] => Lee
[fullName] => Zeke Lee
[earned] => 103
[maxpts] => 120
[submissions] => Array
(
[9] => Array
(
[workID] => 539
[status] => graded
[submitted] => 2021-08-17 08:15:48
[method] => wall
[score] => 9
[points] => 10
[graded] => 2021-09-22 10:26:54
)
[10] => Array
(
[workID] => 541
[status] => graded
[submitted] => 2021-09-23 08:15:48
[method] => wall
[score] => 9
[points] => 10
[graded] => 2021-09-23 10:26:54
)
)
)
[12] => Array
(
[firstName] => John
[lastName] => Smith
[fullName] => John Smith
[earned] => 91
[maxpts] => 110
[submissions] => Array
(
[9] => Array
(
[workID] => 540
[status] => graded
[submitted] => 2021-08-17 08:45:48
[method] => wall
[score] => 7
[points] => 10
[graded] => 2021-09-22 10:28:54
)
[10] => Array
(
[workID] => 590
[status] => graded
[submitted] => 2021-09-23 09:15:48
[method] => wall
[score] => 9
[points] => 10
[graded] => 2021-09-23 11:24:54
)
)
)
[13] => Array
(
[firstName] => Amy
[lastName] => Appleton
[fullName] => Amy Appleton
[earned] => 91
[maxpts] => 110
[submissions] => Array
(
[10] => Array
(
[workID] => 913
[status] => graded
[submitted] => 2021-09-23 10:45:48
[method] => wall
[score] => 7
[points] => 10
[graded] => 2021-09-23 12:31:54
)
)
)
)
)
Array structure:
$array['assignments'][assID][details]
$array['students'][usid][details]
$array['students'][usid]['submissions'][assID][details]
Screenshots of table structure from SQL
Screenshot of CSV dump from SQL query
The main issue I see is that your query is trying to do too much. It is trying to get assignments in a specific order (by due), as well as students and their submissions in a different order (by lastName, firstName). For this reason, I would break this up into 2 separate queries. You can combine the datasets later to make your final data structure.
retrieve the assignments and order them by due date. This is a very simple query and the result set can be used directly.
retrieve all students and their submissions, if they exist, and order them by lastName, first name. Then, in PHP, manipulate the dataset into the format you need.
Pseudocode
// Get assignments. These can be used directly in your data structure `$combined`.
//
// Order by due date
$assignments = SELECT ... FROM assignments
WHERE ...
ORDER BY due;
// Get students and their assignments. These need to be manipulated to fit your
// requirements. Use a left join here because a student may not have started
// an assignment yet.
//
// Order by lastName, firstName
$students = SELECT ... FROM students a
LEFT JOIN assignGrades b ON b.usid = a.usid
ORDER BY lastName, firstName;
// See http://sandbox.onlinephpfunctions.com/code/9460c51954f8ea00f7ba75adadfcbf91fc03076e
// Loop over the student/submission dataset and manipulate it.
$studentMapping = [];
foreach($students as $row) {
// Build up student data if it doesn't already exist.
// Importantly, create an empty submissions element. If the student's
// submission doesn't exist, this is what will be returned.
$student = $studentMapping[$row['usid']] ?? [];
if (empty($student)) {
$student = [
'firstName' => $row['lastName'],
'lastName' => $row['lastName'],
...
'submissions' => [],
];
}
// Build up submissions data for the student if they have submitted an assignment.
// If they haven't submitted an assignment yet, their submissions array will remain empty.
if (!empty($row['assignment_id'])) {
$student['submissions'][] = [
'assignmentId' => $row['assignment_id'],
'status' => $row['status'],
...
];
}
// Finally, assign the student (and their submissions) to the $studentMapping array.
$studentMapping[$row['usid']] = $student;
}
// Now combine them into your final datastructure.
$combined = [
'assignments' => $assignments,
'students' => $studentMapping,
];
Related
I'm parsing a json file to add data to a sqlite database. It works fine apart from the fact that some entries I retrieve from the json file are just not added completely. As an example see this array I got from the json file:
(
[0] => Array
(
[Id] => 4215717
[Guid] => a2fd45b0-c602-4c82-bb30-19aba8bfacdf
[IsRawData] =>
[Name] => test 1
[State] => Released
[Created] => 2015-10-25T10:21:13Z
[CreatorId] => 65edfb59-8087-4f20-81d8-1dc7b94c5624
[Modified] => 2020-02-19T13:08:07Z
[ModifierId] => 65edfb59-8087-4f20-81d8-1dc7b94c5624
[EntryId] => 377794
[LanguageId] => Array
(
[PrimaryLangId] => 7
[SubLangId] => 1
)
)
[1] => Array
(
[Id] => 4215718
[Guid] => 4609ace8-8e6f-457d-a0ae-faa29950cdcb
[IsRawData] =>
[Name] => test 2
[State] => Released
[Created] => 2015-10-25T10:21:41Z
[CreatorId] => 65edfb59-8087-4f20-81d8-1dc7b94c5624
[Modified] => 2017-01-18T13:30:05Z
[ModifierId] => 65edfb59-8087-4f20-81d8-1dc7b94c5624
[EntryId] => 377794
[LanguageId] => Array
(
[PrimaryLangId] => 9
[SubLangId] => 1
)
)
)
For some entries I add to the db using the code below, I randomly only have the first entry from the array written to the db and I can't figure out a pattern for which entries it worked and for which ones it didn't.
Is there something wrong with my code? Or do you see any issues that could cause this behaviour?
// Prepare statement to propagate terms table
$termStmt = $o_db->prepare("INSERT INTO terms
(entryID, termID, term, status, wordclass, usage, type,
primaryLangID, secondaryLangID )
VALUES (:entryID, :termID, :term, :status, :wordclass, :usages, :types,
:primaryLangID, :secondaryLangID)");
// Bind term parameters
$termStmt->bindParam(':entryID', $i_entryID);
$termStmt->bindParam(':termID', $i_termID);
$termStmt->bindParam(':term', $s_term);
$termStmt->bindParam(':status', $s_status);
$termStmt->bindParam(':wordclass', $s_wordclass);
$termStmt->bindParam(':usages', $s_usage);
$termStmt->bindParam(':types', $s_type);
$termStmt->bindParam(':project', $s_project);
$termStmt->bindParam(':primaryLangID', $i_primaryLangID);
$termStmt->bindParam(':secondaryLangID', $i_secondaryLangID);
foreach ($a_entryData as $entry){
$entryID = $entry['entryID']; // entryID comes from another table in the db to get the full entry
$fullEntry = getEntry($entryID); //get entry from json
foreach ($fullEntry as $term){
foreach($term as $termEntry) {
$i_entryID = $termEntry['EntryId'];
$i_termID = $termEntry['Id'];
$s_term =$termEntry['Name'];
$s_status =$termEntry['State'];
$i_primaryLangID = $termEntry['LanguageId']['PrimaryLangId'];
$i_secondaryLangID = $termEntry['LanguageId']['SubLangId'];
// Now get the term properties and the corresponding values
$a_properties = getTermProperties($i_termID);
$a_propertyValues = propertyValues($a_properties);
$a_properties = getTermProperties($i_termID);
$a_values = propertyValues($a_properties);
if (isset($a_values['Wortklasse'])){
$s_wordclass = $a_values['Wortklasse'];
}else{
$s_wordclass = "N\A";
}
if (isset($a_values['Verwendung'])){
$s_usage = $a_values['Verwendung'];
}else{
$s_usage = "N\A";
}
if (isset($a_values['Benennungstyp'])){
$s_type = $a_values['Benennungstyp'];
}else{
$s_type = "N\A";
}
// Execute term statement and add data to the table
$termStmt->execute();
}
}
}
Thanks for your help :)
I am working on a chat.
I have an array that contains the info regarding the room
(in the example below, 2 rooms (the lounge, the beach)).
I have an issue when I have an empty room (in the example the beach), as it contains by default no user, (which is a Null user).
$roomNusers=Array (
[The Lounge] =>
Array ( [id] => 1
[privacy] => public
[users] => Array
[QUICHE POIREAU] => Array
[smiley] => smi_smile.gif
[name] => QUICHE POIREAU
[state] => NULL
[id] => 1 )
[JOHN DOE] => Array
[smiley] => smi_smile.gif
[name] => Joe FRANC
[state] =>
[id] => 40 )
[The Beach] => Array
[id] => 2
[privacy] => public
[users] => Array
[Null] => Array
[smiley] => Null
[name] => Null
[state] => Null
[id] => Null
I am trying to count, in my array, the number of users currently present in the room.
Looking around Stack Overflow, I managed to find the solution I wanted:
foreach($roomNusers as $room => $value)
{
echo $room.' user count:'.count($room['users'])
}
This output:
The lounge user count: 2
The beach user count: 1
My issue is that it counts the user [null] in the beach.
How can I count the number of users not null per room?
I thought of a workaround with something similar to:
$countperroom= .count($room['users'])-1;
if(isset(end([$room]['users']))){$countuser+1;}
In this, the last user is empty, I do not add an user, but I do not know how to write this.
Rather than counting the number of values in $room['users'], you could count the number of keys after filtering them to remove empty keys:
foreach ($rooms as $name => $room) {
$users = count(array_filter(array_keys($room['users'])));
echo "$name: $users users\n";
}
Output (for your sample data):
The Lounge: 2 users
The Beach: 0 users
Demo on 3v4l.org
I have 4 lectures where each lecture has it's own name:
jbc2014_lezingen
Then for each lecture there will be questions, in this case 2 for each lecture:
jbc2014_vragen
I use this query:
$query = "SELECT * FROM jbc2014_lezingen
INNER JOIN jbc2014_vragen
ON jbc2014_lezingen.id=jbc2014_vragen.lezing_id";
This results in something like:
[0] => Array
(
[id] => 1
[lezing_naam] => lezing 1
[lezing_id] => 1
[vraag] => foobar?
)
[1] => Array
(
[id] => 2
[lezing_naam] => lezing 1
[lezing_id] => 1
[vraag] => foobar?
)
etc.
So the array size is based on the amount of the questions, not the amount of the lectures.
I would like something like:
[0] => Array
(
[id] => 1
[lezing_naam] => lezing 1
[lezing_id] => 1
[questions] Array (
[0] => [vraag] => foobar?
[1] => [vraag] => foobar?
)
)
Where the questions are in an array (vraag means question).
How can this be done?
Later on I need the multiple choice answers in an array inside the questions as well. But I think that won't be hard after having this one fixed.
The closest thing you could do is something like this -
SELECT jbc2014_lezingen.id as lezing_id,
GROUP_CONCAT(vraag) as vraags
FROM jbc2014_lezingen
INNER JOIN jbc2014_vragen
ON jbc2014_lezingen.id=jbc2014_vragen.lezing_i
GROUP BY jbc2014_lezingen.id
and then just get the values with
explode(",",$row['vraags']);
or create another array with something like this
foreach($raw_result as $key => $value){
$new_array[$key]['lezing_id'] = $value['lezing_id'];
$new_array[$key]['vraags'] = explode(",",$value['vraags']);
}
I'm trying to build a multi array with this structure:
Array
(
[2] => Array //this is the user's unique ID
(
[name] => Jack
[location] => Somerville, Massachusetts, United States
[cars] => Array
(
[10] => Toyota //this is the car's unique ID
[11] => Porsche
)
)
[5] => Array
(
[name] => Luke
[location] => Schwelm, North Rhine-Westphalia, Germany
[cars] => Array
(
[1] => Honda
[2] => VW
[5] => Maserati
)
)
[1] => Array
(
[name] => Jabba
[location] => Denver, Colorado, United States
[cars] => Array
(
[3] => Tesla
)
)
)
I am using this foreach loop but am stuck in getting the cars array embedded within the search data array.
Each user may have more than one car so I would need to loop through all cars for each user, generate that array, and put it in the original foreach loop.
$search_data = array();
foreach ($query->result() as $row) {
$search_data[$row->id] = array(
'name' => $row->name,
'location' => $row->location,
'cars' => array($row->car_id), //this is where I need to insert another array
);
}
return $search_data;
Any suggestions how to do this?
Thanks for helping!
SAMPLE TABLE DATA
USER NAME LOCATION CARS
2 JACK A TOYOTA
2 JACK A PORSCHE
5 LUKE B HONDA
5 LUKE B VW
5 LUKE B MASERATI
1 JABBA C TESLA
It seems that you are creating the array through a database table. So can you please give 2 or more sample data. it'll be easier to give an answer if sample data is there.
Edit:
Well this might not be the best code but I think you'll be able to figure out a better way after seeing this
$id = $row['id'];
if (!isset($search_data[$id])){
$search_data[$id] = array();
}
$search_data[$id]['name'] = $row['name'];
$search_data[$id]['location'] = $row['location'];
if (isset($search_data[$id]['cars'])) {
array_push($search_data[$id]['cars'],$row['cars']);
}else{
$search_data[$id]['cars'] = array($row['cars']); //this is where I need to insert another array
}
Firstly, sorry about the long question... but this is doing my head in. Any help would be greatfully accepted.
I've written the following function to return data from a mysql database:
function injuryTable()
{
# get all players that are injured and their injuires...
$sql = "SELECT players.id, players.injury_id, players.pname, injuries_date.name, start_date, end_date
FROM players INNER JOIN injuries_date
ON injury_id = injuries_date.id";
$result = sqlQuery($sql);
return $result;
}
The sqlQuery function is as follows:
function sqlQuery($sql)
{
$products = array();
$link = dbConnect('localhost', 'root', '', 'umarrr');
$result = mysqli_query($link, $sql);
while ($row = mysqli_fetch_array($result))
{
$products[] = $row;
}
# return each row:
return $products;
#mysqli_close($link);
}
It's all connected to the database, and everything works fine.
However when I try to iterate through the results: it only returns one row:
$injury_table = injuryTable();
// make it more readable:
foreach ($injury_table as $table);
{
echo $table['pname'];
echo $table['name'];
echo $table['start_date'];
echo $table['end_date'];
}
The sql statement I wrote above works perfectly in mysql query browser, so does anyone know what the problem here might be?
Output of print_r($injury_table)
Array ( [0] => Array ( [0] => 1 [id] => 1 [1] => 6 [injury_id] => 6 [2] => person [pname] => person [3] => wrist [name] => wrist [4] => 2008-11-21 [start_date] => 2008-11-21 [5] => 2010-11-11 [end_date] => 2010-11-11 ) [1] => Array ( [0] => 2 [id] => 2 [1] => 5 [injury_id] => 5 [2] => woman [pname] => woman [3] => neck [name] => neck [4] => 2009-11-12 [start_date] => 2009-11-12 [5] => 2010-09-09 [end_date] => 2010-09-09 ) [2] => Array ( [0] => 3 [id] => 3 [1] => 4 [injury_id] => 4 [2] => girl [pname] => girl [3] => groin [name] => groin [4] => 2010-11-27 [start_date] => 2010-11-27 [5] => 2010-12-01 [end_date] => 2010-12-01 ) [3] => Array ( [0] => 4 [id] => 4 [1] => 1 [injury_id] => 1 [2] => boy [pname] => boy [3] => achilles [name] => achilles [4] => 2010-02-01 [start_date] => 2010-02-01 [5] => 2010-03-23 [end_date] => 2010-03-23 ) [4] => Array ( [0] => 5 [id] => 5 [1] => 2 [injury_id] => 2 [2] => man [pname] => man [3] => toe [name] => toe [4] => 2010-01-01 [start_date] => 2010-01-01 [5] => 2010-02-02 [end_date] => 2010-02-02 ) )
Some things to check:
Check the return value of mysqli_query(). You assume the query succeeds, and there's far too many reasons for a query to fail to NOT check each time. It will return boolean FALSE on failure.
If you're doing many queries in your script, you'll be opening a new connection handle for each. By default these will NOT be persistent connections, so any transactions you may start will be automatically rolled back after the query results are consumed. Generally it's better to connect to the database ONCE (you can store the handle in a static variable inside your sqlQUery() function if you'd like and reuse it. There aren't many situations where you'd want (or need) multiple handles at the same time, or a brand new sparkling clean handle each time.
Have you tried doing a print_r()/var_dump() of the rows as they're retrieved in the while() loop? Tried spitting out a mysqli_num_rows() to see how many the PHP version of the query is returning?
Are you viewing the results in a browser? Perhaps something in the first or second result rows has an HTML tag (or something that your browser's interpreting) as an HTML tag and is "hiding" the output. View the page's source to make sure they're really not being output, and not just being hidden.
And finally, on the security front, it is very poor practice to allow a web-facing application to connect to the database as 'root'. Any flaws in your code will leave the DB server wide open for anyone to play in. Create an account with the least amount of privileges necessary to get the job done (probably insert/update/select/delete only) and use that instead of the root account. Even if you're just playing around with this on a private server, it's a good habit to get into.
Why are you opening a new connection to the database for each call? This is a very inefficient way of executing queries. I would pass the database connection as a parameter, or since you are using mysqli, just pass the $link as a parameter.
As to why your code is not working, I do not know, but you can try some basic error reporting with the following:
$result = mysqli_query($link, $sql) or
trigger_error('Query Failed: ' . mysqli_error($link));
I would also add the MYSQL_ASSOC to the fetching function, as you are not using the index-based array, this will make your script that much more efficient.
Hope this helps you out.
Really bad (and maybe insulting) answer...
foreach ($injury_table as $table);
{
echo $table['pname'];
echo $table['name'];
echo $table['start_date'];
echo $table['end_date'];
echo '<br/>'; // HTML new line
}
Or
foreach ($injury_table as $table);
{
echo $table['pname'];
echo $table['name'];
echo $table['start_date'];
echo $table['end_date'];
echo "\n"; // Console new line
}