This is probably very simple and I apologise in advance, if its a silly question.
I working on a web page, and I have a database which I am calling data from that in the most part is working fine, however I need to use a mysql query which isn't just select all or specific fields:
$sqlAuthors = ('SELECT authors.Forename, authors.Surname
FROM authors
LEFT JOIN BookAuthor ON authors.AuthorID = BookAuthor.AuthorID
LEFT JOIN Books ON BookAuthor.BookID = Books.BookID
WHERE Books.BookID="'.$loopnumber.'"');
This query I've tested on the database and works fine, the query will in cases have multiple records.
The code I have is:
$conn = mysql_connect('localhost', 'root', '');
mysql_select_db('library', $conn);
$sql = "SELECT * FROM Books";
$result = mysql_query($sql, $conn) or die("Can't run query");
$loopnumber = 1;
if (mysql_num_rows($result) ==0 ){echo "No Books have been found";}
else
{
while ($row = mysql_fetch_object($result))
{
$sqlAuthors = ('SELECT authors.Forename, authors.Surname
FROM authors
LEFT JOIN BookAuthor ON authors.AuthorID = BookAuthor.AuthorID
LEFT JOIN Books ON BookAuthor.BookID = Books.BookID
WHERE Books.BookID="'.$loopnumber.'"');
$Authors = mysql_query($sqlAuthors,$conn);
echo'<article class="topContent">
<header><h2>'.$row->BookName.'</h2></header>
<footer><p class="post-info">Written By: '.print_r(mysql_fetch_array($Authors)).' '.$Authors[Forename].'</p></footer>
The last line being where I try to output the values.
However if I have $Authors[Forename] it gives me the error:
Notice: Use of undefined constant Forename - assumed 'Forename' in I:\xamptest\htdocs\Library\index.php on line 51
Even though I have:
print_r(mysql_fetch_array($Authors))
and that works and outputs:
Array ( [0] => J.R.R [Forename] => J.R.R [1] => Tolkien [Surname] => Tolkien )
So it should recognise it, also the array print out appears no where near the line it is meant to be in, not even n the same DIV, but where it should be there is just "1" echoed.
If I try:
'.$Authors[1].'
Then I get no errors but nothing is printed out.
No idea if it'll help but heres what the section of the site page looks like: http://i.imgur.com/mMEQke5.png
Should be printing the Authors name where it says "Written By:" but instead just has "1".
I'm still fairly new to this, but it has me stumped.
All help much appreciated -Tom
A great amount of your code needs cleaning up, but we can focus on that after fixing the problem at hand.
$Authors = mysql_query($sqlAuthors,$conn);
That returns a result set from the query, no issues there (besides mysql_query() being deprecated, but we can talk about that later :)
Now, let's assume for a moment a book could have multiple authors, as is the case in many reference texts, and since you're already looping over all the books you are returning. Let's just go ahead and put our new result set inside a similar loop to what you've already done:
while($author_row = mysql_fetch_array($Authors)){
echo $author_row['Forename'];
}
The code above is totally incomplete, but hopefully it's a starting point for what you need to do.
Just a few items of note: mysql_*() is deprecated, so switch over to using the mysqli API as soon as you can! People will start yelling at you here if you don't ;)
Make sure you're using prepared statements after cleaning up for security. Easy to protect against if you do it right, but sql injections are nasty.
Related
I have the below code:
$result = $conn->query($sql)->fetch(PDO::FETCH_COLUMN);
$myArray = explode(',', $result);
$sql = ("SELECT username,status,location FROM Users WHERE username IN ('" . implode("','",$myArray) . "') AND status > 0 ORDER BY username");
$data = $conn->query($sql)->fetchAll(PDO::FETCH_OBJ);
print_r ($data);
FYI $result is "kk,bb"
The output is as follows:
Array ( [0] => stdClass Object ( [username] => bb [status] => 1 [location] => ) [1] => stdClass Object ( [username] => kk [status] => 1 [location] => ) )
But I really just want to see a two-dimensional array without it saying stdClass Object in front of each item. I tried different kinds of fetching, but this is the only one I could make work. I know there are functions for converting object to array, but I´d rather if there´s a way to do this right, if there is such a thing, in the pasted code.
I hope someone can help, thanks in advance!
You requested objects by using PDO::FETCH_OBJECT. If you want to fetch as arrays, use another fetch type:
$data = $conn->query($sql)->fetchAll(PDO::FETCH_ASSOC);
or
$data = $conn->query($sql)->fetchAll(PDO::FETCH_NUM);
The other choices are documented here: http://php.net/manual/en/pdostatement.fetch.php
By the way, why do you run one query to get the values in $myArray, then use these in an SQL-injection vulnerable way in a second query? Wouldn't it be easier to use a JOIN?
Re your comment:
Fist of all, I strongly urge you to code safely the first time, don't rely on "going back later." You probably won't have time to go back later, because once the code seems to work "good enough," the temptation is to go live immediately. Even if you do go back later to fix security flaws, you might miss one. Either way, your development process creates a high risk that you will go live with insecure code.
A piece of old advice from famous computer scientist Andrew S. Tanenbaum: "Security, like correctness, is not an add-on feature."
Now about joins. If you use SQL, you should understand how to do a joins. Not knowing joins in SQL is like thinking you know PHP even though you don't know how to use foreach(). Technically, you can still do some stuff, but you're missing out on a major part of the language.
Joins allow you to query multiple tables in one query, and find rows from each table that match.
In your example, I don't know your first query, but I'll take a guess that it queries some other table to get a list of usernames. Maybe it queries a list of users who are recipients of a email, like this:
SELECT username FROM EmailRecipients WHERE EmailId = 1234
Then your code uses the result list of usernames in a second query. But you can do it all in one query:
SELECT Users.username, Users.status, Users.location
FROM Users JOIN EmailRecipients
ON Users.username = EmailRecipients.username
WHERE EmailRecipients.EmailId = 1234
The SQL engine searches the EmailRecipients table and say it finds six rows for the recipients of email 1234. For each of the six rows, it looks up the corresponding row in the Users table with the matching username. Then it uses columns from those rows in the SELECT result.
When you join tables, there's a possibility both tables might have some columns with the same name. That's why I showed column named by qualifying them as belonging to one table or the other. This removes the ambiguity about which table you meant in each case.
It doesn't matter which table you list first. This type of join is the same left-to-right as it is right-to-level, like some expressions in algebra (2 + 4 is the same as 4 + 2, etc.). MySQL automatically figures out which table is most efficient to read first.
There's more to learn about joins, but that should get you started. I suggest you pick up a tutorial or a book on SQL sometime.
Re your comment:
Where is the "SQL-injection vulnerable way in a second query"?
In your code:
$result = $conn->query($sql)->fetch(PDO::FETCH_COLUMN);
$myArray = explode(',', $result);
$sql = ("SELECT username,status,location FROM Users WHERE username IN ('" . implode("','",$myArray) . "') AND status > 0 ORDER BY username");
$data = $conn->query($sql)->fetchAll(PDO::FETCH_OBJ);
There's no guarantee that the elements in $myArray are safe to use in an SQL statement. Just because they came out of the database doesn't make them safe. What if they contain names like "O'Reilly"? That will upset your quote-implosion.
You can never be 100% sure the data is safe, so it's better to use query parameters. Then you don't care if it's safe, because bound parameters are never combined with the SQL query string.
Here's how to create a dynamic list for the IN ( ) predicate from an array, and bind parameter values to the query using PDO:
$result = $conn->query($sql)->fetch(PDO::FETCH_COLUMN);
$myArray = explode(',', $result);
$placeholders = implode(',', array_fill(0, count($myArray), '?'));
$sql = "
SELECT username,status,location FROM Users
WHERE username IN ($placeholders) AND status > 0
ORDER BY username");
$stmt = $conn->prepare($sql);
$stmt->execute($myArray);
$data = $stmt->fetchAll(PDO::FETCH_OBJ);
While both answers are perfectly valid, I just wanted to add in that there is another solution to your issue.
You can change the default fetch style by defining it where you connect to the database:
$host = 'localhost';
$user = 'user';
$pass = 'pass';
$mydb = 'mydbname';
$conn = new PDO("mysql:host=$host;dbname=$mydb", $user, $pass, [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
Or you can change it by using setAttribute() using the existing connection:
$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
Then you can simply use:
$conn->query($sql)->fetchAll();
And you still have the ability to overwrite the default value:
$conn->query($sql)->fetchAll(PDO::FETCH_NUM);
using PDO::FETCH_ASSOC you can make it as an array. So
$data = $conn->query($sql)->fetchAll(PDO::FETCH_ASSOC);
Documentation link:
PDO Statement
I am stuck on a query/calculation based on two different tables (joined by a third table). A form collects information to input into tbl2, tbl1 is linked to tbl2 via foreign key. I need to calculate and insert a value into tbl2 based on form data and a value from tbl1.
My Query + Calculation looks like this:
$res = $user->runQuery("SELECT binWeight FROM jobBins jb JOIN butchJobOpen bjo ON bjo.jobBins_binID = jb.binID JOIN butchJobClose bjc ON bjc.butchJobOpen_butchJobNum = bjo.butchJobNum WHERE bjo.butchJobNum = '$butchJobOpen_butchJobNum'");
$res->execute();
$row = $res->fetch(PDO::FETCH_ASSOC);
//Calculate $trimYield
$trimYield = $row['binWeight'] / $trimWeight;
The query should use a form value $butchJobOpen_butchJobNum, verify the bin exists on the join conditions, and return the weight of the bin....this works fine as a standalone query, however, I cannot get any results from my calculation...I am sure I am just missing something stupid...but I am stuck, so I lob this up to the merciless SO community, in hopes that while getting downvoted, someone will point me in the right direction while ridiculing my obvious mistake.
Okay, here is what you have to do
Get rid of all the home-brewed stuff. Instead of whatever $user->runQuery use vanilla PDO.
Verify the input. See whether your variable contain anything useful.
Use PDO properly, utilizing prepared statements.
Make your code to give at least any outcome.
Do realize that above instruction is not a ready to use solution on a golden plate but just steps you have to make yourself to investigate the problem.
So
ini_set('display_errors',1);
error_reporting(E_ALL);
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION];
$pdo = new PDO($dsn, $user, $pass, $opt);
var_dump("that argle-bargle var name I can't type it:",$butchJobOpen_butchJobNum);
$sql = "SELECT binWeight FROM jobBins jb
JOIN butchJobOpen bjo ON bjo.jobBins_binID = jb.binID
JOIN butchJobClose bjc ON bjc.butchJobOpen_butchJobNum = bjo.butchJobNum
WHERE bjo.butchJobNum = ?";
$res = $pdo->prepare($sql);
$res->execute([$butchJobOpen_butchJobNum]);
$binWeight = $res->fetchColumn();
var_dump("bin weight:", $binWeight);
var_dump("trim weight:", $trimWeight);
$trimYield = $binWeight / $trimWeight;
var_dump("finally", $trimYield);
The var_dump part is most essential, giving you a clue what's going on in your program. A 10000 times more fruitful than a hour of staring at the code or a whine on SO.
The point here is to verify every bloody variable's state. And yo see whether it contains anything expected.
I am currently taking an IT course in which people can bring in their computer(s) and the class works on them to get experience. Right now, the instructor has the customers fill out a sheet of paper giving their name, phone number and the computer's issue(s). However, he would like to use a PHP page to allow the students or himself look back to see what this person's previous issues were (if any). I am using PDO and prepared statements to query the database, but I am having trouble figuring out how to get the number of records returned by the prepared statement. I've tried using stmt_num_rows, but it doesn't appear to be working. Here is the code I have so far:
$custID = $_GET["id"];
$compID = $_GET["compID"];
$stmtIssues = $db->prepare("SELECT IssueID, DateRequested, Issue, ActionsTaken FROM ISSUES WHERE ComputerID=:compID AND CustomerID=:custID ORDER BY DateRequested");
$stmtIssues->bindParam(":custID", $custID);
$stmtIssues->bindParam(":compID", $compID);
$stmtIssues->execute();
$numIssues = stmt_num_rows($stmtIssues);
Am I doing this right?
Any and all help is greatly appreciated.
Chris
You could do a few things:
Solution one, if you just want the count, let the database count for you:
$stmtIssues = $db->prepare("SELECT COUNT(IssueID) FROM ISSUES WHERE ComputerID=:compID AND CustomerID=:custID ORDER BY DateRequested");
$stmtIssues->bindParam(":custID", $custID);
$stmtIssues->bindParam(":compID", $compID);
$stmtIssues->execute();
$numIssues = $stmtIssues->fetchColumn();
Solution two, if you're going to display the results in addition to the count:
$stmtIssues = $db->prepare("SELECT IssueID, DateRequested, Issue, ActionsTaken FROM ISSUES WHERE ComputerID=:compID AND CustomerID=:custID ORDER BY DateRequested");
$stmtIssues->bindParam(":custID", $custID);
$stmtIssues->bindParam(":compID", $compID);
$stmtIssues->execute();
$rows = $db->fetchAll();
$numIssues = count($rows);
foreach ($rows as $row) {
echo $row['IssueID']; // you can add other columns and/or formatting here too.
}
Solution one is quicker and returns only the count. Solution two, on the other hand, returns all details you may want to display.
*MySQL will be upgraded later.
Preface: Authors can register in two languages and, for various additional reasons, that meant 2 databases. We realize that the setup appears odd in the use of multiple databases but it is more this abbreviated explanation that makes it seem so. So please ignore that oddity.
Situation:
My first query produces a recordset of authors who have cancelled their subscription. It finds them in the first database.
require_once('ConnString/FirstAuth.php');
mysql_select_db($xxxxx, $xxxxxx);
$query_Recordset1 = "SELECT auth_email FROM Authors WHERE Cancel = 'Cancel'";
$Recordset1 = mysql_query($query_Recordset1, $xxxxxx) or die(mysql_error());
$row_Recordset1 = mysql_fetch_assoc($Recordset1);
In the second db where they are also listed, (table and column names are identical) I want to update them because they cancelled. To select their records for updating, I want to take the first recordset, put it into an array, swap out the connStrings, then search using that array.
These also work.
$results = array();
do {
results[] = $row_Recordset1;
} while ($row_Recordset1 = mysql_fetch_assoc($Recordset1));
print_r($results);
gives me an array. Array ( [0] => Array ( [auth_email] => renault#autxxx.com ) [1] => Array ( [auth_email] => rinaldi#autxxx.com ) [2] => Array ( [auth_email] => hemingway#autxxx.com )) ...so I know it is finding the first set of data.
Here's the problem: The query of the second database looks for the author by auth_email if it is 'IN' the $results array, but it is not finding the authors in the 2nd database as I expected. Please note the different connString
require_once('ConnString/SecondAuth.php');
mysql_select_db($xxxxx, $xxxxxx);
$query_Recordset2 = "SELECT auth_email FROM Authors WHERE auth_email IN('$results')";
$Recordset2 = mysql_query($query_Recordset2, $xxxxxx) or die(mysql_error());
$row_Recordset2 = mysql_fetch_assoc($Recordset2);
The var_dump is 0 but I know that there are two records in there that should be found.
I've tried various combinations of IN like {$results}, but when I got to "'$results'", it was time to ask for help. I've checked all the available posts and none resolve my problem though I am now more familiar with the wild goose population.
I thought that since I swapped out the connection string, maybe $result was made null so I re-set it to the original connString and it still didn't find auth_email in $results in the same database where it certainly should have done.
Further, I've swapped out connStrings before with positive results, so... hmmm...
My goal, when working, is to echo the Recordset2 into a form with a do/while loop that will permit me to update their record in the 2nd db. Since the var_dump is 0, obviously this below isn't giving me a list of authors in the second table whose email addresses appear in the $results array, but I include it as example of what I want use to start the form in the page.
do {
$row_Recordset2['auth_email_addr '];
} while($row_Recordset2 = mysql_fetch_assoc($Recordset2));
As always, any pointer you can give are appreciated and correct answers are Accepted.
If you have a db user that has access to both databases and tables, just use a cross database query to do the update
UPDATE
mydb.Authors,
mydb2.Authors
SET
mydb.Authors.somefield = 'somevalue'
WHERE
mydb.Authors.auth_email = mydb2.Authors.auth_email AND
mydb2.Authors.Cancel= 'Cancel'
The IN clause excepts variables formated like this IN(var1,var2,var3)
You should use function to create a string, containing variables from this array.
//the simplest way to go
$string = '';
foreach($results as $r){
foreach($r as $r){
$string .= $r.",";
}
}
$string = substr($string,0,-1); //remove the ',' from the end of string
Its not tested, and obviously not the best way to go, but to show you the idea of your problem and how to handle it is this code quite relevant.
Now use $string instead of $results in query
I'm really new to PHP but I've been learning all I can, but I'm also aware that I have a lot to learn so go easy on me.
I'm currently working on a classifieds script and although the code is severely dated, It was a lot worse before I began cleaning it up and now I'm just trying to get the classifieds stable again, then I plan on going back and updating things to a more modern language (PDO) but for now I have NO IDEA on where to begin (and don't want to be reading for months while getting nowhere as I have been while learning HTML, CSS, PHP and JS)
The code below originally displayed the Title column from the table it was fetching the data from (tt_%s) but I have since modified the form which inserts the data into the DB so now there is no longer a "Title" column and in it's place there is now Year and Model from one table and Manufacturer from another table but this is where I get confused and am asking for help.
Presently this is what I am looking at; (Below the code I'll explain more)
<?php
echo "<div class='mostPopular'>";
echo "Popular<br>Listings";
$node = new sqlNode();
$node->table = "types";
$node->select = "ID";
$node->orderby = "ORDER BY rand()";
if(($typeRS = $mysql->select($node)) === false )
die('Unable to Retrieve Ad Type');
$sql = array();
$typeID = 0;
while( $adType = mysql_fetch_assoc($typeRS) ){
$typeID = sprintf("`tt_%s`", abs(intval($adType['ID'])));
$sql[] = sprintf("SELECT %s.Year, %s.Hits, %s.CategoryID, %s.ID, CONCAT('','%s') AS TypeID FROM %s WHERE (`ExpireDate` > NOW()) ",
$typeID,
$typeID,
$typeID,
$typeID,
intval($adType['ID']),
$typeID);
}
$sqlStr = #implode("union ",$sql);
$sqlStr .= " ORDER BY Hits DESC LIMIT 5";
if(($adRS = $mysql->exSql($sqlStr)) === false)
die('Unable to Retrieve Most Popular Ads');
while( $ad = mysql_fetch_assoc($adRS) ){
echo "<p>";
echo "<a href='detail.php?fatherID=".$ad['CategoryID']."&TypeID=".$ad['TypeID']."&ListingID=".$ad['ID']."'>";
echo $ad['Title'];
echo "</a>";
echo "</p>";
}
echo "</div>";
?>
For starters, I don't know what CONCAT('','%s') is all about, nor do I know what $sqlStr = #implode("union ",$sql); is all about.
In addition to these issues, considering that this code is outdated and I should be switching to PDO, I was hoping someone could show me exactly how to perform these queries with PDO as then I could learn by example and apply the same procedures to other queries throughout the site.
If anyone wouldn't mind conversing with me to the point where I can perform the same outcome with PDO I'd be most appreciative and I thank you all in advance while I eagerly await your replies.
BTW, I ask this after studying every PHP and MySql tutorial I can find for the past several months
Thanks
Although "Please rewrite this code to PDO" being too localized one, the real problem you have require different solution.
First of all, you need to fix your database architecture. It's terrible.
Premature optimization, being the root of all evil, depraved you from the right ways. There is no point in splitting your data into small chunks if you going to union them anyway.
You have to have one table for the ads.
It will make all this code obsolete, leaving only one regular query which can be run in one conventional PDO call.
Next, this code has been written by some sprintf() maniac, who made it overcomplicated out of nowhere. As a matter of fact, not a single sprintf() is really needed here.
$sql = array();
while( $row = mysql_fetch_assoc($typeRS) ){
$typeID = abs($row['ID']);
$sql[] = "SELECT Year, Hits, CategoryID, ID, '$typeID' AS TypeID
FROM tt_$typeID WHERE ExpireDate > NOW()";
}
$sqlStr = implode("union ",$sql)." ORDER BY Hits DESC LIMIT 5"
is enough.
But again, no need for this union query. Everything have to be performed via single ordinary query like this one
SELECT Year, Hits, CategoryID, ID, TypeID FROM ads ORDER BY Hits DESC LIMIT 5
which you no doubt will be able to run using whatever PDO example you have