-
-
Save kefzce/d56272e7b6a741b29fdc7e2acedd3f6b to your computer and use it in GitHub Desktop.
Doctrine yield
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyBundle\Repository; | |
use Doctrine\ORM\EntityRepository; | |
use MyBundle\Doctrine\RepositoryTrait; | |
class MyRepository extends EntityRepository | |
{ | |
use RepositoryTrait; | |
/** | |
* Obtain all data and yield it | |
* | |
* @return \Generator | |
*/ | |
public function yieldAll() | |
{ | |
$qb = $this->createYieldQueryBuilder('m'); | |
$fct = $qb->getQuery()->yieldIterate; | |
yield from $fct(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyBundle\Doctrine; | |
class QueryBuilder extends \Doctrine\ORM\QueryBuilder | |
{ | |
/** | |
* {@inheritdoc} | |
* | |
* The parent method obtain a \Doctrine\ORM\Query object via EntityManager | |
* But this class is final (WTF !!) so to add method without copy/paste existing | |
* code (and kill version upgrade), need to use Closure::bindTo(). | |
* The bindTo closure change the $this into the closure to the parameter object | |
*/ | |
public function getQuery() | |
{ | |
$query = parent::getQuery(); | |
//Add a closure on yieldIterate property to $query object | |
$yieldIterate = $this->generateClosureYieldIterate(); | |
$query->yieldIterate = $yieldIterate->bindTo($query, $query); | |
return $query; | |
} | |
/** | |
* This method is called like iterate() or getResult() froù | |
* the Query returned by getQuery() | |
* So we add a new method which can yield. | |
* We yield from the return of iterate parent method. | |
* | |
* the iterate parent method return an instance of \Doctrine\ORM\Internal\Hydration\IterableResult | |
* This class implement \Iterator (so we can foreach on it), and, mainly, this class | |
* not know all result ! Each this the foreach ask the current value (call next), the | |
* method hydrateRow() of the configurate Hydrator is called. | |
* So we only have one row :) | |
* With that, we just need to yield the row :) | |
* | |
* @return callable | |
*/ | |
protected function generateClosureYieldIterate() | |
{ | |
return function(...$args) { | |
$iterator = parent::iterate(...$args); | |
foreach ($iterator as $row) { | |
yield $row[0]; | |
} | |
}; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyBundle\Doctrine; | |
trait RepositoryTrait | |
{ | |
/** | |
* Based on \Doctrine\ORM\EntityRepository::createQueryBuilder | |
* Override "$this->_em->createQueryBuilder()" to return my QueryBuilder | |
* The QueryBuilder is instancied by the entityManager. So if I don't want | |
* use Closure::bindTo, need to change the entityManager instance to override the method. | |
* It's the correct way for a new project or a clean project. But with a mess | |
* project, I prefer to keep the original EntityManager; Mainly for use that | |
* one to two time in the project. | |
*/ | |
public function createYieldQueryBuilder($alias, $indexBy = null) | |
{ | |
$newQueryBuilder = function() { | |
return new \MyBundle\Doctrine\QueryBuilder($this); | |
}; | |
$newQueryBuilder = $newQueryBuilder->bindTo($this->_em, $this->_em); | |
return $newQueryBuilder() | |
->select($alias) | |
->from($this->_entityName, $alias, $indexBy) | |
; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment