9

Below is some poorly written and heavily misunderstood PHP code with no error checking. To be honest, I'm struggling a little getting my head around the maze of PHP->MySQLi functions! Could someone please provide an example of how one would use prepared statements to collect results in an associative array whilst also getting a row count from $stmt? The code below is what I'm playing around with. I think the bit that's throwing me off is using $stmt values after store_result and then trying to collect an assoc array, and I'm not too sure why...

$mysqli = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['db']);
$stmt = $mysqli->prepare("SELECT * FROM licences WHERE generated = ?");
$stmt->bind_param('i', $core['id']);
$result = $stmt->execute();
$stmt->store_result();

if ($stmt->num_rows >= "1") {

    while($data = $result->fetch_assoc()){ 
        //Loop through results here $data[] 
    }

}else{

    echo "0 records found";

}

I feel a little cheeky just asking for code, but its a working demonstration of my circumstances that I feel I need to finally understand what's actually going on. Thanks a million!

Arbiter
  • 456
  • 1
  • 8
  • 20
  • For starters, instead of FETCH_ASSOC or FETCH_NUM you can always use FETCH_ALL, resulting in an array that contains both associative and numbered values. Other option: While youre looping trough the results, just have an index, set it to $i=0 before the loop, then do $i++; inside the loop. Besides that, I don't think I actually understand your question. I'm thinking about it some more, but clarification would be appreciated. – Kjeld Schmidt Apr 06 '14 at 19:46
  • I'm currently getting the error `Fatal error: Call to a member function fetch_assoc() on a non-object`, I thought it was down to me coding incorrectly rather than it actually being a non-object because `num_rows` does not actually return 0. So UI thought this part was wrong `$logData = $result->fetch_assoc()` – Arbiter Apr 06 '14 at 19:52
  • Nope, the problem, then, starts in the `$result = $stmt->execute();` part. It prooobably means, that $results is actually an error, because execute wasn't succesful. When I work with it, this is usually because the query is wrong. Are all the table names and column names correct and in the proper case? – Kjeld Schmidt Apr 06 '14 at 19:55
  • One thing to maybe try is `echo $mysqli->error;` after the prepare and/or execute. – Kjeld Schmidt Apr 06 '14 at 19:59
  • hmmm I see what you mean. Nope, the query looks fine and no errors are output :/ – Arbiter Apr 06 '14 at 20:01
  • You may want to change `if ($stmt->num_rows >= "1")` to `if ($stmt->num_rows >= 1)` `"1"` may be misinterpreted as a string instead of an integer. @Arbiter – Funk Forty Niner Apr 06 '14 at 20:02
  • Mhh, tricky. In those situation, I like to just deconstruct and slowly rebuild my code. For example, I would start by changing my query to just `"SELECT * from licenses"`, then get rid of the bind_param(). See if that does anything. Also, I agree on the "1" - change it to 1. – Kjeld Schmidt Apr 06 '14 at 20:10
  • Hi guys and thanks for your help thus far, I have changed it to a 1 and played around with my code - no luck. I'll keep playing. – Arbiter Apr 06 '14 at 20:37
  • By removing the `WHERE` clause from the query `$stmt->num_rows` changed to `7` which is all the records in the DB table, spot on. Not this is the part that is failing: `while($logData = $result->fetch_assoc()){`. I have also tried `fetch_array`. The error is still `Call to a member function fetch_array()` - is `$result` the wrong thing to fetch the array from? I can't understand why `$result` is not an object? – Arbiter Apr 06 '14 at 20:43
  • Possible duplicate of [How can I put the results of a MySQLi prepared statement into an associative array?](http://stackoverflow.com/questions/994041/how-can-i-put-the-results-of-a-mysqli-prepared-statement-into-an-associative-arr) – Jeff Puckett Jun 01 '16 at 02:22

6 Answers6

17

I searched for a long time but never found documentation needed to respond correctly, but I did my research.

$stmt->get_result() replace $stmt->store_result() for this purpose. So, If we see

$stmt_result = $stmt->get_result();
var_dump($stmt_result);

we get

object(mysqli_result)[3]
  public 'current_field' => int 0
  public 'field_count' => int 10
  public 'lengths' => null
  public 'num_rows' => int 8  #That we need!
  public 'type' => int 0

Therefore I propose the following generic solution. (I include the bug report I use)

#Prepare stmt or reports errors
($stmt = $mysqli->prepare($query)) or trigger_error($mysqli->error, E_USER_ERROR);

#Execute stmt or reports errors
$stmt->execute() or trigger_error($stmt->error, E_USER_ERROR);

#Save data or reports errors
($stmt_result = $stmt->get_result()) or trigger_error($stmt->error, E_USER_ERROR);

#Check if are rows in query
if ($stmt_result->num_rows>0) {

  # Save in $row_data[] all columns of query
  while($row_data = $stmt_result->fetch_assoc()) {
    # Action to do
    echo $row_data['my_db_column_name_or_ALIAS'];
  }

} else {
  # No data actions
  echo 'No data here :(';
}
$stmt->close();
Blaztix
  • 1,034
  • 1
  • 15
  • 25
  • 2
    JFYI, [there is no need to test every mysqli functions's result manually](https://phpdelusions.net/mysqli/error_reporting). Makes your code not that bloated. – Your Common Sense Jan 10 '20 at 09:05
3
$result = $stmt->execute(); /* function returns a bool value */

reference : http://php.net/manual/en/mysqli-stmt.execute.php

so its just sufficient to write $stmt->execute(); for the query execution.


The basic idea is to follow the following sequence :
1. make a connection. (now while using sqli or PDO method you make connection and connect with database in a single step)
2. prepare the query template
3. bind the the parameters with the variable
4. (set the values for the variable if not set or if you wish to change the values) and then Execute your query.
5. Now fetch your data and do your work.
6. Close the connection.


/*STEP 1*/
$mysqli = mysqli_connect($servername,$usrname,$pswd,$dbname);
/*STEP 2*/
$stmt = $mysqli->prepare("SELECT * FROM licences WHERE generated = ?");
/*Prepares the SQL query, and returns a statement handle to be used for further operations on the statement.*/
//mysqli_prepare() returns a statement object(of class mysqli_stmt) or FALSE if an error occurred.
/* STEP 3*/
$stmt->bind_param('i', $core['id']);//Binds variables to a prepared statement as parameters
/* STEP 4*/
$result = $stmt->execute();//Executes a prepared Query
/* IF you wish to count the no. of rows only then you will require the following 2 lines */
$stmt->store_result();//Transfers a result set from a prepared statement
$count=$stmt->num_rows;
/*STEP 5*/
//The best way is to bind result, its easy and sleek
while($data = $stmt->fetch()) //use fetch() fetch_assoc() is not a member of mysqli_stmt class
{ //DO what you wish
  //$data is an array, one can access the contents like $data['attributeName']
}

One must call mysqli_stmt_store_result() for (SELECT, SHOW, DESCRIBE, EXPLAIN), if one wants to buffer the complete result set by the client, so that the subsequent mysqli_stmt_fetch() call returns buffered data.
It is unnecessary to call mysqli_stmt_store_result() for other queries, but if you do, it will not harm or cause any notable performance in all cases.
--reference: php.net/manual/en/mysqli-stmt.store-result.php
and http://www.w3schools.com/php/php_mysql_prepared_statements.asp
One must look up the above reference who are facing issue regarding this, My answer may not be perfect, people are welcome to improve my answer...

2

Your problem here is that to do a fetch->assoc(), you need to get first a result set from a prepared statement using:

http://php.net/manual/en/mysqli-stmt.get-result.php

And guess what: this function only works if you are using MySQL native driver, or "mysqlnd". If you are not using it, you'll get the "Fatal error" message.

PaulJ
  • 1,410
  • 4
  • 25
  • 43
2

You can try this using the mysqli_stmt function get_result() which you can use to fetch an associated array. Note get_result returns an object of type mysqli_result.

$stmt->execute();
$result = $stmt->get_result(); //$result is of type mysqli_result
$num_rows = $result->num_rows;  //count number of rows in the result

// the '=' in the if statement is intentional, it will return true on success or false if it fails.
if ($result_array = $result->fetch_assoc(MYSQLI_ASSOC)) { 
       //loop through the result_array fetching rows.
       // $ rows is an array populated with all the rows with an associative array with column names as the key 
        for($j=0;$j<$num_rows;$j++)
            $rows[$j]=$result->fetch_row();
        var_dump($rows);
   }
else{
   echo 'Failed to retrieve rows';
}
yovsky
  • 111
  • 4
1

If you would like to collect mysqli results into an associative array in PHP you can use fetch_all() method. Of course before you try to fetch the rows, you need to get the result with get_result(). execute() does not return any useful values.

For example:

<?php

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli($config['host'], $config['user'], $config['pass'], $config['db']);
$mysqli->set_charset('utf8mb4'); // Don't forget to set the charset!

$stmt = $mysqli->prepare("SELECT * FROM licences WHERE generated = ?");
$stmt->bind_param('i', $core['id']);
$stmt->execute(); // This doesn't return any useful value
$result = $stmt->get_result();
$data = $result->fetch_all(MYSQLI_ASSOC);

if ($data) {
    foreach ($data as $row) {
        //Loop through results here
    }
} else {
    echo "0 records found";
}

I am not sure why would you need num_rows, you can always use the array itself to check if there are any rows. An empty array is false-ish in PHP.

Dharman
  • 21,838
  • 18
  • 57
  • 107
-2

True, the Databasefunctions are a bit weird. You'll get there.

The code looks a bit iffy, but heres how it works:

A connection is build, a statement prepared, a parameter bound and it's executed, all well.

$result = $stmt->execute(); //execute() tries to fetch a result set. Returns true on succes, false on failure.
$stmt->store_result(); //store_result() "binds" the last given answer to the statement-object for... reasons. Now we can use it!

if ($stmt->num_rows >= "1") { //Uses the stored result and counts the rows.

  while($data = $result->fetch_assoc()){ 
        //And here, the answer-object is turned into an array-(object)
        // which can be worked with nicely.
        //It loops trough all entries in the array.
  }

}else{

   echo "0 records found";
}
Kjeld Schmidt
  • 850
  • 8
  • 18
  • Thanks for your demo, I've answered your comment above. `print_r($result);` returns '1'. – Arbiter Apr 06 '14 at 19:54
  • 39
    This is wrong. In prepared statements, `$stmt->execute()` does NOT retturn a result set, just TRUE or FALSE. – PaulJ Mar 10 '15 at 12:05
  • 13
    The answer still displays the result rows as coming from `$result`, which only contains the boolean value indicating whether the statement was executed successfully. – cornergraf Sep 01 '15 at 11:03
  • 2
    This answer has been up for close to two years, and still gets the occasional upvote/downvote. By far the most upvoted things in this question are the comments pointing out my mistakes. I would like to remind everybody that editing my answer or posting your own remains a possibility even now. – Kjeld Schmidt Jan 31 '17 at 13:55
  • 1
    @KjeldSchmidt Fair point! I have gone with this solution: http://stackoverflow.com/a/28622062/1617737 . There are other good solutions on that page, too. – ban-geoengineering Feb 17 '17 at 10:13