Print two lists based on two related database tables - php

I have these 2 while loops.
Loop. Left side screen. Shows users attached to a task.
$getcurrentusers = $conn->prepare("SELECT u.userid, u.username, u.Fname, u.inactive, r.userid, r.job_id FROM users AS u INNER JOIN job_responsible AS r ON u.userid = r.userid WHERE u.inactive = 0 AND r.job_id = $getPostID ORDER BY u.Fname");
$getcurrentusers->execute();
$resultgetcurrentusers = $getcurrentusers->get_result();
$getcurrentusers->close();
if ($resultgetcurrentusers->num_rows > 0) {
while($row = $resultgetcurrentusers->fetch_assoc()) {
echo $row["username"]." (".$row["Fname"].")<br />";
}
} else {
echo "Der er ikke valgt nogle ansvarlige til denne opgave.";
}
Loop. Right side screen. List all possible users that can be added to the task
$getdepartmentview = $SetDepartmentView;
$explodegetdepartmentview = explode(",",$getdepartmentview);
$implodegetdepartmentview = "'".implode("','",$explodegetdepartmentview)."'";
$getusers = $conn->prepare("SELECT userid, username, Fname, inactive FROM users WHERE departmentid IN ($implodegetdepartmentview) AND inactive = 0 ORDER BY Fname");
$getusers->execute();
$resultgetusers = $getusers->get_result();
$getusers->close();
if ($resultgetusers->num_rows > 0) {
while($row = $resultgetusers->fetch_assoc()) {
echo "<input type=\"checkbox\" name=\"choose_responsible[]\" value=\"".$row["userid"].",\" />".$row["Fname"]."<br />";
}
}
The problem is, I would really like, that the input checkbox is 'checked' on the right side when there is a match from the left side 1. loop.
So..
Loop 1 | Loop 2
name3 | name1
name5 | name2
name7 | name3 - Inputbox checked
name8 | name4
| name5 - Inputbox checked
| name6
| name7 - Inputbox checked
| name8 - Inputbox checked
| name9
| name10

In the first loop you can store user_id in an array
$user_ids[] = $row["userid"]
In the second loop you check the current user id if it exists in the previous saved $user_ids array
$checked = false;
if (in_array($row["userid"], $user_ids)) {
$checked = true;
}

This task calls for a LEFT JOIN in a single query to avoid multiple trips to the database and to avoid inefficient php processes on the result set.
$sql = <<<SQL
SELECT u.userid,
u.username,
u.Fname,
u.inactive,
r.userid responsibleUserId,
r.job_id
FROM users AS u
LEFT JOIN job_responsible AS r
ON u.userid = r.userid
AND r.job = ?
WHERE u.inactive = 0
ORDER BY u.Fname
SQL;
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $getPostID);
$result = $stmt->execute();
$users = $result->fetch_all(MYSQLI_ASSOC);
Now you have all of the data that you need and therefore no more reasons to visit the database.
To list the users who are assigned to the targeted job, you iterate the array of associative arrays and show users where job_id is not null.
foreach ($users as $user) {
if ($user['job_id']) {
printf('%s (%s)<br />', $user['username'], $user['Fname']);
}
}
To display all users and conditionally check checboxes next to names that are already "responsible", iterate the array again and rely on job_id to determine the checkbox.
foreach ($users as $user) {
printf(
'<input type="checkbox" name="choose_responsible[]" value="%d"%s/>%s<br />',
$user['userid'],
$user['job_id'] ? ' checked' : '',
$user['Fname']
);
}

Related

Best practice for writing code in php for foreach->foreach->if(1st loop id == 2nd loop id)->result

First loop table
user_id | fname | lname
1 | first | emp
2 | second| emp
3 | third | emp
Second loop table
shift_id | employee_id
1 | 1
2 | 2
3 | 2
if($employees)
{
foreach ($employees as $employee)
{
if($employee['user_id'] == $shift['employee_id'])
{
echo ucwords($employee['fname']. ' ' .$employee['lname']);
}
}
}
I am getting the right result but I think there is some better way of writing this.
You can use joins in table. Left join means that the user line has to exists (because: LEFT) and the shifts enty is optional.
SELECT user.user_id, user.fname, user.lname, shifts.shift_id
FROM yourUserTable AS user
LEFT JOIN yourShiftsTable AS shifts ON(user.user_id = shifts.employee_id)
Now you get it in your initial array, as if you'd select it as one row from a table and no longer need to do tricks in PHP to combine information. If you can, always try to get the database to manage data, it does that way faster than PHP can.
Please note, the query could be a little off, I just wrote this out of the top of my head.
Just some test code I whipped up to test this from the information provided for this "Demonstration Code".
Note: I have used the mysqli class for the database (instantiating $db ) and have excluded the SQL Table setup.
What you would have had is something along the lines of this...
Case 1 - The original
$db = new mysqli('localhost', 'root', 'test', 'phptutorials_st26');
echo '<h2>Create $employees </h2>';
$query = "SELECT * FROM users";
$result = $db->query($query);
$employees = $result->fetch_all(MYSQL_ASSOC);
var_dump($employees);
echo '<h2>Create $shifts </h2>';
$query = "SELECT * FROM shifts";
$result = $db->query($query);
$shifts = $result->fetch_all(MYSQL_ASSOC);
var_dump($shifts);
echo '<h2>Using foreach on $employees and $shifts</h2>';
if ($employees) {
foreach ($employees as $employee) {
foreach ($shifts as $shift) {
if ($employee['user_id'] == $shift['employee_id']) {
echo ucwords($employee['fname'] . ' ' . $employee['lname']);
echo '<br>';
}
}
}
}
The Result from the above is
First Emp
Second Emp
Second Emp
Case 2 - Using a Join
Well using a join, as everyone has already stated, is the way to go...
$sql = "SELECT u.user_id, u.fname, u.lname, s.shift_id
FROM users AS u
JOIN shifts AS s ON(u.user_id = s.employee_id)
";
$result = $db->query($sql);
$employees = $result->fetch_all(MYSQL_ASSOC);
// To see what comes out because we always check things.
var_dump($joined_result);
(Don't ask me why I love using very abbreviated aliases for the table names! It's just "a thing".)
Then your "loop" simply becomes...
echo '<h2>Using foreach on join</h2>';
foreach ($employees as $employee) {
echo ucwords($employee['fname'] . ' ' . $employee['lname']);
echo '<br>';
}
And the result is...
First Emp
Second Emp
Second Emp
Case 2 - has reduced the code and only requires 1 Trip to the Database.
Does that help you any?
You could do it this way also. Its a little shorter.
SELECT TABLE1.FNAME, TABLE1.LNAME, TABLE2.EMPLOYEE_ID
FROM TABLE1, TABLE2
WHERE TABLE1.USER_ID = TABLE2.EMPLOYEE_ID;

Looping through a loop each result gives multiple incorrect results

I have been scratching my head for a very long time about this PHP code. I am trying to achieve something like
->Get each status
->Get each user in user's friends list
->Display status' from each user that is in the user's friends list
and repeat until there is no more. I have been looking for a solution for more a few days and it is really bugging me. Here is the code I tried:
EDIT: posted schema as requested
https://kjf-tech.net/files/schema.png
<?php
$connect = new MySQLi($DBhost,$DBuser,$DBpass,$DBname);
$querya = "SELECT * FROM statuses ORDER BY `id` DESC";
$result = mysqli_query($connect, $querya);
$ALLDATA = array();
$DBcon2 = new MySQLi($DBhost,$DBuser,$DBpass,$DBname);
if ($DBcon2->connect_errno) {
die("ERROR : -> ".$DBcon2->connect_error);
}
while ($record = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
array_push($ALLDATA, $record);
$queryb = "SELECT * FROM friendslist WHERE idOfPerson1='".$record['idOfUser']."' OR idOfPerson2='".$record['idOfUser']."' OR idOfPerson2='".$userRow['user_id']."' OR idOfPerson1='".$userRow['user_id']."' ORDER BY `id` DESC";
$result2 = mysqli_query($connect, $queryb);
$ALLDATA2 = array();
while ($record2 = mysqli_fetch_array($result2, MYSQLI_ASSOC)) {
array_push($ALLDATA2, $record2);
if($record['idOfUser'] == $userRow['user_id']) {
echo '<div>You Posted on '.$record['whenPosted'].'<br />'.$record['content'].'</div>';
}
elseif($record2['idOfPerson1'] == $userRow['user_id']) {
$query2 = $DBcon2->query("SELECT * FROM tbl_users WHERE user_id='".$record2['idOfPerson2']."'");
$userRow2=$query2->fetch_array();
echo '<div>'.$userRow2['username'].' Posted on '.$record['whenPosted'].'<br />'.$record['content'].'</div>';
}
elseif($record2['idOfPerson2'] == $userRow['user_id']) {
$query2 = $DBcon2->query("SELECT * FROM tbl_users WHERE user_id='".$record2['idOfPerson1']."'");
$userRow2=$query2->fetch_array();
echo '<div>'.$userRow2['username'].' Posted on '.$record['whenPosted'].'<br />'.$record['content'].'</div>';
}
}
mysqli_free_result($result2);
}
$DBcon2->close();
mysqli_free_result($result);
?>
Your schema looks good, but let's take another look at the relations. I'm going to rename some of the columns for convenience.
Users:
+- user_id
| user_name
|
|
| Friendships:
| fid
+- user_id
| friend_id --------+
| friendship_start |
| |
| |
| Statuses: |
| sid |
+- user_id ----------+
post_date
content
If you just wanted to find statuses of your friends, the query would look thus:
SELECT statuses.content
FROM friendships, statuses
WHERE friendship.user_id=? AND
friendships.friend_id = statuses.user_id
You would, of course, bind the appropriate user_id value when you ->prepare() the statement.
(See http://php.net/manual/en/mysqli.prepare.php for the proper way to do sql. You don't ever want to do something like mysql_query("select * from table where id=".$_POST['id']) because it's open to SQL injection)
Unfortunately, though, this does not include your own status in the query results. We'll have to do a little more work on the query...
SELECT statuses.content
FROM friendships, statuses
WHERE
( friends.user_id = ? AND
friends.friend_id = stuatuses.user_id )
OR
statuses.user_id = ?
ORDER BY statuses.post_date DESC
So far, so good... but we don't have the names nor the post date. The post date is easy, just add that to the select:
SELECT statuses.content, statuses.post_date
To add the name, we have to get data from Users also.
SELECT users.user_name, statuses.content, statuses.post_date
FROM users, friendships, statuses
WHERE
users.user_id = ? AND
(
( users.user_id = friendships.user_id AND
friendships.friend_id = statuses.user_id )
OR
statuses.user_id = users.user_id
)
ORDER BY statuses.post_date DESC
And there you have it; the database does all the work for you. No need for nested queries and such. This will just give you the simple list to print on your page. Please keep in mind that this is off the top of my head, so you may have to tweak it if I overlooked something.

Report generation based from mysql query

I have this 3 tables namely form, form_responses, metrics with the following structure
form
->id
->phone
->calldatetime
form_reponses
->id
->form_id
->metrics_id
->response
metrics
->id
->description
->question
And I want to make report with a format something like this
|Metrics Description|Metrics Question|Phone1|Phone2|Phone3|Phone4
|___________________|________________|______|______|______|______
| Sample | Sample | Yes | Yes | Yes | Yes
Is it possbile to this output just by the mysql query alone? Please note that the Phone1, Phone2, Phone3... is scaling horizontally. Originally I need that output in the excel file I have already tried this using Laravel PHP and http://www.maatwebsite.nl/laravel-excel/docs
$query = "SELECT id, phone FROM qcv.forms WHERE calldatetime >= '$from' AND calldatetime <= '$to' ORDER BY id ASC LIMIT 250 ;";
$phone = DB::connection('mysql')->select($query);
$metrics = Metric::all();
$metric_start = 10;
$start = "D";
$count = 10;
foreach ($phone as $key => $value2) // Populate Phone Numbers Horizontally
{
$sheet->cell($start.'9', $value2->phone);
// This will fill the responses for each number
foreach ($metrics as $key => $value)
{
$responses = FormResponses::where('form_id', '=', $value2->id)->where('metrics_id', '=', $value->id)->get();
$sheet->cell($start.$count, $responses[0]->response);
$count++;
}
$start++;
$count = 10;
}
foreach ($metrics as $key => $value) // Populate Metrics Vertically
{
$sheet->cell('C'.$metric_start, $value->question);
$sheet->cell('B'.$metric_start, $value->description);
$sheet->cell('A'.$metric_start, $value->metrics_name);
$metric_start++;
}
But seems this method is really slow especially in processing so I'm wondering if I could do the output in mysql command alone?
To get multiple sub-records per row in a one-to-many relationship using SQL, you would have to use a sub-query:
SELECT
m.description,
m.question,
(select phone from form f1 where f1.id = m.id and ...
/* some other unique criteria */) as Phone1,
(select phone from form f2 where f2.id = m.id and ...
/* some other unique criteria */) as Phone2,
(select phone from form f3 where f3.id = m.id and ...
/* some other unique criteria */) as Phone3,
(select phone from form f4 where f4.id = m.id and ...
/* some other unique criteria */) as Phone4
FROM metrics m
However...you may not have any columns to uniquely identify each form in this way...and your SQL engine may not allow a 3rd level of nesting of sub-queries (which is another way to individually select records from the same table).
So here's one other variation that would work. It should be slightly less code and fewer database connections, so it should perform better, even if you find it less intuitive. Here's the SQL portion:
SELECT
m.description,
m.question,
f.phone
FROM metrics m
INNER JOIN form f ON f.id = m.id
And then in PHP:
$lastid = '';
$phone_count = 0;
foreach ($record as $key => $value) {
$phone[$phone_count] = $value->phone;
$phone_count++;
if ($lastid != $value->id) {
// new record
$sheet->cell ( /* whatever */ );
$phone_count = 0;
}
$lastid = $value->id;
}

PHP/MYSQL: how to loop matching results from a query already inside a while loop

I'm very new to PHP/MYSQL and find this quite difficult to explain, so hopefully someone will understand what I'm trying to do. I have a database that collects information on artists and songs. I want to be able to link the artists to the song and display the "credit" with the song information.
I have a database with tables similar to this:
Artist
| artist_id, artist_name
Credits
| credit_id, credit_name
Song
| song_id, song_name
Credit_To_Artist
| credit_id, artist_id, song_id
Example data:
Artist
| 2, Peter Mark
Artist
| 5, Mette Christiansen
Credits
| 1, Producer
Credits
| 2, Writer
Credits
| 3, Vocalist
Song
| 23, The Game
Credit_To_Artist
| 1, 2, 23
Credit_To_Artist
| 2, 2, 23
Credit_To_Artist
| 3, 5, 23
I have created a page "song.php" that displays information on each song using mysql_real_escape_string to get the song ID from the URL:
$id = mysql_real_escape_string($_GET['id']);
if (!$id) {
die('Please provide an id!');
}
$query = "SELECT * FROM `Credit_To_Artist` AS c2a
INNER JOIN `Credits` AS cr ON cr.credit_id = c2a.credit_id
LEFT OUTER JOIN `Artist` AS a ON a.artist_id = c2a.artist_id
LEFT OUTER JOIN `Song` AS s ON s.song_id = c2a.song_id
WHERE c2a.song_id = $id";
$res = mysql_query($query);
$row = mysql_fetch_assoc($res);
The issue I'm having is I want to be able to list all of the artists linked to that song, and all of their credits in brackets next to it. Since there are more than one artist linked to each song, and most of them have more than one credit (producer, writer, vocalist etc), I have no idea how to write a loop function that shows both of these. Below is my attempt to show what I mean, although it obviously doesn't work:
while ($row = mysql_fetch_array($res)) {
$artist = $row[artist_name];
echo "$artist";
while ($row = mysql_fetch_array($res)) {
$credit = $row[credit_name];
echo "$credit";
}
echo "<br />";
}
This is what I'd ideally like to achieve from the example data above:
Song: The Game
Credits: Peter Mark (Producer, Writer)
Mette Christiansen (Vocalist)
You have two options:
GROUP_CONCAT(..)
You can use GROUP_CONCAT(..). This mysql function groups values in a column that are in each group. You would alter the sql to group by artist_id in this case.
SELECT a.artist_name as aname, GROUP_CONCAT(c.credit_name) as credits
FROM Credits_To_Artist as c2a
JOIN Artist as a ON c2a.artist_id = a.artist_id
JOIN Credits as c ON c2a.credit_id = c.credit_id
GROUP BY c2a.credit_id
Your rows would look like:
Array( "aname" => "name",
"credits" => "function 1,function 2" )
The biggest problem with GROUP_CONCAT is that if you have to concat a lot of values together, it might exceed the maximum width of the row. This does not seem to be the case for your problem. You would not need a loop with this approach.
Adding to array
If you keep the query as it is, you have a row for each 'credit'. You can prepare your data by adding it to an Array, then use implode(..) in php to add commas.
$artists = Array();
while( $row = mysql_fetch_array($res) ) {
$artist = $row[artist_name];
$credit = $row[credit_name];
if( !array_key_exists( $artist, $artists ) ) {
$artists[$artist] = Array();
}
$artists[$artist][] = $credit;
}
foreach( $artists as $artist => $creditarr ) {
$credits = implode( ", ", $creditarr );
echo "{$artist} ({$credits})<br>";
}
You'll find that preparing your data in an array will sometimes be much faster than writing a query that does the same thing. I would probably choose the latter solution.
Let's start with the tables, here is what i think you should do
artist | id, name
song | id, title
credit | id, credit
song_artists | id, song_id, artist_id
credit_to_artists | id, song_artists_id, credit_id
Should be the way to handle the kind of relationship you want.
And here is the PHP code, it might not be the most efficient one, but it will do the job
$query = "SELECT * FROM song_artists WHERE song_id = $id_from_link";
$result = mysql_query($query);
while($row = mysql_fetch_array($result))
{
$artistQuery = "SELECT * from artist WHERE id = {$row['artist_id']}";
$artistResult = mysql_query($artistQuery);
while($artistRow = mysql_fetch_array($artistResult))
{
echo "The Artist: " . $artistRow['name'];
}
echo $songArtistId;
$creditToArtistQuery = "SELECT * FROM credit_to_artists WHERE song_artists_id = {$row['id']}";
$creditToArtistResult = mysql_query($creditToArtistQuery);
if(mysql_num_rows($creditToArtistResult)>0)
{
echo " { ";
$isFirstCredit = true;
while($creditToArtistRow = mysql_fetch_array($creditToArtistResult))
{
$creditQuery = "SELECT * FROM credit WHERE id = {$creditToArtistRow['credit_id']}";
$creditResult = mysql_query($creditQuery);
while($creditRow = mysql_fetch_array($creditResult))
{
if($isFirstCredit)
{
echo $creditRow['credit'];
$isFirstCredit = false;
}
else
{
echo ", " .$creditRow['credit'];
}
}
}
echo " } <br />";
}
}

Join of 3 Tables - How to walk through results?

I have this sql statement joining 3 tables:
SELECT * FROM `int_news`
LEFT JOIN tl_member ON int_news.member_id = tl_member.id
LEFT JOIN tl_news ON int_news.news_id = tl_news.id
The 3 Tables are like this:
Table 1 (int_news)
ID, member_id, news_id
Table 2 (tl_member)
id, firstname, lastname
Table 3 (tl_news)
id, headline
So far so good, but it seems i have a big blackhole in my head making me unable to solve how to output the result like this
For each "headline" i want ALL lastnames e.g.
headline 1 Jonny
Walker
Jim
headline 2 Knopf
Jon
Doe
It sounds your looking something like a pivot so if you group your query by headline, it will display each lastname as a column.
I found this good tutorial on pivots for mysql that might help you http://www.artfulsoftware.com/infotree/queries.php#78
headline 1 Jonny Walker Jim
headline 2 Knopf Jon Doe
Here is a loop that would do that *Forgive my php it's been a while.
$curHeadline = "";
while ( $db_field = mysql_fetch_assoc($result) ) {
if($curHeadline != $db_field['headline'])
{
$curHeadline = $db_field['headline'];
print $curHeadline . $db_field['ID']
}
print $db_field['lastName'] . "<BR>";
}
Try this for mysql :
SELECT tlnews.headline, GROUP_CONCAT(tl_member.last_name)
FROM `int_news` LEFT JOIN tl_member ON int_news.member_id = tl_member.id
LEFT JOIN tl_news ON int_news.news_id = tl_news.id
GROUP BY 1;
Expected Output:
headline1 Johnny,Walker,Jim
headline2 Knopf,Jon,Doe
...
Would something like this vaguely reflect your situation?
<?php
$sql = "
SELECT
`int_news`.`ID` AS `int_news_ID`,
`int_news`.`member_id`,
`int_news`.`news_id`,
`t1_member`.`id` AS `t1_member_id`,
`t1_member`.`firstname`,
`t1_member`.`lastname`, /* desired */
`t1_news`.`id` AS `t1_news_id`,
`t1_news`.`headline` /* desired */
FROM
`int_news`
LEFT JOIN `tl_member` ON `int_news`.`member_id` = `tl_member`.`id`
LEFT JOIN `tl_news` ON `int_news`.`news_id` = `tl_news`.`id`
";
$res = mysql_query($query);
$arrHeadings = array();
while($row = mysql_fetch_assoc($res)) {
// We want to output the results in groups of headings
$arrHeadings[$row['heading']][] = $row;
}
// Don't forget to cleanse for html output (unlike below)
// Loop through the headers
foreach($arrHeadings as $heading=>$arrRow) {
echo '<dl>';
echo '<dt>'.$heading.'</dt>';
echo '<dd>';
// Loop through rows with the same header
foreach($arrRow as $index=>$dbRow) {
echo $dbRow['lastname'].'<br />';
}
echo '</dd>';
echo '</dl>';
}
?>

Categories