PHP PDO with foreach and fetch

130,254

Solution 1

A PDOStatement (which you have in $users) is a forward-cursor. That means, once consumed (the first foreach iteration), it won't rewind to the beginning of the resultset.

You can close the cursor after the foreach and execute the statement again:

$users       = $dbh->query($sql);
foreach ($users as $row) {
    print $row["name"] . " - " . $row["sex"] . "<br/>";
}

$users->execute();

foreach ($users as $row) {
    print $row["name"] . " - " . $row["sex"] . "<br/>";
}

Or you could cache using tailored CachingIterator with a fullcache:

$users       = $dbh->query($sql);

$usersCached = new CachedPDOStatement($users);

foreach ($usersCached as $row) {
    print $row["name"] . " - " . $row["sex"] . "<br/>";
}
foreach ($usersCached as $row) {
    print $row["name"] . " - " . $row["sex"] . "<br/>";
}

You find the CachedPDOStatement class as a gist. The caching iterator is probably more sane than storing the result set into an array because it still offers all properties and methods of the PDOStatement object it has wrapped.

Solution 2

Executing the same query again only to get the results you already had, as suggested in the accepted answer, is a madness. Adding some extra code to perform such a simple task also makes no sense. I have no idea why people would devise such complex and inefficient methods to complicate such primitive, most basic actions.

PDOStatement is not an array. Using foreach over a statement is just a syntax sugar that internally uses the familiar one-way while loop. If you want to loop over your data more than once, simply select it as a regular array first

$sql = "SELECT * FROM users";
$stm = $dbh->query($sql);
// here you go:
$users = $stm->fetchAll();

and then use this array as many times as you need:

foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
echo "<br/>";
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

Also quit that try..catch thing. Don't use it, but set the proper error reporting for PHP and PDO

Solution 3

This is because you are reading a cursor, not an array. This means that you are reading sequentially through the results and when you get to the end you would need to reset the cursor to the beginning of the results to read them again.

If you did want to read over the results multiple times, you could use fetchAll to read the results into a true array and then it would work as you are expecting.

Solution 4

$users = $dbh->query($sql);
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

Here $users is a PDOStatement object over which you can iterate. The first iteration outputs all results, the second does nothing since you can only iterate over the result once. That's because the data is being streamed from the database and iterating over the result with foreach is essentially shorthand for:

while ($row = $users->fetch()) ...

Once you've completed that loop, you need to reset the cursor on the database side before you can loop over it again.

$users = $dbh->query($sql);
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
echo "<br/>";
$result = $users->fetch(PDO::FETCH_ASSOC);
foreach($result as $key => $value) {
    echo $key . "-" . $value . "<br/>";
}

Here all results are being output by the first loop. The call to fetch will return false, since you have already exhausted the result set (see above), so you get an error trying to loop over false.

In the last example you are simply fetching the first result row and are looping over it.

Share:
130,254

Related videos on Youtube

Eathen Nutt
Author by

Eathen Nutt

Updated on February 15, 2022

Comments

  • Eathen Nutt
    Eathen Nutt about 2 years

    The following code:

    <?php
    try {
        $dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
        echo "Connection is successful!<br/>";
        $sql = "SELECT * FROM users";
        $users = $dbh->query($sql);
        foreach ($users as $row) {
            print $row["name"] . "-" . $row["sex"] ."<br/>";
        }
        foreach ($users as $row) {
            print $row["name"] . "-" . $row["sex"] ."<br/>";
        }
        $dbh = null;
    }
    catch (PDOexception $e) {
        echo "Error is: " . $e-> etmessage();
    }
    

    Output:

    Connection is successful!
    
    person A-male
    person B-female
    

    Running "foreach" twice is not my purpose, I'm just curious why TWO "foreach" statements only output the result once?

    Following is the similar case:

    <?php
    try {
        $dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
        echo "Connection is successful!<br/>";
        $sql = "SELECT * FROM users";
        $users = $dbh->query($sql);
        foreach ($users as $row) {
            print $row["name"] . "-" . $row["sex"] ."<br/>";
        }
        echo "<br/>";
        $result = $users->fetch(PDO::FETCH_ASSOC);
        foreach($result as $key => $value) {
            echo $key . "-" . $value . "<br/>";
        }
        $dbh = null;
    }
    catch (PDOexception $e) {
        echo "Error is: " . $e-> etmessage();
    }
    

    Output:

    Connection is successful!
    
    person A-male
    person B-female
    
    SCREAM: Error suppression ignored for
    Warning: Invalid argument supplied for foreach()
    

    But when I delete the first "foreach" from the above codes, the output will become normal:

    <?php
    try {
        $dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
        echo "Connection is successful!<br/>";
        $sql = "SELECT * FROM users";
        $users = $dbh->query($sql);
    
        echo "<br/>";
        $result = $users->fetch(PDO::FETCH_ASSOC);
        foreach($result as $key => $value) {
            echo $key . "-" . $value . "<br/>";
        }
        $dbh = null;
    }
    catch (PDOexception $e) {
        echo "Error is: " . $e-> etmessage();
    }
    

    Output:

    Connection is successful!
    
    user_id-0000000001
    name-person A
    sex-male
    

    Why does this happen?

  • Eathen Nutt
    Eathen Nutt about 11 years
    Thank you for the tip, learning the prepare and execute is my next step.
  • Eathen Nutt
    Eathen Nutt about 11 years
    Thanks, although I can't understand it clearly but your example is definitely useful to me in the future.
  • hakre
    hakre about 11 years
    Please see yet another edit, I had a typo: Use $users->execute(); after the first foreach to reset the cursor to the beginning of the result-set. This is most likely what you're looking for. The cache is a more specific solution and not really necessary as you can do $users->execute();. I added it just for the general example.
  • Eathen Nutt
    Eathen Nutt about 11 years
    thank you, the first one seems to be clearer to me, I will look into the execute function in php manual.
  • hakre
    hakre about 11 years
    Yes, it's much more straight forward. The method is here: php.net/manual/en/pdostatement.execute.php
  • Norman
    Norman over 10 years
    @YourCommonSense Why do you have two for each loops in your answer? I'm not fault finding, just learning...
  • John
    John over 7 years
    using pdo->query() is definitely NOT a wrong way of using the library. exec() and query() are there to be used. The only wrong usage was trying to go through the statement two times, he should just have fetchAll() and then he can iterate several times through the array.. If you stay to your word that using query() is wrong, give a proper reason please.
  • showdev
    showdev about 7 years
    I too am unclear on what is wrong with using query(), as mentioned in your tutorial. Is there something about this particular implementation that's not ideal?
  • Your Common Sense
    Your Common Sense about 7 years
    @showdev you are right. I don't remember the context o this answer but definitely it looked alien, so I edited it
  • Neal Davis
    Neal Davis over 5 years
    Hi Common Sense can you expand on the comment "quit that try..catch thing" more? Do you have a good link to the correct way to catch errors when using PHP/PDO? Thanks
  • Your Common Sense
    Your Common Sense over 4 years
    @NealDavis yes, I do. See the link added to the answer
  • Your Common Sense
    Your Common Sense about 2 years
    There is no PHP function getAllRecords() (and shouldn't be)
  • Mubashir Majeed
    Mubashir Majeed about 2 years
    it's a self made function which will get all records from database
  • Your Common Sense
    Your Common Sense about 2 years
    Yes. But this answer helps no one because getAllRecords is not a php function. And also, this function is very bad bacause it allows SQL injection.
  • Mubashir Majeed
    Mubashir Majeed about 2 years
    Okay, i'l change it