I am editing a tutorial I found on inline editing. It uses mysql but obviously I want to use mysqli as mysql is depreciated. I have changed most of it but one part is causing me difficulty. There is a seperate file that is called to connect to the database and run queries so the main page I will declare at the top at the top of my page I declare
require_once("dbcontroller.php");
$db_handle = new DBController();
$sql = "SELECT * from php_interview_questions";
$faq = $db_handle->runQuery($sql);
The relevant part of the dbcontroller.php is:
function runQuery($query) {
$result = mysqli_query($conn, $query);
while($row=mysqli_fetch_assoc($result)) {
$resultset[] = $row;
}
if(!empty($resultset))
return $resultset;
}
function numRows($query) {
$result = mysqli_query($conn,$query);
$rowcount = mysqli_num_rows($result);
return $rowcount;
}
Am I right in thinking that I need to use prepared statements? If so how would this cope with working with a select query that could involve any number of columns or any number of conditions in the WHERE clause?
numRows() is a strange function since it runs a query only to find out how many rows it returns. I would consider it a questionable practice. If it is necessary to find out how many rows a SELECT would return, then one could change SELECT * to SELECT COUNT(*). MySQL would be able to optimize that for MyISAM tables, plus there is a saving on not having to transfer the result set to the client.
Otherwise there is no need to use prepared statements, since a prepared statement only increases the amount of work a DBMS has to do when a statement is executed only once (it has to handle two requests - prepare, execute, -- instead of one -- execute, plus extra work to clean up the cached statement when connection is destroyed).
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.
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.
For the sake of avoiding unnecessary information, roughly my code flows as follows:
$db = new PDO(DSN, DB_USER, DB_PW);
$sql1 = "SELECT * FROM Table1";
// fetching the first result
$stt1 = $db->prepare($sql1);
if ($stt1->execute()) {
$result = $stt1->fetch(PDO::FETCH_ASSOC);
}
// doing update in the middle by using the SAME $db object, but different statement variable
$sql2 = "UPDATE Table1 SET field1 = 'footest1' WHERE id = 1";
$stt2 = $db->prepare($sql2);
$stt2->execute();
// fetching the next result
$result = $stt1->fetch(PDO::FETCH_ASSOC);
Ok, I ran this, and to my surprise, when I am fetching my next result, I get false. Does preparing another statement ($stt2) in the middle interrupt my already created $stt1?
And I have 15+ records on that table.
Update: It seems like the execute method of the statement object is the reason my second fetch is returning false. For this to work, calling $stt1->execute() again before fetching the second time solves this problem... But this shows that there is some connection via the execute method between all the statement object?
Different databases have different constraints. This is not an issue with PHP or PDO, but with the database connection.
While you can rely on any DB connector to support at least one cursor with pending rows, many databases will limit you at one, and require you to either fully fetch or explicitly close the underlying cursor before executing a new statement.
This is indeed a property of the database connection, as all PDO statements are bound to one. (Where would they fetch the data from if they didn't remain bound to the database connection?) If you're using a database that only supports one open prepared statement at a time, you'll have no choice but to either serialize your accesses or open multiple connections to the database. You might also want to take a look at the closeCursor method of the PDOStatement class.
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(*) ...
Quick question. Whilst using the prepare method in mysqli. It is possible/a good idea; to use it twice within the same mysqli connection? Example:
OOP layered
public function getStuff(){
$posts=array();
$query = $this->DBH->prepare('SELECT * FROM table WHERE stuff =?');
$query->bind_param('s','param');
$query->execute();
$query->bind_result($ID,$col1,$col2,$etc);
while($query->fetch()){
$posts[]=array('ID'=>$ID,'col1'=>$col1,'extras'=>$this->getExtras($ID));
}
$query->close();
return $posts;
}
private function getExtra($postID){
$extras=array();
$query = $this->DBH->prepare('SELECT * FROM anotherTable WHERE moreStuff =?');
$query->bind_param('s',$postID);
$query->execute();
$query->bind_result($ID,$col1,$col2,$etc);
while($query->fetch()){
$extras[]=array('ID'=>$ID,'col1'=>$col1,'etc'=>$etc);
}
$query->close();
return $extras;
}
Right my possible error is that I've used the same variable and the same database connection. I'm not 100% sure this will work as I've called $this->DBH whilst it is already being used in the parent function. Is there a better method to what I'm trying to achieve or is there a better structure I can use. Or should I just give up and use a separate variable? lol
Hopeful outcome:
$posts=array('ID'=>'column ID number','col1'=>'column1 data', 'extras'=>array('ID'=>'second table\'s ID number','col1'=>'second tables data','etc'=>'etc etc etc'));
In your example above, the variables which matter are $query. Each of those is local to its own method, and so the variables themselves will not collide. The MySQLi connection $this->DBH is capable of handling multiple open statements at once if circumstances are right.
The place where you need to use caution is with their execution order. If you prepare and execute a statement but do not fetch all rows from it, you may not be able to prepare() the next one until all rows have been fetched unless you first close it with mysqli_stmt::close() to deallocate the open statement handle.
For example:
// Prepares successfully:
$s1 = $mysqli->prepare("SELECT * FROM t1");
// Also prepares successfully (previous one not executed)
$s2 = $mysqli->prepare("SELECT * FROM t2");
// Then consider:
$s1 = $mysqli->prepare("SELECT id, name FROM t1");
$s1->bind_result($id, $name);
$s1->execute();
// And attempt to prepare another
$s2 = $mysqli->prepare("SELECT id, name FROM t2");
// Fails because $s1 has rows waiting to be fetched.
echo $m->error;
// "Commands out of sync; you can't run this command now"
Edit: misread your example...
Looking at your example above, you are indeed calling getExtras() while you are fetching from the getStuff() statement. You may run into the issue described above. Your two operations in this case may be able to be handled with a single JOIN instead, from which you fetch in only one loop to populate all your variables and build your output array as you want it. Depending on your need, this should either be an INNER JOIN if a related row is expected to exist in the othertable, or a LEFT JOIN if the related othertable may or may not have a row that matches the given ID.
SELECT
maintable.id,
maintable.col1,
othertable.col2,
othertable.col3
FROM
maintable
JOIN othertable ON maintable.id = othertable.id