Jens
Jens

Reputation: 5877

Is it possible to use Doctrine2 batch processing seamlessly with Twig's for tag?

I have certain use cases where I need to show datasets without pagination. In order to save memory I would rather use Doctrine's batch processing features (the query iterator).

I want to know if twig provides any mechanism (writing my own extension is ok) to allow using the for tag with an iterator result set just as I would with any other collection.

Then in my extension (or whatever handles the iteration process) I would detach the objects as they are used.

So far, I think my only option is creating a custom for tag, as I don't think twig's for tag handles this.

Upvotes: 2

Views: 1282

Answers (1)

Jens
Jens

Reputation: 5877

Considering that:

  1. Doctrine's iterator uses PDO's fetch method (it uses only one object at a time)
  2. Doctrine's iterator implements PHP's Iterator interface

Instead of passing:

$query->getResult()

to twig you can just pass:

$query->iterate()

Then in twig instead of doing:

{% for item in result %}
     {# do work with item #}
{% endfor %}

It should be:

{% for item in result %}
     {# doctrine's iterator yields an array for some crazy reason #}
     {% set item = item[0] %} 

     {# do work with item #}

     {# the object should be detached here to avoid staying in the cache #}
{% endfor %}

Besides, the loop.last variable stops working, so if you use it you should figure out another way to solve your problem.

Finally, instead of writing a custom twig tag, I just created a Decorator for doctrines iterator to handle the extra stuff that I need, the only still broken is the loop.last var:

class DoctrineIterator implements \Iterator {

    public function __construct(\Iterator $iterator, $em) {
        $this->iterator = $iterator;
        $this->em = $em;
    }

    function rewind() {
        return $this->iterator->rewind();
    }

    function current() {
        $res = $this->iterator->current();
            //remove annoying array wrapping the object
        if(isset($res[0])) 
            return $res[0];
        else
            return null;
    }

    function key() {
        return $this->iterator->key();
    }

    function next() {
            //detach previous entity if present
        $res = $this->current();
        if(isset($res)) {
            $this->em->detach($res);
        }
        $this->iterator->next();
    }

    function valid() {
        return $this->iterator->valid();
    }
}

Upvotes: 5

Related Questions