I'm trying to use mysqli prepared statements with a LIKE statement query and wildcard operators. After debugging be sprinkling echo statements throughout the code, I can see that my while statement is not executing. Can you see what I'm doing wrong here?
This is my first time asking on this forum, so I apologize if this isn't a good question; I've spent 6 hours trying to get the prepared statement section of my code to work and I can't find any threads addressing my question that don't go completely over my head (e.g. How can I put the results of a MySQLi prepared statement into an associative array?). The two closest I found were:
Using wildcards in prepared statement - MySQLi
and Combine PHP prepared statments with LIKE.
Here's the relevant excerpt of my code:
//set up and execute queries
$titleQuery = "SELECT keyframeurl, videoid, title, creationyear, sound, color,
duration, genre FROM openvideo WHERE title LIKE CONCAT ('%', ?, '%')
ORDER BY $order";
if($stmt = mysqli_prepare($db, $titleQuery)){
//bind parameters
mysqli_stmt_bind_param($stmt, 's', $trimmedTitleSearch);
//execute query
mysqli_stmt_execute($stmt);
//bind results
mysqli_stmt_bind_result($stmt, $keyframeurl, $videoid, $title, $year, $sound,
$color, $duration, $genre);
//store result so num rows can be counted
$result = mysqli_stmt_store_result($stmt);
//fetch results
while ($row = mysqli_fetch_array($result, MYSQL_ASSOC)) {
echo "<tr>";
echo "<td>".$row['videoid']."</td>";
echo "<td>" . $row['title'] . "</td>";
echo "<td>" . $row['year'] . "</td>";
echo "<td>" . $row['sound'] . "</td>";
echo "<td>" . $row['color'] . "</td>";
echo "<td>" . $row['duration'] . "</td>";
echo "<td>" . $row['genre'] . "</td>";
echo "</tr>";
}
}
else {
// Error
printf("Prepared Statement Error: %s\n", $db->error);
}
Thanks for any advice!
You are mixing 2 styles of fetching results. Either use ugly bind_result way (and get your data using fetch() then), or try to use get_result() - so, you'll be able to use fetch_array() (not guaranteed though).
Anyway, just get rid of all that mess and use PDO.
$titleQuery = "SELECT keyframeurl, videoid, title, creationyear, sound, color,
duration, genre FROM openvideo WHERE title LIKE CONCAT ('%', ?, '%')
ORDER BY $order";
$stmt = $pdo->prepare($titleQuery);
$stmt->execute(array($trimmedTitleSearch));
$data = $stmt->fetchAll();
foreach ($data as $row ) {
// the rest is the same as yours
I hope you properly sanitized your $order variable. The best way would be apparently to add it via placeholder, so, you will need a library that allows it, SafeMysql for example:
$sql = "SELECT * FROM openvideo WHERE title LIKE CONCAT ?s ORDER BY ?n";
$data = $db->getAll($sql,"%$trimmedTitleSearch%", $order);
foreach ($data as $row ) {
// the rest is the same as yours
Note the amount of code and compare with that load of raw API calls you are using at the moment
#Your Common Sense - You see there is no need for allenbell_nc to "... just get rid of all that mess and use PDO" like you inferred. Just because you've got an erroneous impression of the mysqli extension with prepared statements does not mean that others should do away with it at the slightest hint of trouble as opposed to carrying out indepth but painful research. After all, this is what stackoverflow is about, isn't it? well researched answers..
#allenbell_nc - To answer your question, I do not think your problem has to do with your use of Wild cards and stuff. The problem lies in your line of code where you attempt to make use of
mysqli_fetch_array(). This will very likely throw an error complaining about parameter 1 ($result), because mysqli_stmt_store_result() is used in cases where you want to later find the number of rows returned from a query, so it therefore returns boolean(true or false) and NOT a result set.
INSTEAD, use mysqli_stmt_bind_result() after the execute call then call mysqli_stmt_fetch()in the while condition, before finally using array_push() in the while condition body which will help you to store and subsequently echo out the contents of your ASSOCIATIVE array.
A QUICK EXAMPLE(Idea provided by Mr. Carson McDonald # [http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/][1]):
...
$comments = array();
$comment_stmt->bind_param('i', $post_id);
if ($comment_stmt->execute())
{
$comment_stmt->bind_result($user_id);
while ($comment_stmt->fetch())
{
array_push($comments, array('user_id' => $user_id));
//Now you can go ahead and make use of $comments however you want, whether as stored in an $_SESSION array variable or just to echo it out! As Demonstrated Below:
$_SESSION = $comments;
echo $_SESSION['user_id'];
}
}
...
Hope that helps, goodluck to you - a first time asker, as this is also my first time answer - on your project.
echo "<td>" . $row['color'] . "</td>";
echo "<td>" . $row['duration'] . "</td>";
echo "<td>" . $row['genre'] . "</td>";
echo "</tr>"; while ($row = mysqli_fetch_array($stmt, MYSQL_ASSOC))
Or while ($row = mysqli_stmt_fetch($stmt))
Edit:
mysqli_stmt_bind_result($stmt, $keyframeurl, $videoid, $title, $year, $sound,
$color, $duration, $genre);
//store result so num rows can be counted
$result = mysqli_stmt_store_result($stmt);
//fetch results
while (mysqli_stmt_fetch($stmt)) {
echo "<tr>";
echo "<td>".$videoid."</td>";
echo "<td>" . $title . "</td>";
echo "<td>" . $year . "</td>";
echo "<td>" . $sound . "</td>";
echo "<td>" . $color . "</td>";
echo "<td>" . $duration . "</td>";
echo "<td>" . $genre. "</td>";
echo "</tr>";
}
Related
I have two MySQL tables with number of columns. The table structure is given below,
1.pictures
postedON
caption
imageName
thumbName
imageLocation
thumbLocation
2.Videos
postedOn
category
Link
I am using the folowing PHP function to fetch data from DB using a select command.
function select($table){
if($this->db_connection){
$query = 'SELECT * FROM '. $table;
$result = mysqli_query($this->db_connection,$query) or die($this->db_connection->error);
//print_r($result);
//echo "Affected rows: " . mysqli_affected_rows($this->db_connection);
//var_dump($result);
echo "<table>";
echo "<tr>";
echo "<th>Date Posted</th>";
echo "<th>Category</th>";
echo "<th>Link</th>";
echo "</tr>";
while($row = $result->fetch_assoc()){
echo "<tr>";
echo "<td>" . $row['postedOn'] . "</td>";
echo "<td>".$row['category']. "</td>";
echo "<td>" . $row['link'] . "</td>";
echo "</tr>";
}
echo "</table>";
}else{
echo "db_connection is = " . $this->db_connection;
}
}
}
The problem with this function as you can see, it can only serve only one table and not dynamic. Can someone please explain the way to dynamically fetch data from different table with different number of columns using only one PHP function? Thanks
Try using mysqli_fetch_fields()
<?php
function select($table){
if($this->db_connection){
$query = 'SELECT * FROM '. $table;
$result = mysqli_query($this->db_connection,$query) or die($this->db_connection->error);
$fieldinfo = mysqli_fetch_fields($result);
echo "<table>";
echo "<tr>";
foreach ($fieldinfo as $val)
{
echo "<th>".$val->name."</th>";
}
echo "</tr>";
while($row = $result->fetch_assoc()){
echo "<tr>";
foreach ($fieldinfo as $val)
{
echo "<td>" . $row[$val->orgname] . "</td>";
}
echo "</tr>";
}
echo "</table>";
}else{
echo "db_connection is = " . $this->db_connection;
}
}
It could be hard to explain given all the wrong premises you approach is based on, but I'll try.
First of all, you have to understand that a query like SELECT * FROM table has a very little use, next to none. Most of time you are always have some WHERE or at least LIMIT clause. So, such a function will have no use at all.
Next, you have to learn by heart that database interaction should never be intermixed with any output stuff like HTML.
Given these two premises above, your function should accept a fill qualified query as a parameter and return an array with data as a result.
For which array, in turn you can write a helper function to display its contents in the form of HTML table.
But again, such a generalized output function will be of little use as well, because, as you can see from your own example, different fields will need different formatting. So it's better to write output each time by hand.
I have working code but I seem to have introduced a bad bottleneck to my code. Before I go into detail, let me give you some background on what this code does. I have a table generated in PHP/HTML that shows a list of parts that are in a database (149 parts). The user can select a quantity for that item and click "Add To Cart" which all works fine.
However, I tried to spruce things up a bit by adding in a feature that only allows the user to select the quantity we actually have on hand for this product (before add this feature I previously had the max value hard coded at 100).
Now this does work, but it is TERRIBLY slow (50 seconds to load the page). However, before implementing this the page loaded immediately without a problem, therefore this is definitely a problem with the getPartQuantityOnHand() function.
I think the problem is that for every part (149) I am calling this function that then has to reconnect to the database to find the correct quantity. Does anyone see a way I can improve this? FYI, the parts are hosted on a completely separate database from where the quantity on hand for these parts are hosted.
I have left some parts out of the php code since it was unnecessary bulk for this post, I know that I am missing parts here.
getParts.php
<?php
$partList = $_SESSION['controller']->getParts();
foreach ($partList as $part) {
//Call to get the current quantity on hand for this specific part
//THIS IS WHEN THE PROBLEM WAS INTRODUCED
$quantityOnHand = $_SESSION['controller']->getPartQuantityOnHand($part->number);
//Only display this part if we have at least one on hand
if ($quantityOnHand > 0)
{
echo "<tr>";
echo "<td>" . $part->number . "</td>";
echo "<td>" . $part->description . "</td>";
echo "<td>$" . $part->price . "</td>";
echo "<td>";
echo '<input name="qty'. $part->number .'" type="number" value="1" min="1" max="' . $quantityOnHand .'"/>';
echo "</td>";
echo "</form>";
echo "</td>";
echo "</tr>";
}
}
echo "</table>";
echo "</div>";
?>
getPartQuantityOnHand()
public function getPartQuantityOnHand($partNum) {
$conn = $this->connect();
$sql = "select Quantity from ReceivingInfo where PartNumber = '$partNum'";
$stmt = $conn->prepare($sql);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$quantityOnHand = $row['Quantity'];
return $quantityOnHand;
}
Thanks for any help you can provide!
As the two databases are separate you have two options:
Determine the part numbers before you loop and generate your HTML, then query all at the same time with an IN clause:
SELECT ... WHERE PartNumber IN (1, 2, 3, 4);
Select all stock levels for your parts when you first call getPartQuantityOnHand and store that:
public function getPartQuantityOnHand($partNum) {
if(!$this->partQuantitiesOnHand) {
$conn = $this->connect();
$sql = "select Quantity, PartNumber from ReceivingInfo";
$stmt = $conn->prepare($sql);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$this->partQuantitiesOnHand = array_column($rows, 'Quantity', 'PartNumber');
}
return $this->partQuantitiesOnHand[$partNum] ?? null;
}
The disadvantage of option 2 is that if you have many more parts than are listed on a single page, it will never perform as well as option 1.
So, instead of doing a 149 different SQL queries, just do one query that brings back all the Quantity values you need.
ex.
SELECT Quantity FROM ReceivingInfo WHERE PartNumber IN ($listOfPartNums)
of course you will have to build the string $listOfPartNums
Or, if you really are always bringing back the full list of them, you can exclude the IN clause and don't need to worry about generating the string.
You'll have to change your function a bit to store the partnum/quantity pairs in an associative array.
Then in your getParts.php get the Quantity like this:
<?php
$partList = $_SESSION['controller']->getParts();
$quantityList = getQtyList(); // return assoc array of partnum/qty pairs
foreach ($partList as $part) {
//Call to get the current quantity on hand for this specific part
//THIS IS WHEN THE PROBLEM WAS INTRODUCED
$quantityOnHand = $quantityList[$part->number];
//Only display this part if we have at least one on hand
if ($quantityOnHand > 0)
{
echo "<tr>";
echo "<td>" . $part->number . "</td>";
echo "<td>" . $part->description . "</td>";
echo "<td>$" . $part->price . "</td>";
echo "<td>";
echo '<input name="qty'. $part->number .'" type="number" value="1" min="1" max="' . $quantityOnHand .'"/>';
echo "</td>";
echo "</form>";
echo "</td>";
echo "</tr>";
}
}
echo "</table>";
echo "</div>";
?>
Apologies, I can't think of how to phrase this better (which has the additional problem of making it more difficult to research an answer, too). Suggestions for editing terms for clarity are more than welcome.
I am running two queries on a table in my database. The first simply returns all results within certain constraints imposed by the user - I'm echoing this out to a table with no problems at all. The second returns a COUNT and a SUM AS things, which I am having trouble accessing and echoing to the screen.
First Query -
$results = $connection->query(" SELECT `Date`, `Test`, `Errors` ... ");
while($result = $results->fetch_assoc())
{
echo "<tr>";
echo "<td>" . $result['Date'] . "</td>";
echo "<td>" . $result['Test'] . "</td>";
echo "<td>" . $result['Errors'] . "</td>";
echo "</tr>";
}
This works perfectly well. As expected, it echos out a table with the results in each row.
Second Query -
$totals = $connection-query("SELECT COUNT(*) AS Tests, SUM(`Errors`) AS TotalErrors ... ");
echo "<th>Total Tests</th>";
echo "<td>" . $totals['Tests'] . "</td>";
echo "<th>Total Errors</th>";
echo "<td>" . $totals['TotalErrors'] . "</td>";
I cannot seem to access the values in the second query to echo them to the screen.
I have tried using var_dump to ensure the query is returning results correctly, and it is. If I use var_dump($totals->fetch_assoc()); it will display array(2) { ["Tests"]=> string(2) "33" ["TotalErrors"]=> string(1) "9" }, as expected.
I'm not sure where I'm going wrong, looking at my syntax, it seems the same as when I access the values from the first query, but I'm not sure if it should be different because I am returning values AS rather than looking at field names.
Try
$results = $connection->query("SELECT COUNT(*) AS Tests, SUM(`Errors`) AS TotalErrors ... ");
$totals = $results->fetch_assoc();
echo "<th>Total Tests</th>;
echo "<td>" . $totals['Tests'] . "</td>";
echo "<th>Total Errors</th>";
echo "<td>" . $totals['TotalErrors'] . "</td>";
This question already has answers here:
How can I use PDO to fetch a results array in PHP?
(2 answers)
Closed 2 years ago.
I have a php script that selects data via mysql_, however recently I have been reading that PDO is the way to go and that mysql_ is becoming depreciated. Now I am converting that script to PDO.
My question is though, I am not using $_POST to select. I just want to select the entire table with all of its data so I enter this query :
$query = $dbh->prepare("SELECT * FROM students");
$query->execute();
$result = $query->fetchall(); // or you can just $result = $query as hakre proposed!
so then like I did with my old depreciated mysql_ version of the script I used the echo to echo a table with the data in it.
echo
"<table border='2'>
<tr>
<th>ID</th>
<th>A Number</th>
<th>First Name</th>
<th>Last Name</th>
<th>Why</th>
<th>Comments</th>
<th>Signintime</th>
</tr>"
;
foreach($result as $row)
{
echo "<tr>";
echo "<td>" . $row['id'] . "</td>";
echo "<td>" .$row['anum'] . " </td>";
echo "<td>" . $row['first'] . "</td>";
echo "<td>" . $row['last'] . "</td>";
echo "<td>" . $row['why'] . "</td>";
echo "<td>" . $row['comments'] . "</td>";
echo "<td>" . $row['signintime'] . "</td>";
echo "<td> <input type=\"button\" value=\"Start Session\"onClick=\accept.php?id=" . $row['id'] . "&start=true></td>";
}
echo "</tr>";
echo "</table>";
now using this, I can not get a single output to my table.
My question is am I missing something from my select statements? Or am I not fetching any rows? Also I the connection settings set in another script called connect.php that is required by init.php (at the top of all of my pages)
Edit : 1
Edited the code so it now works, also adding a picture to show others how it should look! Hopefully some one can put this to some sort of use!
You are doing too much actually:
$query = $dbh->prepare("SELECT * FROM students");
$query->execute();
$result = $dbh->query($query);
The problematic line is:
$result = $dbh->query($query);
Check with http://php.net/pdo.query, the parameter is a string, actually the SQL string you already use above, not the result value of a PDO::prepare() call.
For your simple query you can just do:
$result = $dbh->query("SELECT * FROM students");
Or if you like to prepare:
$query = $dbh->prepare("SELECT * FROM students");
$query->execute();
$result = $query;
The later is some boilerplate if you want to insert variables into the query, that's why you prepare it.
The next problem is with the foreach line:
foreach($result as $row);
You are terminating the loop immediately because of the semicolon ; at the end. Remove that semicolon so that the following angle-bracketed code-block becomes the body of the foreach-loop.
Your code is wrong:
$query = $dbh->prepare("SELECT * FROM students");
$query->execute();
$result = $dbh->query($query);
After executing a prepared statement, you can just call fetchAll() on it:
$query = $dbh->prepare("SELECT * FROM students");
$query->execute();
$result = $query->fetchAll();
The rest of your code will work fine once you remove the semicolon after the foreach.
I'm trying to create a table with links that return a 'mf_id' value and its corresponding 'Manufacturer' value. I can do one at a time, but when I try to combine the two, problems begin to crop up. Here's what I have so far:
while($row = mysql_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['Manufacturer'] . "</td>";
echo "</tr>";
}
and the other page:
$numb = $_GET['mf_id'];
$name = $_GET['Manufacturer'];
echo "<h1>$name</h1>";
$result=mysql_query("select * from Products where mf_id=$numb");
Thanks in advance!
Because you never pass Manufacturer through your querystring, the second page doesn't have access to it via GET. Also, for validity purposes, your querystring values should be passed through urlencode().
This line:
echo "<td>" . $row['Manufacturer'] . "</td>";
Should be:
echo "<td>" . $row['Manufacturer'] . "</td>";
Please Note: It may not help answer your question, but you should stop using mysql_* functions. They're being deprecated. Instead use PDO (supported as of PHP 5.1) or mysqli (supported as of PHP 4.1). If you're not sure which one to use, read this article.
UPDATE:
Per meagar's advice I learned about http_build_query(). This is definitely the way to go when writing querystrings to URLs:
$data = array('mf_id' => $row['mf_id'], 'Manufacturer' => $row['Manufacturer']);
echo "<td><a href='list.php?" . http_build_query($data) . "'>" . $row['Manufacturer'] . "</a></td>";
This doesn't make any sense at all: $row['mf_id'&&'Manufacturer']. That is not how you access two elements of an array. You're combining two strings with &&, yielding boolean true, and attempting to access $row[true]. You can't access an array that way.
If you want to use both items, you need to access them individually:
$row['mf_id'] . $row['Manufacturer']
If you want to build a query string containing these two values, you should use http_build_query which will take care of URL-encoding your data:
$query = http_build_query(array('mf_id' => $row['mf_id'], 'manufacturer' => $row['Manufacturer']));
echo '<td>' . $row['Manufacturer'] . '</td>';
Note that, if you actually just select the fields you need, you don't have to explicitly specify them in the arguments to http_build_query. If your $row already contains only mf_id and manufacturer, it would be enough to use
$query = http_build_query($row);
You're only passing mf_id into the page, you are not passing Manufacturer.
Edit (as you've changed your code)
Change:
while($row = mysql_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['Manufacturer'] . "</td>";
echo "</tr>";
}
To:
while($row = mysql_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['Manufacturer'] . "</td>";
echo "</tr>";
}