using yield generator with mongodb
tl;dr
(don't even) try!
EDIT: Thanks to @jmikola feedback, it appears that a simple fix is to put the selectCollection
call out of the generator function!
<?php
public function findBy($class, $id)
{
$events = $this->events->selectCollection($class)->find([
'emitter_id' => (string)$id,
]);
if (0 === $events->count()) {
throw new NoResult;
}
return $this->iterate($events);
}
private function iterate(\MongoCursor $events)
{
foreach ($events as $event) {
yield $this->serializer->unserialize($event);
}
}
MongoCollection::find
returns MongoCursor
instances.
Those are iterators.
Why on earth would I use a generator (which is an Iterator
) for something that is an iterator already?
I can alrady iterate my results one by one, without loading everything in memory.
Well, as stated in another post,
sometimes you have to do extra stuff on each result.
Hydration for example.
I could wrap the mongo iterator in a special iterator, that iterates over its internal iterator,
and hydrates the current result before returning it.
BTW, there is a class for that! That’s what IteratorIterator
s are made for.
But Generators are made to avoid writing such Iterator classes by hand!
The problem
It’s all nice, but this won’t work, currently:
<?php
foreach ((new \MongoDB(new \MongoClient, 'test'))->selectCollection('test')->find(['provider_id' => (string)$id,]) as $document) {
yield $this->serializer->unserialize($document);
}
And this is due to known issues, that are registered here:
- https://jira.mongodb.org/browse/PHP-977
- https://jira.mongodb.org/browse/PHP-820
- and maybe others ?
The solution
Instead you have to fallback to the pre-generator era, and write your own IteratorIterator:
<?php
final class CursorIterator extends \IteratorIterator
{
public function __construct(\Traversable $t, $serializer)
{
parent::__construct($t);
$this->serializer = $serializer;
}
public function current()
{
return $this->serializer->unserialize(parent::current());
}
}
Conclusion
Generators are not yet the silver bullet :)
Of course, I’m not blaming anything nor anyone. It’s all pure happiness to be able to play with all this good work!
I’m just sharing my thoughts and experience.
Talking about that, I’d like to hear what @jmikola is thinking about that?
PS: You can also have a look at the complete implementation.