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 MongoDBDriverExceptionLogicException: 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 MongoDBBSONObjectId($docid); $filter = ['_id' => $id]; $options = []; $query = new MongoDBDriverQuery($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.
Advertisement
Answer
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