How does looping though a PHP array using While() work? - php

I have found some PHP code that connects to a MySQL database and gets the column CityName for each row of the table Cities. I'm curious why while() loop is used and not for() or foreach. So here are my questions regarding how while() works in case of looping through arrays:
First, isn't $row variable an 2D array which it's rows contains the list of cities from the SQL query and it's columns contain the columns of each row of the query?
If this is the case, couldn't for() or foreach() be used to loop through the $row array?
Second, how does while() know when the array ends using only $row = $stmt->fetch_assoc() in the while()'s first line in order to end the loop?
Third, how does while() move to the next row of the $row array without using next() at the end of the loop?
And last but not least, how does echo $row['CityName']; output the city name of each row of the $row array without specifying the row of the array to use but only it's column CityName?
Thanks for any answers.
$query="SELECT CityName FROM Cities";
if($stmt = $connection->query("$query"))
{
while ($row = $stmt->fetch_assoc())
{
echo $row['CityName'];
}
}
else
{
echo $connection->error;
}

You could loop through $row, because it is an array (a simple array, not a 2d array); but you're not looping through an array called $row with the while, you're iterating over the resultset returned by $stmt->fetch_assoc() - which isn't an array- and assigning the value of a single returned row to $row in that statement (note the = for assignment) from the resultset.
while itself doesn't magically move any pointer; it's the call to $stmt->fetch_assoc() that not only returns a single row result, but moves the resultset pointer to the next result (and determines when it has reached the end of the resultset)

while($this_is_true){
// do this
}
# if the condition is not true for this while stmt, it will not execute (not even once)
do{
// stuff here
} while($thisIsTrue);
# even if the condition isn't true, a do-while executes at least once (the first time)
The point is that they are using a while() because it's a lazy way to say "only do it if it's true", so they don't have to check if it's true before they loop through it. WIth a for loop, you need to know the number of results you are looping through. For a do, you need to know that there are results before you attempt to use the results (echo). So, you use a while() to check if it's valid and then execute it, with just that piece of code.
Personally, I like to do...
if($query->num_rows > 0){
$query->bind_result($bindvar);
while($query->fetch()){
// do stuff
}
$query->close();
} else {
// no results found
}

You can use foreach instead of while. At least if stmt is a mysqli_result and you PHP version is not terribly outdated (newer than 5.4). In that case your loop would be:
foreach($stmt as $row)
{
echo $row['CityName'];
}
But this does not mean that $stmt is a 2D array. It's an object that implements the Traversable interface. That means that the foreach loop will implicitly invoke the required methods.

Related

Query display result with while() or foreach()

I was messing around with how to do queries from MySQL and show them on PHP and I stumbled upon something:
This is the table I'm doing the query to:
$query = mysqli_query($conexion, "SELECT * FROM notas");
while($nota = mysqli_fetch_assoc($query)){
var_dump($nota);
echo $nota["Descripcion"];
}
Whenever I use a while() to display all the results of the query, it works. This table have 2 rows and both of them are showing.
Result of the var_dump($notas):
But whenever I use a foreach(), it just returns me the last result of the query.
$query = mysqli_query($conexion, "SELECT * FROM notas");
foreach(mysqli_fetch_assoc($query) as $valor){
var_dump($valor);
}
Result of the var_dump($valor):
Is there any reason why? I'm doing something wrong in the foreach() loop? I really can't tell. I would just say "fudge it", accept it and only use while loops to display queries, but, you know, want to know if I was doing something wrong or not understanding something.
The second version is looping through the columns, not the rows. It's equivalent to:
$row = mysqli_fetch_assoc($query);
foreach ($row as $valor) {
var_dump($valor);
}
You can see here that it's just fetching one row, which is an associative array, then looping through the elements of that array.
foreach (<expression> as <variable>) doesn't re-evaluate the expression every time through the loop. It executes it once, saves that array, then loops through the array elements.
The mysqli_result object is iterable, so you can do:
foreach($query as $valor) {
var_dump($valor);
}
You can also call mysqli_fetch_all($query), which will return a 2-dimensional array of all the results, and then loop through that. But if the query returns many results, this will use lots of memory.

returning results from a column into an array

I am very new to PHP and I realise I have a lot to learn, especially about SQL injection attacks and the new php 5.5 oo stuff.
However, whilst I am learning I try to play about a bit and experiment and I have come across a problem. I need to get all results from one column (team_name) of my query into an array (zero indexed would be easier) so that I can put those results into a foreach loop to create a fixture list. For example - this is what I would like to do with the data...
foreach ($teams as $team) {
foreach ($teams as $opposition) {
if ($team != $opposition) {
echo "$team versus $opposition";
}
}
}
So my sql query is as follows...
$query="select * from pool_a";
$result=mysql_query($query);
$num=mysql_num_rows($result);
Then I can easily output the data I am looking for as follows;
echo "<table>";
for ($i=0;$i<$num;$i++) {
$teams=mysql_result($result,$i,'team_name');
echo "<tr><td>$teams</tr>";
}
echo "</table>";
But what I can't do is get the data into an array. I thought that data that came back from a msql_query such as the above always came back as an array, no matter the number of results, but if I run an is_array() check on $teams, it returns false.
I have tried using explode (after adding a comma after each team name and trimming the last one off) and then using a foreach loop but it only ever returns the value associated with the index [0]. I should add here that although the key is 0, the actual value I get is the LAST in the list when echoed as above, which I also don't understand.
Any help would be much appreciated.
Thanks
$team_names = array();
while ($row = mysql_fetch_assoc($result)) {
$team_names[] = $row['team_name'];
}
mysql_fetch_assoc, mysql_fetch_row, and mysql_fetch_array all return a row as an array (they differ in how it's indexed). But mysql_result just returns a single item from the result, so it doesn't return an array.
You could do the same thing as above with your use of mysql_result, but you still have to push them onto the result array yourself:
for ($i=0;$i<$num;$i++) {
$team=mysql_result($result,$i,'team_name');
$team_names[] = $team;
}
To get the results of the query into an array, you can do this
$query="select * from pool_a";
$result=mysql_query($query);
$result_arr = mysql_fetch_array($result);
$num=mysql_num_rows($result);
However, consider using the PDO drivers to connect to the database. It's far superior to doing it procedurally, and it's safer as Prepared Statments will help protect you against injection attacks. The method you're using is deprecated.
Instead of using mysql_result try using mysql_fetch_array($result, MYSQL_ASSOC)
This function will give you an array of whatever your result from MySQL is.

Most efficient way to dump entire mysql result set into an array?

I am trying to put the entire results set into an array as follows:
while($myres[]=$result->fetch_array(MYSQLI_ASSOC));
This works ok but if I have 5 rows returns in my results the array has 6 index the last being null. So it seems that my array is always one index too big.
I could use num_rows to loop the results but this requires me setting up my own counter and incrementing it, I like the shorthand efficiency of my line above but how to stop it populating the last index with a null set.
This is an alternative to me using fetch_all which I discovered requires a special driver which not all php servers have installed.
I would just use multiple lines
$myres = array();
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$myres[] = $row;
However, if you have to have it without the extra array assignment line
while ($row = $result->fetch_array(MYSQLI_ASSOC) && $myres[] = $row) {
but that is more or less the same.
The expression
while($myres[]=$result->fetch_array(MYSQLI_ASSOC));
does the following:
execute $result->fetch_array(MYSQLI_ASSOC)
assign the result of this expression to $myres[], thereby appending an entry to the $myres array
pass the result of $result->fetch_array(MYSQLI_ASSOC) as a condition to the while statement and determine whether the loop should be exited.
Notice that appending an entry to the $myres array happens before determining if the while loop should be exited. This is where your extra row comes from.
You will not lose any efficiency by doing it the classical way:
while ($record = $result->fetch_array(MYSQLI_ASSOC)) {
$myres[] = $record;
}

Use a foreach loop instead of while with myslqli_fetch_array()

Okay, so I realize that when do:
//connection code;
//query code;
//$result= mysqli_query();
$row= mysqli_fetch_array($result);
you create an associative array where the column names from your table are the keys for the data in the respective row.
Then you can use:
while ($row= mysqli_fetch_array($result))
{
//code to echo out table data.
}
My question is how does the while loop go to the next row after each iteration? I thought that was what foreach loops were for?
From http://www.php.net/manual/en/function.mysql-fetch-array.php
array mysql_fetch_array ( resource $result [, int $result_type = MYSQL_BOTH ] )
Returns an array that corresponds to the fetched row and moves the internal data pointer ahead.
Many functions that return a result set do so by returning an array that you can do a foreach() on like you are used to. This is not always the case however, especially with database functions. mysqli_fetch_array fetches just a single row, or returns boolean false if there are no more remaining. This is how the loop works: the expression evaluates to true as long as there is a row to process.
The reason for this construction is mainly efficiency. Fetching database rows can be a performance critical operation, and there are cases where not all rows are needed. In these situations this approach will give more flexibility. Fetching rows one-by-one is also more memory-friendly since not all result data will have to be loaded into memory at once.
Mysqli actually has a function that does fetch the entire result set in an array: mysqli_fetch_all. You will be able to foreach() over that.
mysql_fetch_array simply fetches the next row of the result set from your mysql query and returns the row as an array or false if there are no more rows to fetch.
The while loops continually pulls the results, one at a time from the result set and continues until mysql_fetch_array is false.
A foreach loop loops through each value of an array. As mysql_fetch_array only pulls one result and therefore the value of count($row) would be 1 every time.
Each time the while loop runs, it executes the function mysql_fetch_array and gets the next result. It does that until there aren't more results to show.
mysql_fetch_array returns an array of strings that corresponds to the fetched row, or FALSE if there are no more rows. If row exists then get data.
I hope this has answered you q. Its hard to understand what you mean
This part fetches one row at a time
$row = mysqli_fetch_array($result);
Putting it into a while loop makes it fetch one row at a time, until it does not fetch a row because there are no more to be fetched.
The alternative would be to fetch all the rows, then loop through them with a foreach
$rows = mysql_fetch_all($result);
foreach($rows as $row){
// do something with row
}
For this to work, you have to make yourself a mysql_fetch_all function, which of course has the original while loop in it...
function mysql_fetch_all($result)
{
$all = array();
while($thing = mysql_fetch_assoc($result)) {
$all[] = $thing;
}
return $all;
}
This works due to the SQL connector storing the current state of the query (i.e. the next result row to return) inside the result.
If you want a similar example, it works like reading from a file, where you're able to use similar constructs:
while ($line = fgets($fp, 1000)) {
// ...
}
Behind the scenes (and depending on the language, interpreter, compiler, etc.) for and while essentially result in the same code. The difference is, depending on what your code should do, one approach could be more readable than the other.
Take the following two loops as an example. Both do exactly the same.
for ($i = 0; $i < 10; $i++) {
// ...
}
$i = 0;
while ($i < 10) {
// ...
$i++;
}

Second while loop not running. Why?

I have two while loops running one after the other (not inside of each other) - I've simplified the code a bit so that only the important parts of it are listed below. The problem arises when I compare the two echoed queries because the 2nd while loop apparently isn't running at all.
I read somewhere that someone got around the problem by using a for loop for the second one but I want to get down to why exactly the second while loop is not running in my code.
$query_work_title = "SELECT title FROM works WHERE ";
while ($row = mysql_fetch_assoc($result_work_id)) {
$query_work_title .= "OR '$work_id' ";
}
echo $query_work_title;
echo '<br />';
$result_work_title = mysql_query($query_work_title) or
die(mysql_error($cn));
// retrieve the authors for each work in the following query
$query_author_id = "SELECT author_id FROM works_and_authors WHERE ";
while ($row = mysql_fetch_assoc($result_work_id)) {
$query_author_id .= "work_id = 'hello' ";
}
echo $query_author_id;
The MySQL extension keeps track of an internal row pointer for each result. It increments this pointer after each call to mysql_fetch_assoc(), and is what allows you to use a while loop without specifying when to stop. If you intend on looping through a result set more than once, you need to reset this internal row pointer back to 0.
To do this, you would mysql_data_seek() after the first loop:
while ($row = mysql_fetch_assoc($result_work_id)) {
$query_work_title .= "OR '$work_id' ";
}
mysql_data_seek($result_work_id, 0);
You've already looped through the result rows, so it's at the end and returns FALSE. (That's why it exited the loop the first time.)
To reset the internal pointer to the beginning of the result set, use mysql_data_seek().
mysql_data_seek($result_work_id, 0);
After the first while() loop completes, the internal pointer in the MySQL result is at the end of itself. You need to tell it to go back to the beginning using mysql_data_seek() between the first and second loops:
mysql_data_seek($result_work_id, 0);
You have already reached the end of your result set, but you can use mysql_data_seek to reset it.
// query your database
$result = mysql_query(...);
// loop through results
while(($row = mysql_fetch_assoc($result))) {
}
// reset result set
mysql_data_seek($result,0);
// loop again
while(($row = mysql_fetch_assoc($result))) {
}
According to your posted code, your SQL will look something like:
SELECT title FROM works WHERE OR '1'
That query will result in an error so your script shouldn't be getting past that point.
Even if it does, your second loop:
while ($row = mysql_fetch_assoc($result_work_id))
is using a result handle that has already been completely iterated by the first loop. By the the time the second loop tries to use it, mysql_fetch_assoc will return FALSE because there are no more rows to fetch. This will cause the second loop to exit immediately.
If both while loops need to access the same rows, combine their logic so the rows only need to be iterated over one time.
mysql_fetch_assoc steps through the results, right? It's already at the end on the second while loop, so it does nothing.

Categories