Treat double loop from sql query? - php

I'm currently learning php/mysql, and I have a project.
I have 3 main SQL tables:
animations contains names, dates, adresses, etc
animateurs ('workers') is a list of people with many data
crosstable is a many-to-many relationship between the two other tables. Each line contains an "animationId", an "animateurId" and a status (is he/she interested, free, or not?).
many-to-many SQL table
I'm trying to make a web table showing all the animations, with one columns containing the animators. I would like to sort them based on their status. This is what I managed to do so far:
my results table atm
(I'm still pretty proud, having started learning php and SQL 1 month ago and not being a programmer... but whatever).
At first, I tried to run multiple while loops with $rowDispos but it doesn't seem to reset.
I think I might run one loop and create different arrays for the status of each person for each animation but I'm not sure how to do this.
My objective is to have another list of people below "Disponibles :", labeled "If necessary :".
At some point, I'll need to gather data for all the people participating to one event (to send them an email for example).
Above all I'm not sure what would be the best way to achieve what I'm trying to do.
Thank you!
$sql = "SELECT * FROM animations LEFT JOIN crosstable ON animations.idAnimation = crosstable.idAnimation LEFT JOIN animateurs ON crosstable.idAnimateur = animateurs.idAnimateur";
$res = $pdo->query($sql);
if ($res->rowCount() > 0) {
echo "<table>";
echo "<tr>";
echo "<th>Nom de l'animation</th>";
echo "<th>Date</th>";
echo "<th>Horaire</th>";
echo "<th>Nombre d'animateurs</th>";
echo "<th>Animateurs disponibles</th>";
echo "<th>Ma disponibilité</th>";
echo "</tr>";
while ($row = $res->fetch()) {
echo "<tr>";
echo "<td>".$row['animationNom']."</td>";
//Date
$timeStamp=strtotime($row['dateAnim']);
$numJourAnim=date("w", $timeStamp);
$jourAnim=$jour[$numJourAnim];
echo "<td>" . $jourAnim . "</br>" . date("d/m/Y", strtotime($row['dateAnim'])) ."</td>";
//Heures
echo "<td>de ".substr($row['heureDebut'], 0, 5)."<br/>à " . substr($row['heureFin'], 0, 5)."</td>";
echo "<td>".$row['nombreAnim']."</td>";
echo "<td>";
$req = $pdo->prepare("SELECT * FROM animateurs JOIN crosstable ON animateurs.idAnimateur = crosstable.idAnimateur WHERE idAnimation = :idAnimation");
//echo $row['idAnimation'];
$req->bindParam(':idAnimation', $row['idAnimation']);
$req->execute();
echo "Disponibles :<br/>";
while ($rowDispos = $req->fetch()) {
if ($rowDispos['disponibilite']=="disponible") {
echo $rowDispos['prenom']. " " .$rowDispos['nom'] . "<br/>";
}
}
echo "<td>".'<input type="radio" id="disponibilite" name="disponibilite_id' . $row['idAnimation'] . '" value="disponible">Intéressé<br/>'.
'<input type="radio" id="disponibilite" name="disponibilite_id' . $row['idAnimation'] . '" value="non">Non disponible<br/>'.
'<input type="radio" id="disponibilite" name="disponibilite_id' . $row['idAnimation'] . '" value="siBesoin">Si nécessaire<br/>'.
"</td>";
echo "</tr>";
}
echo "</table>";
unset($res);
}
else {
echo "No matching records are found.";
}
idAnimateur prenom nom email gsm codePostal naissance statut francais anglais neerlandais tailleTShirt asmoTShirt idUser
37 Wanda Maximoff wanda#gmail.com 1111111 7000 1983-04-09 independant 1 0 0 L 0 2
36 Tony Stark tony#despunches.be 666 7000 2022-10-03 independant 1 1 1 L 0 1
To reexplain things:
I need a table with 1 line per animation (per event).
Each column contains data about that event (date, time, place,...).
One columns contains data about people for that event. They can be present, interested, or busy. I would like, in that column, to sort and list people between present, interested, and busy. This data is stored in crosstable, with the id's of the event, the people, and their "status" concerning that event (and probably other things later on).
I'll need (one step at a time, but it might be important) to send an email to all the people that are marked as "present" at an event.

For now, let's stick with the query inside the while () loop. You want the animateurs stored by their status. You currently have:
SELECT *
FROM animateurs
JOIN crosstable ON animateurs.idAnimateur = crosstable.idAnimateur
WHERE idAnimation = :idAnimation
Currently you're only using 3 columns. In that case it's better to really name those columns:
SELECT
animateurs.disponibilite,
animateurs.prenom,
animateurs.nom
FROM animateurs
JOIN crosstable ON animateurs.idAnimateur = crosstable.idAnimateur
WHERE crosstable.idAnimation = :idAnimation;
Note how I make clear, every time, in which table a column is located. It might not be needed now, but when queries become more complex it will prevent errors.
Now you could use three queries to get the three status groups:
SELECT
animateurs.disponibilite,
animateurs.prenom,
animateurs.nom
FROM animateurs
JOIN crosstable ON animateurs.idAnimateur = crosstable.idAnimateur
WHERE crosstable.idAnimation = :idAnimation AND
crosstable.disponibility = 'disponible';
And the same for the 'non' and 'sieBesoin' people. However, we like to do this in one query. I would keep it simple and use this:
SELECT
animateurs.disponibilite,
animateurs.prenom,
animateurs.nom,
crosstable.disponibility
FROM animateurs
JOIN crosstable ON animateurs.idAnimateur = crosstable.idAnimateur
WHERE crosstable.idAnimation = :idAnimation;
Now you can sort them in PHP:
$animateurGroups = [];
while ($rowDispos = $req->fetch()) {
$disponibility = $rowDispos['disponibilite'];
$animateurGroups[$disponibility][] = $rowDispos['prenom']. " " .$rowDispos['nom'];
}
And then you can display the three groups like so:
foreach ($animateurGroups as $disponibility => $animateurGroup) {
echo "Disponibles: $disponibility<br/>";
foreach ($animateurGroup as $animateur) {
echo "$animateur<br>";
}
}
Later you should try and get the query completely out of this while () loop.
OK, I will try to put only a single query outside of the while () loop. We'll follow the same strategy as before, but now we have to add in the information about the animations, keeping in mind that there can be animations without any animators.
I now notice that the main query you have already does this:
SELECT *
FROM animations
LEFT JOIN crosstable ON animations.idAnimation = crosstable.idAnimation
LEFT JOIN animateurs ON crosstable.idAnimateur = animateurs.idAnimateur";
However, I always like to specify what I take from database, if only so that I can tell what is there, because I haven't got your database.
SELECT
animations.idAnimation,
animations.animationNom,
animations.dateAnim,
animations.heureDebut,
animations.heureFin,
animateurs.disponibilite,
animateurs.prenom,
animateurs.nom,
crosstable.disponibility
FROM animations
LEFT JOIN crosstable ON animations.idAnimation = crosstable.idAnimation
LEFT JOIN animateurs ON crosstable.idAnimateur = animateurs.idAnimateur";
Now we need to sort these results in PHP:
$animations = [];
while ($rowDispos = $req->fetch()) {
$idAnimation = $rowDispos['idAnimation'];
if (!isset($animations[$idAnimation]) {
$animations[$idAnimation] = ['animationNom' => $rowDispos['animationNom'],
'dateAnim' => $rowDispos['dateAnim'],
'heureDebut' => $rowDispos['heureDebut'],
'heureFin' => $rowDispos['heureFin']];
}
$disponibility = $rowDispos['disponibilite'];
if (!is_null($disponibility)) {
$animateurNom = $rowDispos['prenom']. " " .$rowDispos['nom'];
$animations[$idAnimation]['animateurGroups'][$disponibility][] = $animateurNom;
}
}
Note that I have no way to check this code myself, so I cannot guarantee it is correct.
Now that we have gathered everything in an array we can render the HTML table. This is a very much simplified version of that:
foreach ($animations as $idAnimation => $animation) {
echo $animation['animationNom'] . '<br>';
echo $animation['dateAnim'] . '<br>';
echo $animation['heureDebut'] . '<br>';
echo $animation['heureFin'] . '<br>';
if (isset($animation['animateurGroups']) {
foreach ($animation['animateurGroups'] as $disponibility => $animateurGroup) {
echo "Disponibles: $disponibility<br/>";
foreach ($animateurGroup as $animateur) {
echo "$animateur<br>";
}
}
}
}
This basically outputs everything on lines, not in a table, you I think you can see how you can turn this back into a table.
Storing everything in an array will work well for a limited number of animations. If you have thousands of them you should use LIMIT in your query.
Again, I must stress that I cannot properly test this code, and I know mistakes are quickly made. However, I think this should get you started.

Related

Grouping database entries into dynamic HTML tables

I have a database where teams will have multiple entries each with different locations. Each entry will have a team name. So for example, team1 might appear several times but each time the location will be different.
The structure of the DB is (each of these represents a column header):
team_name, first_name, last_name, location, arrival_time
My current working code creates HTML tables grouped by team name but currently only creates one row to show the first location and the time of arrival for the first location. I need this to dynamically create more rows to show all locations and arrival times for each team.
The desired result would look like this -
https://codepen.io/TheBigFolorn/pen/LqJeXr
But current result looks like this -
https://codepen.io/TheBigFolorn/pen/qgMppx
And here is an example of how the DB table might look -
https://codepen.io/TheBigFolorn/pen/daqJze
I've tried breaking up the echo and adding a second while loop before the row that I want to apply the above logic to but it seems to break everything. Any input on how I get this to work without having to use separate queries for each team would be very much appreciated. I'm new to php so please go easy on me :)
<?php
$leaders = "SELECT *, COUNT(location) FROM my_example_table GROUP BY team_name";
$result = mysqli_query($connect, $leaders) or die ("<br>** Error in database table <b>".mysqli_error($connect)."</b> **<br>$sql");
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "
<div class='red-border'>
<h2>". $row["team_name"]. "<br><small>Total locations visited: ". $row["COUNT(location)"]. "</small></h2>
</div>
<div class='data-holder'>
<table>
<tr>
<th>Location</th>
<th>Time of arrival</th>
</tr>
<tr><td>". $row["location"]. "</td> <td>". $row["arrival_time"]. "</td></tr>
</table>
</div>
";
}
} else {
echo "0 results";
}
?>
Your problem is due to the GROUP BY, as you've probably realised. This is necessary in order to get a count per team, but causes the number of rows output to be only 1 per team - that's what grouping does. Fundamentally, running an aggregate query such as a COUNT or SUM is incompatible with also outputting all of the row data at the same time. You either do one or the other.
Now, you could run two queries - one to get the counts, and one to get all the rows. But actually you don't really need to. If you just select all the rows, then the count-per-team is implicit in your data. Since you're going to need to loop through them all anyway to output them in the HTML, you might as well use that process to keep track of how many rows you've got per team as you go along, and create the "Total number of locations" headings in your HTML based on that.
Two things are key to this:
1) Making the query output the data in a useful order:
SELECT * FROM my_example_table Order By team_name, arrival_time;
2) Not immediately echoing HTML to the page as soon as you get to a table row. Instead, put HTML snippets into variables which you can populate at different times in the process (since you won't know the total locations per team until you've looped all the rows for that team), and then string them all together at a later point to get the final output:
$leaders = "SELECT * FROM my_example_table Order By team_name, arrival_time;";
$result = mysqli_query($connect, $leaders) or die ("<br>** Error in database table <b>".mysqli_error($connect)."</b> **<br>$sql");
$currentTeam = "";
$locationCount = 0;
$html = "";
$teamHtmlStart = "";
$teamHtmlEnd = "";
if ($result->num_rows > 0)
{
while($row = $result->fetch_assoc())
{
//run this bit if we've detected a new team
if ($currentTeam != $row["team_name"]) {
//finalise the previous team's html and append it to the main output
if ($currentTeam != "") $html .= $teamHtmlStart.$locationCount.$teamHtmlEnd."</table></div>";
//reset all the team-specific variables
$currentTeam = $row["team_name"];
$teamHtmlStart = "<div class='red-border'><h2>".$currentTeam."<br><small>Total locations visited: ";
$locationCount = 0;
$teamHtmlEnd = "</small></h2>
</div>
<div class='data-holder'>
<table>
<tr>
<th>Location</th>
<th>Time of arrival</th>
</tr>";
}
$teamHtmlEnd .= "<tr><td>". $row["location"]. "</td> <td>". $row["arrival_time"]. "</td></tr>";
$locationCount++;
}
//for the final team (since the loop won't go back to the start):
$html .= $teamHtmlStart.$locationCount.$teamHtmlEnd."</table></div>";
echo $html;
}
else {
echo "0 results";
}
Here's a runnable demo (using some static data in place of the SQL query): http://sandbox.onlinephpfunctions.com/code/2f52c1d7ec242f674eaca5619cc7b9325295c0d4

Update MySQL-database with array values

How can I update a database with the values from an array? For example, let’s say we got a database with three tables:
Meals:
mealnr(PK), name, sort
Ingredients: ingredientnr(PK), name, stock
Structure: mealnr(FK), ingredientnr(FK), amount
I filled the database with some meals and ingredients. Every meal consists of multiple ingredients. The chef decides you only need 75g of ingredient x instead of 100g for meal y, so it needs to be changed in the database. Of course it can be done with SQL-commands, but I want to do it using a form in PHP.
First I made a page where all the meals are displayed. A meal can be edited using the edit-button next to it and based on the mealnr, you can change the amount of one or multiple ingredients for that particular meal. On the edit-page all the ingredient names and amounts are displayed in a table. The amount fields are textfields, those can be edited.
I made this script, but I don’t know exactly how I can update my database with the values of an array. I tried it with a foreach-loop, but it doesn't work.. yet. Can somebody help me?
<?php
$conn = mysql_connect('localhost', 'root', '');
mysql_select_db("eatit", $conn);
$id = $_REQUEST['mealnr'];
$result = mysql_query("SELECT meals.name AS mealname, structure.amount, ingredients.name AS ingredientname
FROM Meals, Structure, Ingredients
WHERE meals.mealnr = structure.mealnr
AND structure.ingredientnr = ingredients.ingredientnr
AND meals.mealnr = '$id'");
if(isset($_POST['save']))
{
$new_amount = $_POST['amount[]'];
foreach ($new_amount as $value) {
mysql_query("UPDATE structure SET amount ='$value', WHERE mealnr = '$id'")
or die(mysql_error());
}
}
mysql_close($conn);
?>
<p><strong>Ingredients:</strong></p>
<?php
echo "<table>";
echo "<tr>";
echo "<th>Ingredient</th>";
echo "<th>Amount (gr)</th>";
echo "</tr>";
while($ingredient = mysql_fetch_array($result))
{
echo "<tr>";
echo "<td>";
echo $ingredient['ingredientname'];
echo "</td>";
echo "<td>";
echo '<input type="text" formmethod="post" name ="amount[]" value="' . $ingredient['amount'] . '" />';
echo "</td>";
echo "</tr>";
}
?>
<input type="submit" name="save" value="save" />
In your HTML markup you have declared the elements holding the name amount as an array by using amount[].
So, in your php code that receives the data it's enough to just refer to the amounts this way:
$new_amount = $_POST['amount'];
instead of:
$new_amount = $_POST['amount[]']; // in fact, this is wrong
Your foreach is fine, you should add some checks so that the $value actually contains a value that you expect, for example an int, float or not less than zero (or whatever checks you find necessary).
foreach($new_amount as $value){
if($value != '' && $value >= 1){
//sql statements goes here.
}
}
Receiving form data this way and then directly injecting the result to your SQL statement is always dangerous:
$id = $_REQUEST['mealnr'];
If you declare that you expect an integer (as the id's should be) before you directly inject the code to your SQL statement you have already written safer code.
$id = (int)$_REQUEST['mealnr'];
Also, just for the record - the mysql_* library is deprecated. As pointed out in the comments, try using PDO or mysqli instead - really!

PHP code, breaking somewhere in this flow. Can't figure out where the error is

I've been working on this for 2 days straight with no progress. Basically I'm pulling a list of parts determined by the year, make, and model of a car. The database was written by someone else, so piecing the mess they made together has turned out to be quite a task. So. It's pulling multiple autoparts, from multiple tables, and grouping them together, first by car, then by individual parts with part No. Price, Description, etc. Even using the original code that the site was designed with, breaks at this point. The ajax calls for the year, make, and model all work, it's when I reach these lines of code, that it breaks.
$vid is defined by the yearID from the modelyear table
function loadDetails($vid)
{
$modelYearDetails = "
SELECT Model,YETD.yearID,startYear,endYear,bodyType,engine,Drivetrain,Transmissions,wheelbase
FROM year_engine_tran_drive YETD
LEFT JOIN wheelbase W ON YETD.wheelbaseID = W.wheelbaseID
LEFT JOIN engines E ON YETD.engineID = E.idengines
LEFT JOIN transmissions T ON YETD.transmissionID = T.idTransmissions
LEFT JOIN drivetrains D ON YETD.driveTrainID = D.idDrivetrains
LEFT JOIN bodytype BT ON YETD.bodyTypeID = BT.idbodyType
LEFT JOIN modelyear MY ON YETD.yearID = MY.VID
LEFT JOIN model M ON MY.idModel = M.idModel
WHERE yearID IN (".$vid.")";
$stmt = $this->dbConnect->getDBHandle()->prepare($modelYearDetails);
$stmt->execute();
$cnt = 0;
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row)
{
$model = $row['Model'];
$yearID = $row['yearID'];
$startYear = $row['startYear'];
$endYear = $row['endYear'];
$name = $row['bodyType'];
$engine = $row['engine'];
$driveTrain = $row['Drivetrain'];
$transmission = $row['Transmissions'];
$wheelbase = $row['wheelbase'];
$cab = $row['cab'];
$bed = $row['bed'];
echo "<header id=".$yearID.">";
echo "<a href='#".$yearID."'>";
echo "<div>".$model." ".$startYear."-".$endYear."</div>";
echo "<ul>";
echo "<li><div class='carPart'>Engine:</div> <div class='carValue'>".$engine."</div></li>";
echo "<li><div class='carPart'>Drivetrain:</div> <div class='carValue'>".$driveTrain."</div></li>";
echo "<li><div class='carPart'>Transmission:</div> <div class='carValue'>".$transmission."</div></li>";
echo "</ul>";
echo "</a>";
echo "<div style='clear:both;'></div>";
echo "</header>";
Now. This is from an include file, so I'm not getting the MySQL error when it crashes. Perhaps the code is fine, but something is breaking it somewhere. It's not spitting out an error, or anything. It's just leaving the div blank. I know it's a lot of code to go through, but the file is somewhere around 560 lines long, and I wasn't about to post all that. TY in advance for any help that might be provided. Even tips on how to clean this up would be great.

Nested while loop in PHP is not working for MySQL database queries

All the whole day I'm trying to solve this problem but still no luck.
The scenario is: I am developing a vertical menu which should query groups and items of those groups in a menu respectively, but groups are being populated without its items.
Code was written in the following way:
$data1 = mysql_query(select groupnames from groups where categoryy='Agriculture');
while($info1=mysql_fetch_array($data1))
{
echo $info1[0];
$data2==mysql_query(select itms from groupitems where categoryy='Agriculture' and groupname='$info1[0]');
while($info2=mysql_fetch_array($data2))
{
echo $info2[0];
}
}
In the above code, groups are being populated nicely but no items from groupitems table are being populated. If I write Grain (Grain is one of the group of agriculture in my database) instead of groupname=$info1[0] then it works. But it should be got dynamically from the query.
Please help, I'm in trouble.
at last its solved! here's the code:
<?php
include "aPannel/dbconn.php";
$query="select GroupName from categorygroup where categoryy='Agriculture'";
$i=0;
$result=mysql_query($query);
$num=mysql_num_rows($result);
$groupname=mysql_result($result ,$i ,"GroupName");
mysql_close();
if ($num=="0") echo "<p>Sorry, there are no groups to display for this Category.</p>";
else
{
echo "<p>There are currently <strong>$num</strong> groups represented in our database.</p><br>";
while($i < $num)
{
$groupname=mysql_result($result ,$i ,"GroupName");
include("aPannel/dbconn.php");
$query2 = "SELECT subcategory FROM groupsubcategory WHERE groupname='$groupname'"; // count number of items in each group
echo $query2 . "<br/>";
$resultt=mysql_query($query2);
$countt=mysql_num_rows($resultt);
mysql_close();
echo $countt . "subcategories" . "<br/>"; // display number of items
$i++;
}
} // end if results
?>
Your queries are not wrapped around double-quotes (" ") . Always remember that what you pass to mysql_query method is a string argument. And also $data2==.... seems wrong.
So, change the code like this
$data1=mysql_query("select groupnames from groups where categoryy='Agriculture'");
while($info1=mysql_fetch_array($data1))
{
echo $info1[0];
$infoTemp=$info1[0];
$data2=mysql_query("select itms from groupitems where categoryy='Agriculture'
and groupname='$infoTemp'");
while($info2=mysql_fetch_array($data2))
{
echo $info2[0];
}
}
I hope it should work
EDIT: Also are you sure column itms in second query or items ?
EDIT: added temporary variable

Displaying results after MySQL JOIN query with PHP

$sql = "SELECT messages.text, users.name FROM messages INNER JOIN users ON messages.user_id=users.id ORDER BY messages.ms_id DESC LIMIT 10";
$result = mysql_query($sql);
$rows = array();
while($row = mysql_fetch_array($result))
{
$rows[]=$row;
}
echo $rows[0][1].$rows[0][0];
/* for($i=0;$i<=10;$i++)
{
echo $rows[i][1].$rows[i][0];
}
*/
This script is supposed to show the last 10 messages in a chat.What I'm doing is getting the Name of the user from the users table and the text from the message table and I want to display them in my chat window.Right now I have only 4 messages recorded and don't know how this affects the whole script I should implement a check for this too, but the bigger problem is that when i use echo $rows[0][1].$rows[0][0]; the info is displayed correctly, but when I try to make a loop so I can show tha last 10 (I tried the commented one) then nothing is displayed.I thought at least when I use this loop I'll see the 4 recorded messages but what really happen is a blank window.Obvously I have the info recorded in $rows[] and can echo it, but don't understand why this loop don't work at all.I'll appreciate if someone can help me with this and with the check if the messages are less then 10.
Thanks.
Leron
P.S
Here is the edited script, thanks to all of you, I need the array because otherwise the most recent message is shown at the top which is not an opiton when I use it for diplaying chat masseges.
for($i=10;$i>=0;$i--)
{
if($rows[$i][1]!="" || $rows[$i][0]!="")
{
echo $rows[$i][1].' : '.$rows[$i][0];
echo "</br>";
}
}
Your FOR loop was running 11 times even if only 10 records. The second clause should be a < instead of <=. Plus the $ was missing on the i variable.
For example sake, you don't really need to make an array from the rows, and you can refer to the fields by name:
while($row = mysql_fetch_array($result))
{
echo $row['name'] . ' says: ' . $row['message'] . '<BR>';
}
why not just do
while($row = mysql_fetch_array($result))
{
echo $row[1]." ".$row[0];
}
Your query, auto limits it to the last 10, this will then show anything from 0 to 10 which get returned.
PS I added a space between username and message for readability
You need $ symbols on your i variable:
for($i=0;$i<10;$i++)
{
echo $rows[$i][1].$rows[$i][0];
}
A more robust solution would be like this:
$sql = "SELECT messages.text, users.name FROM messages INNER JOIN users ON messages.user_id=users.id ORDER BY messages.ms_id DESC LIMIT 10";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
echo $row[1].$row[0];
}

Categories