PDO Count & Loop Optimization - php

Information
I am starting to notice a trend in my coding habits that I wonder if there is a better way to do it.
Problem
For the sake of an example, lets pretend I have a table that may have data in it, ideally I want to loop through the data if there is any, if not display an error message.
Current Solution
function countResults($type){
$STH = $this->database->prepare('SELECT COUNT(*) FROM table WHERE type = :type');
$STH->execute(array(':type' => $type));
return $STH->fetchColumn();
}
if($class->countResults("1") != 0){
$STH = $this->database->prepare('SELECT * FROM table WHERE type = :type ');
$STH->execute(array(':type' => $type));
while($row = $STH->fetch(PDO::FETCH_ASSOC)){
echo "Row Name:".$row['name']."<br />";
}
} else {
echo "None found";
}
Attempts Made
I have tried a solution of cutting this down by storing $STH->fetch(PDO::FETCH_ASSOC) into a variable then using if(!empty($var)){// loop code}; but it doesn't allow me to loop out the data correctly.
Conclusion
I understand this isn't a ground breaking bug that I am struggling to fix urgently, but I feel as if this is becoming a bad habit and there's a little nag in my mind that there is a smarter solution to this which I would love to learn before it turns into a habit!
Feel free to ask questions if needed, Thank you.

Your approach is rather illogical than wrong. All you need is common sense.
Imagine you're going to buy a six-pack. Are you getting cash from the pocket in two actions, or one? Are you really do like this:
Reach into pocket, only to feel the money. There are some. Okay.
Take the hand back and then reach again - for the money this time.
Why not to reach just once, and buy your beer if you get enough, or say sorry if not?
$STH = $this->database->prepare('SELECT * FROM table WHERE type = :type ');
$STH->execute(array(':type' => $type));
$data = $STH->fetchAll();
if ($data)
{
foreach($data as $row)
{
echo "Row Name:".$row['name']."<br />";
}
} else {
echo "None found";
}

There are two options I would use. Option one, similar to what you are doing:
$STH = $this->database->prepare('SELECT * FROM table WHERE type = :type ');
$STH->execute(array(':type' => $type));
if ($STH->rowCount() > 0)
while($row = $STH->fetch(PDO::FETCH_ASSOC)){
echo "Row Name:".$row['name']."<br />";
}
} else {
echo "None found";
}
Note that the PHP manual for PDOStatement::rowCount() warns that this might not return the number of rows from a SELECT query on all database types.
What I actually do, is separating the SQL logic from the display, usually by using the template engine Smarty. In that case you have two separate files:
PHP:
$STH = $this->database->prepare('SELECT * FROM table WHERE type = :type ');
$STH->execute(array(':type' => $type));
$objects = $STH->fetchAll();
$template->assign("objects", $objects); // this assigns the array with all returned rows to the template.
Smarty Template:
{foreach from=$objects item=object}
Row Name: {$object.name}<br />
{foreachelse}
None found
{/foreach}
This will basically output the same, with separated logic.
Both methods will prevent doing unnessecary queries or doing the same query twice.

I would do it this way, if anyone sees anything wrong please point it out:
I prefer foreach because I can access the field name
I like to get used to use count() instead of rowCount() because rowCount() can return 2 for an updated row, even if this query is a select, I feel like I could get used to it and have mistakes.
I like to check if it returns false to debug. Normally I do a one-liner for this, so I can comment it out or remove it easily on production
function myQuery(){
$STH = $this->database->prepare('SELECT * FROM table WHERE type = :type ');
$STH->execute(array(':type' => $type));
$data = $STH->fetchAll(PDO::FETCH_ASSOC);
if($data === false) return "SQL query Error"; // or a method/function that returns the actual error code/message";
if($data){
foreach($data as $k => $v) echo "$k: $v<br>"; // I actually prefer to return the array $data and handle the info elsewhere
} else {
"None Found";
}
}

Related

Switch Statement based on if variables in two tables match

So I have a switch statement that I want to display one form or another based on if the id from one table has the matching foreign key in another table.
So far what I have tried is nesting one while statement into another which isn't working.
$subresult = mysqli_query($con,"SELECT * FROM tags GROUP BY tag");
$subresult2 = mysqli_query($con,"SELECT * FROM tag_subscribe WHERE uid = $uid");
while ($row = mysqli_fetch_array($subresult)) {
$tid = $row['tid'];
while ($row2 = mysqli_fetch_array($subresult2)) {
$tid2 = $row2['tid'];
}
if ($tid2 == $tid) {
$subbedup = 'yes';
} else {
$subbedup = 'no';
}
switch ($subbedup) {
case ('yes'):
echo "alternate html form goes here because $tid2 == $tid";
break;
case ('no'):
echo "html form goes here";
break;
}
}
So when this code is run, it only returns switch "no" except it will return one switch "yes" which just happens to be the last record of the second table that contains the foreign key. When I think about it, that makes sense as it will just keep running through this loop until it runs out of records in the table. So I spent about six minutes getting to this point and I have spent the last 6 hours trying to get it to work correctly without any luck.
So once again, fine people at SO, save me! Please and Thank you :)
So my question is: How would this be done correctly?
I'm not exactly sure of your database structure, so I'll improvise.
Given these sample tables and columns:
tags
id name
tag_subscriptions
user_id tag_id
The query below will loop through all tags. Each tag includes a subscribed column set to either "yes" or "no", depending on whether the current user is subscribed to that particular tag.
$sql="SELECT t.`id`, t.`name`, IF (ISNULL(ts.`tag_id`),'no','yes') AS `subscribed`
FROM `tags` t
LEFT JOIN `tag_subscriptions` ts ON (ts.`user_id`=$uid AND ts.`tag_id`=t.`id`)
WHERE 1;"
Then loop through all tags:
$q=mysql_query($sql) or die(mysql_error());
while ($row=mysql_fetch_assoc($q)) {
switch ($row['subscribed']) {
case 'yes'
// user is subscribed to this tag
break;
default:
// user is not subscribed to this tag
}
}
I think (hope) this is closer to what you're looking for.
http://sqlfiddle.com/#!2/58684/1/0
Sorry for using PDO as thats what i know, you can convert the idea to MYSQLi im sure.
$db = new PDO($hostname,$username,$password);
$arraySubTags = array();
$query = "SELECT tagID FROM tag_subscribe WHERE uid = :uid";
$statement = $db->prepare($query);
$statement->bindValue(':uid', $uid);
$statement->execute();
$subscribedTags = $statement->fetchAll(PDO::FETCH_ASSOC); //or loop with a while using fetch()
$statement->closeCursor();
foreach($subscribedTags as $sTag)
{
array_push($arraySubTags,$sTag);
}
$query = "SELECT * FROM tags GROUP BY tag";
$statement = $db->prepare($query);
$statement->execute();
$allTags = $statement->fetchAll(PDO::FETCH_ASSOC); //or loop with a while using fetch()
$statement->closeCursor();
foreach($allTags as $tag)
{
if(in_array($tag['tagID'], $arraySubTags))
{
echo "person is subscribed";
}else{ echo "person not subscribed";}
}
This code just checks whether the last tag checked is subscribed to - to check for each one you need to move the switch statement into the outer while loop, after the if..else bit that sets the $subbedup variable for the current tag.
Or you could make $subbedup an array, indexed by the tag id, if you need to keep the switch separate for some reason.

conditional arrays

I'm trying to code an array that displays a certain set of products depending on the gender of the logged in user. The arrays not really the problem but the parts where I'm going to have to check the database then create the conditional statement from the results is the main problem i think.
Here is my code:
<?php
include"config.php" or die "cannot connect to server";
$gender=$_POST['gender'];
$qry ="SELECT * FROM server WHERE gender ='$gender'";
$result = mysql_query($qry);
$productdetails;
$productdetails1["Product1"] = "£8";
$productdetails1["Product2"] = "£6";
$productdetails1["Product3"] = "£5";
$productdetails1["Product4"] = "£6";
$productdetails1["Product5"] = "£4";
$productdetails2["Product6"] = "£8";
$productdetails2["Product7"] = "£6";
$productdetails2["Product8"] = "£5";
$productdetails2["Product9"] = "£6";
$productdetails2["Product10"] = "£4";
if (mysql_num_rows($result) = 1) {
foreach( $productdetails1 as $key => $value){
echo "Product: $key, Price: $value <br />";
}
}
else {
foreach( $productdetails2 as $key => $value) {
echo "Product: $key, Price: $value <br />";
}
}
?>
You if statement is wrong. = is an assignment operator, you should use a comparison operator like == or ===
What happens with the current code?
Some tips:
First try echoing $gender, to make sure it is getting through. It is submitted through post, what happens if nothing is being posted? Where is this coming from? You should try to use get instead. This seems like something you'd give someone a link to therefore post doesn't make sense here. You could always have both, and just get post if it exists otherwise use get otherwise default to 'male' or 'female' depending on your audience.
Next, what is your query outputting? It might be empty at this point if gender is not giving anything back. It seems like you are querying for all rows where gender = whatever was passed, but then your if statement is asking was there anything returned? Then all you are doing is going to the arrays, but you shouldn't be doing that you should be outputting what you got from the DB. Assuming you do actually have products in the table called server you should do something like this:
$products = mysql_query("SELECT * FROM server WHERE gender ='$gender");
while($product = mysql_fetch_array($products)){
echo $product['name'] . " " . $product['price']. " " . $product['gender'];
echo "<br />";
}
On that note. You should really call your table something else, like product not just "server" unless by server you mean a table filled with instances of waiters or computer hardware.

php mysqli object search and sort

Using php 5.3 and mysqli I return a result set from a query that just has usernames, something like
$query_username = "SELECT username FROM some_table WHERE param = 1";
$username = $mysqliObject->query($query_username);
while($row_username = $username->fetch_object()){
print "<br>Username: $row_username->username";
}
All fine, but here is my problem, there are repeated usernames, and I don't know which names are going to be in the query before hand, could be bob, sue, james. Or it could be tom, dick, harry, tom. What I need to do is print out each username and how many times it shows up in this object. For very strange reasons I CANNOT use neat stuff like group by and count(*) in the query(don't ask it is truly weird). So my question is, what is the fastest way to loop through the returned object(or associative array if need be) to get each unique name and how many times it appears. Thanks for your help and I apologize if this is a freshman CS question, I'm self taught and always filling in the gaps!
If you really must do it on the PHP side instead of using a GROUP BY clause:
while($row_username = $username->fetch_object())
{
if(isset($usernames[$row_username['username']]))
{
$usernames[$row_username['username']]++;
}
else
{
$usernames[$row_username['username']] = 1;
}
}
asort($usernames);
// use ksort() to sort by username instead of the count
// print out the usernames
foreach($usernames as $username => $count)
{
echo $username . ", count: " . $count;
}
e.g.
$users = array();
while( false!==($row=$result->fetch_array()) ){
if ( isset($users[$row['username']]) ) {
$users[$row['username']] += 1;
}
else {
$users[$row['username']] = 1;
}
}
asort($users);

External looping of the variable

How could I best loop the data through a variable that's nested inside a 'while' loop but it's called outside of it ? Like in this example:
PHP:
$fr_q2 = mysqli_query($connect,"SELECT * FROM friends WHERE username ='".$_SESSION['user']."'
ORDER BY id DESC");
while ($rowsPicFr2 = mysqli_fetch_array($fr_q2)) {
$friends_q2[] = $rowsPicFr2['added_friend'];
$frn[] = $rowsPicFr2['added_friend'];
$frn2 = $rowsPicFr2['added_friend'];
}
$rowscheck = mysqli_num_rows($fr_q2);
for ($i=0; $i<$rowscheck; $i++)
HTML:
YES
So I need to pass $frn[$i] into a remdata() function - but the $frn[$i] needs to loop....All I get is a string of all ids from 'friend_added' in $frn[$i]....Thanks.
Once this HTML is sent to the browser, it won't loop anymore. It will just be HTML. So what you need to do, is either have it loop in JavaScript or simply echo each remdata():
YES
This solution is only really an option if you have very few elements in $frn.
Your question is confusing; I think you need the loop for the HTML output
<?php foreach ($frn as $i => $friend_id){ ?>
YES
<?php } ?>
Based on your comment in toon81's answer, it seems like you're having a problem dealing with several loops in your output that deal with the same result set in your database query. I'm not sure. I'd suggest in the future that you try to make your question easier to follow. For instance, do we need to know it's a social networking app? Your variable names aren't inherently easy to understand; what's the difference between $frn and $frn2? Presumably that's 'friend', but I keep reading it as 'fern'. You also only provided one line of your output, but your problem seems related to it's interaction with other output. Your code is cut off -- the rowscheck loop doesn't have a definition.
That said, this is a high level suggestion of how I'd handle your work differently. Data preparation:
$connection = ...;
$user = $_SESSION['user'];
$sql = "
SELECT added_friend
FROM friends
WHERE username = '$user'
ORDER BY id DESC
";
$response = mysql_query($sql, $connection);
$added_friends = array();
while ($row = mysql_fetch_object($response)){
$added_friends[] = $row->added_friend;
}
Ouput handling:
// With one loop if the markup can be ouput all at once.
foreach ($added_friends as $friend){
// Your 'friends_q2', whatever that is.
echo "Friends_q2: $friend";
// Your 'frn2' output, whatever that is.
echo "Frn2: $friend";
// Your 'frn' output.
echo "YES";
}
// ...or multiple loops if it can't.
foreach ($added_friends as $friend){
// Your 'friends_q2', whatever that is.
echo "Friends_q2: $friend";
}
foreach ($added_friends as $friend){
// Your 'frn2' output, whatever that is.
echo "Frn2: $friend";
}
foreach ($added_friends as $friend){
// Your 'frn' output.
echo "YES";
}
In any case, you're handling the same ID three times in different lists and in different ways. I'm not at all sure why. Is this what you're asking for help with?

When listing information from a database using php and mysql how would you make the first row look different to the rest?

Basically I have articles in my database and I want to alter the way the first record displays. I want the lastest (Posted) article to be the focus and the older article just to list, (see F1.com). I need to know how to get the first of my values in the array and get it to display differently but I am not sure how to do this, I can do it so all rows display the same just not how to alter the first row. I also need to know how to tell the rest of the rows to display the same afterwards im guessing you use an if statement there and before that some kind of count for the rows.
Current code:
$result = mysql_query("SELECT * FROM dbArticle WHERE userID='".$_SESSION["**"]."' ORDER BY timestamp DESC");
while($row = mysql_fetch_array($result))
{
echo "<h2 class=\"heading1\">". $row['title'] ."</h2>";
echo "By: ".$row['username']." Type: ".$row['type']." Posted: ".$row['timestamp']."
$body = $row['body'];
echo "<br/><p>";
echo substr("$body",0,260);
echo "...<span class=\"tool\"><a class=\"blue\" href=\"index.php?pageContent=readArticle&id=".$row['id']."\">Read More</a></span></p><hr/>";
}
mysql_close($con);
Ok I have taken Luke Dennis's code and tried to test it, but I am getting this error: Warning: Invalid argument supplied for foreach() this is the line of the foreach statment. Something that has just come to mind is that I will only want 5 or so of the older articles to display. This is what I have thats creating the error:
<? $con = mysql_connect("localhost","****","***");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db("******", $con);
$result = mysql_query("SELECT * FROM dbArticle ORDER BY timestamp DESC");
$first = true;
foreach($result as $row){
if($first)
{
echo"".$row['title']."";
echo"this is the headline";
$first = false;
}
else
{
echo"".$row['title']."";
}
}
?>
Do I need to add mysql_fetch_array somewhere to set the array up?
I would just iterate through the results and apply a css class to the first entry:
$first = true;
while ($row = mysql_fetch_assoc($result)) {
$cssClass = '';
if ($first) {
$cssClass = 'highlight';
}
echo '<p class="' . $cssClass . '">' . $row['text'] . '</p>';
$first = false;
}
It's a bit crude, but I often hard-code a variable to designate the first run through a loop. So something like:
$first = true;
foreach($list_of_items as $item)
{
if($first)
{
// Do some stuff
$first = false;
}
else
{
// Do some other stuff
}
}
A simple if statement when looping through your results will usually do the trick. You can use a boolean to indicate if you've output the first row of results or now. If you haven't then give it a particular style and then set the boolean to true. Then all subsequent rows get a different style.
All of the above are correct. Luke Dennis' post is of course fleshed-out a bit more.
As Brian Fisher said, add some CSS styling to the first link when you encounter it per Luke's post.
I took a look at the article list on the F1 website. Pretty well constructed site - "One would expect that." :-)
Anyway, the article listings are contained within a two row table (summary="Latest Headlines") in descending order (newest first).
Just place a class in the second column (<td class="first-news-article">). Then add the class name and appropriate styling values in the css file - probably your' modules.css. There's already quite a few class values associated with articles in that file, so you may be able to just use an existing value.
That should be about it - other than actually doing it!
By the way, judging by the quality of the underlying html, I'm assuming there's already an "article list emitter." Just find that emitter and place the appropriate conditional to test for the first record.
Darrell
I just noted your code addition. I assume that you were showing the F1 site as an example. Anyway, I think you're on your way.
I presume you have some code that loops through your resultset and prints them into the page? Could you paste this code in, and that might give us a starting point to help you.
I don't know PHP, so I'll pseudocode it in Perl. I wouldn't do it like this:
my $row_num = 0;
for my $row ($query->next) {
$row_num++;
if( $row_num == 1 ) {
...format the first row...
}
else {
...format everything else...
}
}
The if statement inside the loop unnecessarily clutters the loop logic. It's not a performance issue, it's a code readability and maintainability issue. That sort of thing just BEGS for a bug. Take advantage of the fact that it's the first thing in the array. It's two different things, do them in two different pieces of code.
my $first = $query->next;
...format $first...
for my $row ($query->next) {
...format the row...
}
Of course, you must make the first row stand out by using tags.
I'd use array_shift():
$result = mysql_fetch_assoc($resultFromSql); // <- edit
$first = array_shift($result);
echo '<h1>'.$first['title'].'</h1>';
foreach ($result as $row) {
echo '<h2>'.$row['title'].'</h2>';
}
The best way to do this is to put a fetch statement prior to the while loop.
Putting a test inside the while loop that is only true for one iteration can be a waste of time for a result of millions of rows.

Categories