'Trying to iterate over a mongodb cursor twice - failing

I am executing a query against my mongodb database... and then in two separate spots in my front end logic, I need to iterate through the results to extract / show different information.

Problem

The second time I try to iterate through, I get the following error message:

Fatal error: Uncaught MongoDB\Driver\Exception\LogicException: Cursors cannot yield multiple iterators in /var/www/localhost/htdocs/widgets/exception_details.php:46 Stack trace: #0

Backend Code

Here's the logic to query the database in my model:

    try{
        $id = new \MongoDB\BSON\ObjectId($docid);
        $filter = ['_id' => $id];
        $options = [];
        $query = new \MongoDB\Driver\Query($filter, $options);
        $rows = $m->executeQuery('widgets.play_summary', $query);
        return $rows;
    } catch (Exception $e) {
        echo 'Exception:'. $e->getMessage();
        return false;
    }

Front End Logic

Here's the logic that attempts the iteration.

First time:

foreach ($rows as $id=>$value) {
        if ( count($value->unreachable_servers > 0) ) {
            foreach($value->unreachable_servers as $key=>$value) {
                echo "<tr>";
                echo "<td>$value</td>";
                echo "</tr>";
            }
        }
 }

And then later, I try to do this:

$it = new IteratorIterator($rows);  //line 46
$it->rewind();  
foreach ($rows as $id=>$value) {
    if ( count($value->failed_plays > 0) ) { 
            foreach($value->failed_plays as $key=>$value) {
                    echo "<tr>";
                    echo "<td>$value</td>";
                    echo "</tr>";
            }
    }
 }

Additional Information

Commenting out the following lines gives the exact same error, but just on line 48 instead of line 46:

$it = new IteratorIterator($rows);
$it->rewind();  

Any tips would be appreciated.

Thanks.

EDIT 1

As a test, I changed the front end code to use rewind and next like this:

$it = new \IteratorIterator($rows);
$it->rewind(); 
//foreach ($rows as $id=>$value) {
while($value = $it->current()) {
        if ( count($value->unreachable_servers > 0) ) {
            foreach($value->unreachable_servers as $key=>$value) {
                echo "<tr>";
                echo "<td>$value</td>";
                echo "</tr>";
            }
        }
  $it->next();
 }

And for the second loop:

$it = new IteratorIterator($rows);
$it->rewind();  
//foreach ($rows as $id=>$value) { 
while($value = $it->current()) {
    if ( count($value->failed_plays > 0) ) { 
            foreach($value->failed_plays as $key=>$value) {
                    echo "<tr>";
                    echo "<td>$value</td>";
                    echo "</tr>";
            }
    }
    $it->next();
 }

I get the same results. It works the first time but the second time it bombs with the same error message.



Solution 1:[1]

Cursor cannot be iterated twice. Fetch it into array, if it fits into memory:

    $rows = $m->executeQuery('widgets.play_summary', $query);
    return $rows->toArray();

UPDATE

Since the answer still attracts attention 5 years later, there was https://jira.mongodb.org/browse/PHPC-1691 to implement full Iterator interface, not only Traversable.

The implementation does not resolve OPs issue tho.

Technically https://www.php.net/manual/en/class.mongodb-driver-cursor.php does have the rewind method, but it throws an exception Cursors cannot rewind after starting iteration when called after first document was received: https://github.com/mongodb/mongo-php-driver/blob/0960a204b3b60f8a80375abb6390735b95c821f1/src/MongoDB/Cursor.c#L333

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1