How to use bind_result() instead of get_result() in php - php

I'm working on a project for uni and have been using the following code on a testing server to get all devices from a table based on a user_id:
public function getAllDevices($user_id) {
$stmt = $this->conn->prepare("SELECT * FROM devices WHERE primary_owner_id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$devices = $stmt->get_result();
$stmt->close();
return $devices;
}
This worked fine on my testing server but returns this error when migrating over to the university project server:
Call to undefined method mysqli_stmt::get_result()
Some googling suggests using bind_result() instead of get_result() but I have no idea how to do this all fields in the table. Most examples only show returning one field
Any help would be much appreciated

Assuming you can't use get_result() and you want an array of devices, you could do:
public function getAllDevices($user_id) {
$stmt = $this->conn->prepare("SELECT device_id, device_name, device_info FROM devices WHERE primary_owner_id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($id, $name, $info);
$devices = array();
while($stmt->fetch()) {
$tmp = array();
$tmp["id"] = $id;
$tmp["name"] = $name;
$tmp["info"] = $info;
array_push($devices, $tmp);
}
$stmt->close();
return $devices;
}
This creates a temporary array and stores the data from each row in it, and then pushes it to the main array. As far as I'm aware, you can't use SELECT * in bind_result(). Instead, you will annoyingly have to type out all the fields you want after SELECT

By now, you've certainly grasped the idea of binding to multiple variables. However, do not believe the admonitions about not using "SELECT *" with bind_result(). You can keep your "SELECT *" statements... even on your server requiring you to use bind_result(), but it's a little complicated because you have to use PHP's call_user_func_array() as a way to pass an arbitrary (because of "SELECT *") number of parameters to bind_result(). Others before me have posted a handy function for doing this elsewhere in these forums. I include it here:
// Take a statement and bind its fields to an assoc array in PHP with the same fieldnames
function stmt_bind_assoc (&$stmt, &$bound_assoc) {
$metadata = $stmt->result_metadata();
$fields = array();
$bound_assoc = array();
$fields[] = $stmt;
while($field = $metadata->fetch_field()) {
$fields[] = &$bound_assoc[$field->name];
}
call_user_func_array("mysqli_stmt_bind_result", $fields);
}
Now, to use this, we do something like:
function fetch_my_data() {
$stmt = $conn->prepare("SELECT * FROM my_data_table");
$stmt->execute();
$result = array();
stmt_bind_assoc($stmt, $row);
while ($stmt->fetch()) {
$result[] = array_copy($row);
}
return $result;
}
Now, fetch_my_data() will return an array of associative-arrays... all set to encode to JSON or whatever.
It's kinda crafty what is going on, here. stmt_bind_assoc() constructs an empty associative array at the reference you pass to it ($bound_assoc). It uses result_metadata() and fetch_field() to get a list of the returned fields and (with that single statement in the while loop) creates an element in $bound_assoc with that fieldname and appends a reference to it in the $fields array. The $fields array is then passed to mysqli_stmt_bind_result. The really slick part is that no actual values have been passed into $bound_assoc, yet. All of the fetching of the data from the query happens in fetch_my_data(), as you can see from the fact that stmt_bind_assoc() is called before the while($stmt->fetch()).
There is one catch, however: Because the statement has bound to the references in $bound_assoc, they are going to change with every $stmt->fetch(). So, you need to make a deep copy of $row. If you don't, all of the rows in your $result array are going to contain the same thing: the last row returned in your SELECT. So, I'm using a little array_copy() function that I found with the Google:
function array_copy( array $array ) {
$result = array();
foreach( $array as $key => $val ) {
if( is_array( $val ) ) {
$result[$key] = arrayCopy( $val );
} elseif ( is_object( $val ) ) {
$result[$key] = clone $val;
} else {
$result[$key] = $val;
}
}
return $result;
}

Your question suggests that you have MySQL Native driver (MySQLnd) installed on your local server, but MySQLnd is missing on the school project server. Because get_result() requires MySQLnd.
Therefore, if you still want to use get_result() instead of bind_result() on the school project server, then you should install MySQLnd on the school project server.

in order to use bind_result() you can't use queries that SELECT *.
instead, you must select individual column names, then bind the results in the same order. here's an example:
$stmt = $mysqli->prepare("SELECT foo, bar, what, why FROM table_name WHERE id = ?");
$stmt->bind_param("i", $id);
if($stmt->execute()) {
$stmt->bind_result($foo, $bar, $what, $why);
if($stmt->fetch()) {
$stmt->close();
}else{
//error binding result(no rows??)
}
}else{
//error with query
}

get_result() is now only available in PHP by installing the MySQL native driver (mysqlnd). In some environments, it may not be possible or desirable to install mysqlnd.
Notwithstanding, you can still use mysqli to do 'select *' queries, and get the results with the field names - although it is slightly more complicated than using get_result(), and involves using php's call_user_func_array() function. See example below which does a simple 'select *' query, and outputs the results (with the column names) to an HTML table:
$maxaccountid=100;
$sql="select * from accounts where account_id<?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('i', $maxaccountid);
$stmt->execute();
print "<table border=1>";
print "<thead><tr>";
$i=0;
$meta = $stmt->result_metadata();
$query_data=array();
while ($field = $meta->fetch_field()) {
print "<th>" . $field->name . "</th>";
$var = $i;
$$var = null;
$query_data[$var] = &$$var;
$i++;
}
print "</tr></thead>";
$r=0;
call_user_func_array(array($stmt,'bind_result'), $query_data);
while ($stmt->fetch()) {
print "<tr>";
for ($i=0; $i<count($query_data); $i++) {
print "<td>" . $query_data[$i] . "</td>";
}
print "</tr>";
$r++;
}
print "</table>";
$stmt->close();
print $r . " Records<BR>";

Related

PHP changing fetch_field() to mysqli

My php knowledge is fairly limited but I've recently needed to update a number of web pages from an older version of php 5.2 to php 7.3.
I've managed to update most of the mysql references to mysqli etc and get things working correctly, however there is one page that makes use of a calendar and I'm really struggling with this section and the fetch_field part in particular as any examples I have found don't seem to be in a similar format.
The code I need to update is below;
require_once('Connections/connAsh.php');
mysql_select_db($database_connAsh, $connAsh);
function selectonerow($fieldsarray, $table, $uniquefield, $uniquevalue)
{
//The required fields can be passed as an array with the field names or as a comma separated value string
if (is_array($fieldsarray)) {
$fields = implode(", ", $fieldsarray);
} else {
$fields = $fieldsarray;
}
//performs the query
$result = mysql_query("SELECT $fields FROM $table WHERE $uniquefield = '$uniquevalue'") or die("Could not perform select query - " . mysql_error());
$num_rows = mysql_num_rows($result);
//if query result is empty, returns NULL, otherwise, returns an array containing the selected fields and their values
if ($num_rows == NULL) {
return NULL;
} else {
$queryresult = array();
$num_fields = mysql_num_fields($result);
$i = 0;
while ($i < $num_fields) {
$currfield = mysql_fetch_field($result, $i);
$queryresult[$currfield->name] = mysql_result($result, 0, $currfield->name);
$i++;
}
return $queryresult;
}
}
My attempts at editing this are;
require_once('../Connections/connAsh.php')
$connAsh->select_db($database_connAsh);
function selectonerow($fieldsarray, $table, $uniquefield, $uniquevalue)
{
//The required fields can be passed as an array with the field names or as a comma separated value string
if (is_array($fieldsarray)) {
$fields = implode(", ", $fieldsarray);
} else {
$fields = $fieldsarray;
}
//performs the query
$result = $connAsh->query("SELECT $fields FROM $table WHERE $uniquefield = '$uniquevalue'") or die("Could not perform select query - " . mysqli_error());
$num_rows = mysqli_num_rows($result);
//if query result is empty, returns NULL, otherwise, returns an array containing the selected fields and their values
if ($num_rows == NULL) {
return NULL;
} else {
$queryresult = array();
$num_fields = mysqli_num_fields($result);
$i = 0;
while ($i < $num_fields) {
$currfield = mysqli_fetch_field($result);
$queryresult[$currfield->name] = mysqli_fetch_array($result, MYSQLI_BOTH);
$i++;
}
return $queryresult;
}
}
The original function is wrong on so many levels. And there is no point in recreating its functionality.
Basically all you are bargaining for here is just a few SQL keywords. But these keywords contribute for readability.
For some reason you decided to outsmart several generations of programmers who are pretty happy with SQL syntax, and make unreadable gibberish
$row = selectonerow("some, foo, bar", "baz", "id", [$uniquevalue]);
instead of almost natural English
$row = selectonerow("SELECT some, foo, bar FROM baz WHERE id=?", [$uniquevalue]);
Come on. It doesn't worth.
Make your function accept a regular SQL query, not a limited unintelligible mess.
function selectonerow(mysqli $conn, string $sql, array $params = []): array
{
if ($params) {
$stmt = $conn->prepare($sql);
$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat("s", count($params), ...$params);
$stmt->execute();
$result = $stmt->get_result()
} else {
$result = $conn->query($sql);
}
return $result->fetch_assoc();
}
This function will let you to use any query. For example, need a row with max price?
$row = selectonerow("SELECT * FROM baz ORDER BY price DESC LIMIT 1");
Need a more complex condition? No problem
$sql = "SELECT * FROM baz WHERE email=? AND activated > ?";
$row = selectonerow($sql, [$email, $date]);
and so on. Any SQL. Any condition.
I would recommend to get rid of this function or replace it with a function suggested by YCS.
If you really want to continue using this function consider the following fixes. You made the code inside extremely complicated and you forgot to pass the connection variable into the function. I have simplified it:
// open the DB connection properly inside Connections/connAsh.php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$connAsh = new mysqli('host', 'user', 'pass', $database_connAsh);
$connAsh->set_charset('utf8mb4');
// your function:
function selectonerow(mysqli $connAsh, $fieldsarray, $table, $uniquefield, $uniquevalue): array
{
//The required fields can be passed as an array with the field names or as a comma separated value string
if (is_array($fieldsarray)) {
$fields = implode(", ", $fieldsarray);
} else {
$fields = $fieldsarray;
}
//performs the query
$stmt = $connAsh->prepare("SELECT $fields FROM $table WHERE $uniquefield = ?");
$stmt->bind_param('s', $uniquevalue);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
This is your function, but with a lot of noise removed. I added $connAsh in the function's signature, so you must pass it in every time you call this function. The function will always return an array; if no records are fetched the array will be empty. This is the recommended way. Also remember to always use prepared statements!

Mysqli query with bind params using fetch_array

I know how to use fetch_array instead of printf() function when expressing rows from database using mysqli bind function.
How can I use $row->mysqli_fetch_array and then use $row[0],$row[1] instead of using the printf() function every time I want to print something from database?
Returning an associative array from a prepared statement you can follow up the procedure like this as follows.
<?php
$category = $_POST['category'];
$sql = "select id, name from items where category = ?";
$stmt = $connection->prepare($sql);
$stmt->bind_param('s', $category);
if($stmt->execute())
{
$result = $stmt->get_result();
$a = $result->fetch_array(MYSQLI_ASSOC); // this does work :)
}
else
{
error_log ("Didn't work");
}
?>
You can use while loop for printing up the value over from the associative array.
while($a = $result->fetch_array(MYSQLI_ASSOC))
{
echo 'Id: '. $a['id'];
echo '<br>';
echo 'Name: '.$a['name'];
}
Output:
Id: 1
Name: Example
If I got your question correctly you can try:
$row=mysqli_fetch_array($result,MYSQLI_NUM);
foreach($row as $cell) {
echo "$cell";
}

PHP PDO FetchAll vs Fetch

I believe I am using the PDO fetch functions completely wrong. Here is what I am trying to do:
Query a row, get the results, use a helper function to process the results into an array.
Query
function userName($db){
$q = $db->prepare("SELECT id, name FROM users WHERE id = :user");
$q->bindParam(":user", $user);
$q->execute();
$qr = $q->fetchAll(PDO::FETCH_ASSOC);
if ($qr->rowCount() > 0){
foreach($qr as $row){
$names[$row['id']] = buildArray($row);
}
return $names;
}
}
My custom array building function
function buildArray($row){
$usernames = array();
if(isset($row['id'])) $usernames['id'] = $row['id'];
if(isset($row['name'])) $usernames['name'] = $row['name'];
}
I'm actually getting exactly what I want from this, but when I echo inbetween I see that things are looping 3 times instead of once. I think I am misusing fetchAll.
Any help appreciated
If you're going to build a new array, there's not much point in having fetchAll() build an array. Write your own fetch() loop:
function userName($db){
$q = $db->prepare("SELECT id, name FROM users WHERE id = :user");
$q->bindParam(":user", $user);
$q->execute();
$names = array();
while ($row = $q->fetch(PDO::FETCH_ASSOC)) {
$names[$row['id']] = $row;
}
return $names;
}
There's also no need for buildArray(), since $row is already the associative array you want.

PHP convention in retrieving results indexed by a particular column?

I often need to retrieve results and access them by a given column.
Is there a way to write this without walking through the whole dataset each time?
I looked into various PDO fetch modes, but nothing jumped out as being simpler than that. Thanks.
function get_groups() {
$stmt = $dbc->prepare("SELECT * FROM groups ORDER BY group_name");
$stmt->execute();
$groups = $stmt->fetchAll();
$return = [];
foreach($groups as $group) {
$return[$group['id']] = $group;
}
return $return;
}
My proposed solution was pretty obsolete. The right solution comes from this answer
$stmt = $pdo->query("SELECT foo,baz FROM bar")
$groupedByFooValuesArray = $stmt->fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE)
to group it by another column, change the first column you select
if your goal is to have your same array indexed by different column values, I think the only option is to actually index them by different column values.
you can do that by controlling by which field the array is returned
function get_groups($sql,$key,$values='') {
if ($values != '') {
$stmt = $dbc->prepare($sql);
$stmt->execute($values);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
else {
$rows = $dbc->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}
foreach ($rows as $row) {
$indexed[$row[$key]] = $row;
}
return $indexed;
}
then, use get_groups to build an indexed array:
$sql = 'SELECT * FROM groups ORDER BY group_name'
var_dump(get_groups($sql,'id'));
There might be a way to store a resultset in some way that you can fetchAll() it multiple times, that would be awesome. but I don't know it.

PHP PDO FetchAll arguments to remove row number from results

I am building a function that acts like Drupal's variable_initialize() function that pulls all key/value pairs into a global variable. I am trying to find the proper parameters I need to put into fetchAll() to remove the row number and get basically what fetch(PDO::FETCH_ASSOC) does but for all returned rows.
I basically want fetchAll to return:
Array {
[name] = value,
[name2] = value2,
[name3] = value3,
}
The variable table is a simple 2 column table (name)|(value)
function variable_init() {
global $db, $variable;
$query = "SELECT * FROM variable";
$stmt = $db->prepare($query);
$stmt->execute();
$result = $stmt->fetchAll(); //need help here
foreach($result as $name => $value) {
$variable[$name] = $value;
}
}
I have tried PDO_COLUMN/PDO_GROUP/etc... but I can't seem to offset the array to remove the row numbers. Thanks.
I think you may be getting confused about what PDOStatement::fetchAll() returns.
The method returns all rows (arrays or objects, depending on fetch style) in an array.
Try this
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($result as $row) {
$variable[$row['name']] = $row['value'];
}

Categories