I want to migrate a site from some poorly written MySQLi to clean PDO.
I have looked at three similar questions and their answers, and this is a straightforward question, but none of them are giving me results. Here's my code:
$state = "Alaska";
//trying to implement PDO here
$sql = "SELECT * FROM sales WHERE state = ? ORDER BY type";
$result = $conn->prepare($sql);
$result->execute(array($state));
/*
this was the old, successfully working way before
$sql = "SELECT * FROM sales WHERE state = '$state' ORDER BY type";
$result = $conn->query($sql);
*/
Previous questions on this site show me answers that look like my PDO implementation, yet mine doesn't work. I have made sure the PDO class exists and that the extension is loaded.
If you see the error, let me know!
The difference between the two, aside from difference in libraries, is that one is using a direct query() (the mysqli_*), while the other is using a prepared statement. Those are handled a bit different, regardless which API is running.
When using MySQLi, doing
$result = $conn->query($sql);
would have $result be a mysqli-result object, which holds the data. You can use mysqli_result::fetch_assoc() on that to fetch the data. However, when you're using PDO::prepare(), your $result variable will be a PDOStatement - which is a bit different. You'll need to run a fetch() method on it first, and then you can use the return-value of it, as such
$state = "Alaska";
$sql = "SELECT * FROM sales WHERE state = ? ORDER BY type";
$stmt = $conn->prepare($sql);
$stmt->execute(array($state));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
Note that I've changed the names of your variables. Now $result is an array (if there are any results fetched), which you can use as you normally do when fetching associative-arrays. If there are no results, PDOStatement::fetch() will return a boolean false.
var_dump($result['state']);
You can loop the fetch() method as
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
if you expect more than one row. Use $result as you would without looping, as shown above.
Note that this assumes a valid PDO-connection. Beware that you cannot interchange any MySQL libraries, mysql_, mysqli_* and PDO are all different animals in the zoo.
PHP.net on PDOStatement::fetch()
Can I mix MySQL APIs in PHP?
Related
I need to get all the rows from result object. I’m trying to build a new array that will hold all rows.
Here is my code:
$sql = new mysqli($config['host'],$config['user'],$config['pass'],$config['db_name']);
if (mysqli_connect_errno())
{
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$query = "SELECT domain FROM services";
$result = $sql->query($query);
while($row = $result->fetch_row());
{
$rows[]=$row;
}
$result->close();
$sql->close();
return $rows;
$rows is supposed to be the new array that contains all, rows but instead I get an empty array.
Any ideas why this is happening?
You had a slight syntax problem, namely an errant semi-colon.
while($row = $result->fetch_row());
Notice the semi-colon at the end? It means the block following wasn't executed in a loop. Get rid of that and it should work.
Also, you may want to ask mysqli to report all problems it encountered:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$sql = new mysqli($config['host'], $config['user'], $config['pass'], $config['db_name']);
$query = "SELECT domain FROM services";
$result = $sql->query($query);
$rows = [];
while($row = $result->fetch_row()) {
$rows[] = $row;
}
return $rows;
Newest versions of mysqli have some improvements that can simplify such a task.
First, of all, there is a useful function to return an array with all rows returned by a query, mysqli_fetch_all()
It means in case you need a simple enumerated array, the code would be much simpler:
$query = "SELECT domain FROM services";
$result = $sql->query($query);
return $result->fetch_all(MYSQLI_ASSOC);
or even all in one line,
return $sql->query("SELECT domain FROM services")->fetch_all(MYSQLI_ASSOC);
However, if you need to use some column to index the resulting array, you still need a while loop like this:
$query = "SELECT id, domain FROM services";
$result = $sql->query($query);
$data = [];
while ($row = $result->fetch_assoc()) {
$data[$row['id']] = $row;
}
Note that you should always initialize an array before filling it up, because such a variable could already exist.
Also, mysqli_result class is now Traversable. It means you can use it in the foreach loop right away, as though it's an array contains all rows from the database:
$query = "SELECT domain FROM services";
$result = $sql->query($query);
foreach ($result as $row) {
echo $row['domain'];
}
But it is actually just a syntax sugar for the while loop - you cannot access values of this "array" directly, which makes this feature of a little use actually.
Obligatory notes.
This question is a decade old, and the way a connection is made and the query is performed, both in the question and the accepted answer, are obsoleted and frowned upon nowadays.
When a connection is made, there are several things to keep in mind. I wrote an article on how to connect with mysqli properly that provides a correct connection example emphasizing on the following issues:
a proper error reporting mode must be set
a proper character set must be set
no manual error reporting code should be ever used (like die(mysqli_connect_error()))
a connection has to be made only once, in a separate file, and then just included into every script that needs a database interaction. in case a database code is used in a function, a connection variable must be passed in as a function parameter.
When it goes to running a query, there are several things to keep in mind as well:
when even a single variable is used in the query, a prepared statement must be used instead of mysqli_query()
as a result, a special function called mysqli_stmt_get_result() should be used in order to use familiar fetch functions to get the resulting rows. In case this function is not available you are probably to tick some checkbox in your cpanel (look for one labeled mysqlnd).
given a prepared statement with mysqli, although being obligatory, takes a lot code to write, it is advised to use a helper function for mysqli that would perform most of work automatically and make a mysqli prepared statement a smooth as a regular query.
no manual error reporting code should be ever used (like die(mysqli_error())). Thanks to the proper error mode, mysqli will report all errors automatically.
Currently to perform a query with PDO, I use the following lines of code:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
And after some research, I found a shorter way of executing the same command:
$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);
From there I thought I could possibly make it even shorter with the following code:
$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$result = $stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
But I get the following error:
Fatal error: Call to a member function fetchAll() on a non-object in
/home/.../index.php on line 20
QUESTION: Why am I getting this error? From my understanding, $stmt_test->execute([$id]) should be executing first, then the result of that would execute the ->fetchAll(PDO::FETCH_ASSOC) and from there return the array to $result, but since the error is happening, something must be flawed in my logic. What am I doing wrong? Also, does anyone know a better shorthand method to perform the previous query?
So you've got an answer for the question "Why I am getting this error", but didn't get one for the "shorthand PDO query".
For this we will need a bit of a thing called "programming".
One interesting thing about programming is that we aren't limited to the existing tools, like with other professions. With programming we can always create a tool of our own, and then start using it instead of a whole set of old tools.
And Object Oriented Programming is especially good at it, as we can take an existing object and just add some functionality, leaving the rest as is.
For example, imagine we want a shorthand way to run a prepared query in PDO. All we need is to extend the PDO object with a new shorthand method. The hardest part is to give the new method a name.
The rest is simple: you need only few lines of code
class MyPDO extends PDO
{
public function run($sql, $bind = NULL)
{
$stmt = $this->prepare($sql);
$stmt->execute($bind);
return $stmt;
}
}
This is all the code you need. You may store it in the same file where you store your database credentials. Note that this addition won't affect your existing code in any way - it remains exactly the same and you may continue using all the existing PDO functionality as usual.
Now you have to change only 2 letters in PDO constructor, calling it as
$conn = new MyPDO(...the rest is exactly the same...);
And immediately you may start using your shiny new tool:
$sql = "SELECT * FROM myTable WHERE id = :id";
$result = $conn->run($sql, ['id' => $id])->fetchAll(PDO::FETCH_ASSOC);
Or, giving it a bit of optimization,
$result = $conn->run("SELECT * FROM myTable WHERE id = ?", [$id])->fetchAll();
as you can always set default fetch mode once for all, and for just a single variable there is no use for the named placeholder. Which makes this code a real shorthand compared to the accepted answer,
$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$stmt_test->execute([$id]);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);
and even to the best answer you've got so far,
$result = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$result->execute([$id]);
not to mention that the latter is not always usable, as it fits for getting an array only. While with a real shorthand any result format is possible:
$result = $conn->run($sql, [$id])->fetchAll(); // array
$result = $conn->run($sql, [$id])->fetch(); // single row
$result = $conn->run($sql, [$id])->fetchColumn(); // single value
$result = $conn->run($sql, [$id])->fetchAll(PDO::FETCH_*); // dozens of different formats
$stmt_test->execute([$id]) returns a boolean value. That mean that
$result = $stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
isn't valid. Instead you should do
$stmt_test->execute([$id]);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);
The error you're getting comes form the way PDO was designed. PDOStatement::execute() doesn't return the statement, but a boolean indicating success. The shortcut you want therefore isn't possible.
See function definition in http://php.net/manual/en/pdostatement.execute.php
Additionally let me add that forEach() often (not always) is a code smell and takes relatively much memory as it has to store all rows as PHP values.
I believe PDO's execute() method returns either true or false. As the error already tells you: fetchAll() expects an object. Using three lines of code would be the shortest way.
Another option is to use an ORM like propel, it works really smooth and will save you alot of time.
I have a MySQL Database Table containing products and prices.
Though an html form I got the product name in a certain php file.
For the operation in this file I want to do I also need the corresponding price.
To me, the following looks clear enough to do it:
$price = mysql_query("SELECT price FROM products WHERE product = '$product'");
However, its echo returns:
Resource id #5
instead a value like like:
59.95
There seem to be other options like
mysqli_fetch_assoc
mysqli_fetch_array
But I can't get them to output anything meaningful and I don't know which one to use.
Thanks in advance.
You will need to fetch data from your database
$price = mysql_query("SELECT price FROM products WHERE product = '$product'");
$result = mysql_fetch_array($price);
Now you can print it with
echo $result['price'];
As side note I would advise you to switch to either PDO or mysqli since mysql_* api are deprecated and soon will be no longer mantained
If you read the manual at PHP.net (link), it will show you exactly what to do.
In short, you perform the query using mysql_query (as you did), which returns a Result-Resource. To actually get the results, you need to perform either mysql_fetch_array, mysql_fetch_assoc or mysql_fetch_object on the result resource. Like so:
$res = mysql_query("SELECT something FROM somewhere"); // perform the query on the server
$result = mysql_fetch_array($res); // retrieve the result from the server and put it into the variable $result
echo $result['something']; // will print out the result you retrieved
Please be aware though that you should not use the mysql extension anymore; it has been officially deprecated. Instead you should use either PDO or MySQLi.
So a better way to perform the same process, but using for example the MySQLi extension would be:
$db = new mysqli($host, $username, $password, $database_name); // connect to the DB
$query = $db->prepare("SELECT price FROM items WHERE itemId=?"); // prepate a query
$query->bind_param('i', $productId); // binding parameters via a safer way than via direct insertion into the query. 'i' tells mysql that it should expect an integer.
$query->execute(); // actually perform the query
$result = $query->get_result(); // retrieve the result so it can be used inside PHP
$r = $result->fetch_array(MYSQLI_ASSOC); // bind the data from the first result row to $r
echo $r['price']; // will return the price
The reason this is better is because it uses Prepared Statements. This is a safer way because it makes SQL injection attacks impossible. Imagine someone being a malicious user and providing $itemId = "0; DROP TABLE items;". Using your original approach, this would cause your entire table to be deleted! Using the prepared queries in MySQLi, it will return an error stating that $itemId is not an integer and as such will not destroy your script.
What is the preferred method for getting the number of rows that are returned for a SELECT state when using PDO with prepared statements?
I am currently using rowCount() but the docs say I shouldn't be using that since "most databases" don't support it (It is actually working just fine for me, so I'm tempted to keep using it. I can't find any sources that list exactly which databases do not support it, but apparently mine is fine).
Instead they recommend I use fetchColumn() but that requires writing a completely separate SQL statement that includes the COUNT(*) in my sql statement.
This is what they propose (http://php.net/manual/en/pdostatement.rowcount.php#example-1038):
//SQL TO GET ROWS TO OUTPUT
$sql = 'SELECT *
FROM properties
WHERE lister_id = :lister_id
AND lister_type = "landlord"';
$result = $dbh->prepare($sql);
$result->bindParam(':lister_id', $_SESSION['loggedin_lister_id'], PDO::PARAM_INT);
$result->execute();
//SQL TO GET NUMBER OF RETURNED ROWS
$row_num_sql = 'SELECT COUNT(*)
FROM properties
WHERE lister_id = :lister_id
AND lister_type = "landlord"';
$row_num_result = $dbh->prepare($row_num_sql);
$row_num_result->bindParam(':lister_id', $_SESSION['loggedin_lister_id'], PDO::PARAM_INT);
$row_num_result->execute();
$num_rows = $row_num_result->fetchColumn();
if($num_rows > 0) {
while($row = $result->fetch(PDO::FETCH_ASSOC)) {
echo $row['name'];
}
}
I find this method that requires me to write a separate and nearly identical sql statement to be redundant and a serious pain when using prepared statements. I can understand how this approach might be acceptable when using a short SQL statement with a basic query, but not in the case of a prepared statement.
1. Is there any other way I can use the fetchColumn() approach
without having to rewrite what is almost exactly the same code?
2. Where can I find an official list of which databases
rowCount() supports when using a SELECT statement? And since it is
working on the database I am currently using, can I assume it is safe
to use(assuming I am not updating my database anytime soon)?
If you don't want to use rowCount I'm think you should two query, or you can use fetchAll and count(fetchAll) for rowCount
the second way, Use SELECT *,COUNT(*) ...
I notice that these two ways of structuring a query with PHP PDO, both return the same data.
//prepare with $dbh->prepare
$w_ft = "36";
$sth = $dbh->prepare("SELECT * FROM main_products_common_dimensions WHERE w_ft = :w_ft");
$sth->bindParam(':w_ft', $theId, PDO::PARAM_INT);
$sth->execute();
$result = $sth->fetchAll(); //PHP array of data
//prepare with pg_prepare()
$result = pg_prepare($con, "my_query", 'SELECT * FROM main_products_common_dimensions WHERE w_ft = $1');
$result = pg_execute($con, "my_query", array("48")); //A query result resource on success or FALSE on failure.
while ($row = pg_fetch_assoc($result)){
echo $row['w_ft'] . "<BR>";
}
I read at http://php.net/manual/en/function.pg-execute.php that the second way returns "A query result resource on success or FALSE on failure." So I tried to iterate through it with pg_fetch_assoc(). It works, but is that not deprecated along with the rest of the pg_sql functions? Should I be using something in the PDO to look at the results of the query?
For this reason, I'm inclined to use the first method while using PDO. Is this the norm?
My question is how to submit multiple values to either method. Neither method works when I try to submit an array to the prepared statement
$w_ft = array("48", "36");
$result = pg_execute($con, "my_query", array("48", "36"));
I thought that I was able to submit multiple values to the query in this way. How can I do this?
Thank you
Looks like you're taking execute array wrong.
It takes values not for the consequent executions but for one execution only.
So, number of values should be equal to number of placeholders.
So it goes
$sql = 'SELECT * FROM table WHERE w_ft = $1 OR w_ft = $2 OR w_ft = $3'
for your array("48", "36", "12")
I am not a PG user though, so, I can confuse some syntax.
PDO is an agnostic database access API for PHP. This means, whatever your database is, you will be able to use the same objects and methods to query it and fetch results. PDO was the first step toward database abstraction layers in PHP.
The pg_* API is, afaik, not going to be deprecated and is dedicated to Postgres. It also contains functions that were not implemented in PDO (like binary escaping or event notifier functions amongst others).
The choice between PDO and dedicated library is often hidden by the choice of the Model Manager you use in your controllers code. Either it can be Object Relational Mapper over an abstraction layer like ORMs (Doctrine, Propel and many others) or it can be an Object Model Manager (OMM) dedicated to Postgresql like Pomm.
In both ways, you do not need to prepare statements, manage the columns types nor results cursors (btw, using PDO::fetch_all() will dump all results in memory), the database layer handles that for you. Your above query would be written like:
// Using Pomm
// SELECT * FROM main_products_common_dimensions WHERE w_ft = ?
// Returns a MainProductsCommonDimensions instance
$object = $connection
->getMapFor('\Database\Schema\MainProductsCommonDimensions')
->findWhere('w_ft = ?', array(48))
->current(); // fetch only the first result.
Note that both ORMs and OMMs do propose handy query builder:
$where = \Pomm\Query\Where::createWhere('w_ft = ?', array(48))
->orWhere('w_ft = ?', array(12))
->orWhere('w_ft = ?', array(66));
// which is pretty much the same as
$where = \Pomm\Query\Where::createIn('w_ft', array(48, 12, 66));
// SELECT * FROM main_products_common_dimensions WHERE w_ft IN (?, ?, ?);
$collection = $connection
->getMapFor('\Database\Schema\MainProductsCommonDimensions')
->findWhere($where); // Return an Iterable cursor over results
// display 48, 12 and 66 fetching one result at the time in memory:
foreach($collection as $object)
{
printf("W_FT = '%d'.\n", $object['w_ft']);
}
Using those layers above PDO offers numerous advantages, the main one is to focus more on what you want instead of dealing with weird APIs (yes PDO is weird as almost any lib in PHP). Furthermore, since PDO is just an access layer it will return results as arrays of strings (binary or not). Boolean in Postgres are 't' and 'f' and thus will need to be converted to PHP proper Boolean (sic) type to be used. ORMs and OMMs do propose such translation mechanisms and Pomm as being dedicated to Postgres also support Arrays, HStore, LTree, geometric and composite types conversions and more.