Can't get doctrines paginator working with Symfony

2.8k views Asked by At

Got 2 classes Item and Categoy, an item can have more categories and a category can have multiple items. Here is what the classes look like:

Item class:

<?php

namespace m\r\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="tblItems")
 */
class Item {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=200)
     */
    protected $title;

    /**
     * @ORM\ManyToMany(targetEntity="Category")
     * @ORM\JoinTable(name="Item_Category",
     *      joinColumns={@ORM\JoinColumn(name="Item_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="Category_id", referencedColumnName="id")}
     *      )
     */
    private $categories;

    public function __construct() {
        $this->categories = new \Doctrine\Common\Collections\ArrayCollection();
    }
    // getters and setters removed to keep post short
    // Add categories removed
    // Remove categories removed
}
?>

Category class:

<?php

namespace m\rBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="tblCategory")
 */
class Category {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=50)
     */
    protected $CategoryName;

    /**
     * @ORM\ManyToMany(targetEntity="Item")
     * @ORM\JoinTable(name="Item_Category",
     *      joinColumns={@ORM\JoinColumn(name="Category_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="Item_id", referencedColumnName="id")}
     *      )
     */
    private $items;

    public function __construct() {
        $this->items = new \Doctrine\Common\Collections\ArrayCollection();
    }
    // bunch of getters and setters removed to keep post short(and addCategory)
}
?>

The controller:

public function indexAction() {
    $dql = "select i, c FROM mrBundle:Item i  join i.categories c";
    $query = $this->getDoctrine()->getManager()->createQuery($dql)
            ->setFirstResult(0)
            ->setMaxResults(5);
    $paginator = new Paginator($query, $fetchJoinCollection = true);
    var_dump(count($paginator));// shows 2143 no matter what setMaxResults is
    return "Doest really work";
}

[UPDATE]

While posting this question I tried one more thing that seems to help:

    var_dump(count($paginator->getIterator()));

This gives 5 when setMaxResults is set to 5 and 500 when setMaxResults is set to 500. Have to check what exactly is returned and if I can access the categories of each item when showing them. Tried to do a var_dump on the paginator object for inspection but that crashes Firefox and brings down Fedora if I don't xkill it quick enough.

3

There are 3 answers

1
Florian Klein On BEST ANSWER

So, Based on what I read form the code,

if controller returns array('items' => $paginator),

you can use {% for item in items %} because paginator implements IteratorAggregate.

Next: When you use count($paginator) it will call count (implements \Countable).

This method does a real COUNT() using SQL, and behaves differently than count($paginator->getIterator()) which counts only the number of results).

One point, did you try to put fetchJoinCollection to false ? // shouldn't but anyway

0
HMR On

Yea, looks like $paginator->getIterator() returns the right amount of records and the twig form can get to the categories. Here is the the code:

public function indexAction() {
    $dql = "select i, c FROM mrBundle:Item i  join i.categories c";
    $query = $this->getDoctrine()->getManager()->createQuery($dql)
            ->setFirstResult(0)
            ->setMaxResults(5);
    $paginator = new Paginator($query, $fetchJoinCollection = true);
    return $this->render('mrBundle:Default:index.html.twig', 
            array('items' => $paginator->getIterator()));
}

Twig file:

<ul id="items">
{% for item in items %}
        <li>
            <a href="{{ path('mr_open',{'id': item.id}) }}">
              {{ item.title }}</a>
            {% for category in item.categories %}
                {{category.CategoryName}}
            {% endfor %}
        </li>
{% endfor %}
</ul>
0
websky On

This my paginator, maybe useful to someone:

namespace XXX\YourBundle\Library;

/**
 * @author websky
 * 
 * Licensed MIT
 * Paginator 
 * 
 * instruction:
 * 1. Controller
 * use XXX\YourBundle\Library\Paginator;
 * new Paginator(data_array, limit, $current);
 * and metods
 * createPaginator()
 * createPaginatorData()
 * 
 * example:
 * controller
 * use XXX\YourBundle\Library\Paginator;
 * $page = ($request->query->get('page') ? $request->query->get('page') : 1);
 * $paginator = new Paginator($this->resultQueryArray(), 1, $page);
 * return $this->render('YourBundle:Your:your.html.twig', array('list' => $paginator->createPaginator(), 'resultData' => $paginator->createPaginatorData()));
 * 
 * 2. View - example
 * {% if resultData.allElements >= 2 %}
 *           <section class="pagination">
 *               {% if resultData.first == resultData.current %}
 *                   <a href="#" class="firstoff"></a>
 *               {% else %}
 *                   <a href="{{path('your_route',{'page' : resultData.first})}}" class="first"></a>
 *               {% endif %}    
 *               {% if resultData.previous == 0 %}
 *                   <a href="#" class="prevoff"></a>
 *               {% else %}
 *                   <a href="{{path('your_route',{'page' : resultData.previous})}}" class="prev"></a>
 *               {% endif %}    
 *               {% for i in 1..resultData.allElements %}
 *                   {% if resultData.allElements < 8 %}
 *                       {% if i == resultData.current %}
 *                           <a href="{{path('your_route',{'page' : i})}}"  class="page-number active">{{ i }}</a>
 *                       {% else %} 
 *                           <a href="{{path('your_route',{'page' : i})}}"  class="page-number">{{ i }}</a>
 *                       {% endif %}
 *                   {% else %}
 *                       {% if i == resultData.current %}
 *                           <a href="{{path('your_route',{'page' : i})}}"  class="page-number active">{{ i }}</a>
 *                       {% elseif (i == (resultData.current - 3)) or (i == (resultData.current - 2)) or (i == (resultData.current - 1)) or (i == (resultData.current + 1)) or (i == (resultData.current + 2)) or (i == (resultData.current + 3)) %}
 *                          <a href="{{path('your_route',{'page' : i})}}"  class="page-number">{{ i }}</a>         
 *                       {% endif %}
 *                   {% endif %}
 *                   
 *               {% endfor %} 
 *               of <a href="{{path('your_route',{'page' : resultData.allElements})}}"  class="page-number">{{resultData.allElements}}</a>
 *               {% if resultData.next  %}      
 *                   <a href="#{{path('your_route',{'page' : resultData.next})}}"  class="next"></a>
 *               {% else %}
 *                   <a href="#" class="nextoff"></a>
 *               {% endif %}
 *               {% if resultData.last == resultData.current %} 
 *                   <a href="#" class="lastoff"></a>  
 *               {% else %}
 *                   <a href="{{path('your_route',{'page' : resultData.last})}}" class="last"></a>
 *               {% endif %}
 *           </section>
 *           {% endif %}
 */
class Paginator 
{
    protected $limit;
    protected $current;
    protected $previous;
    protected $next;
    protected $jump;
    protected $last;
    protected $data;
    protected $numberElements;
    protected $allElements;
    protected $sourceLength;

    /**
     * Pagination construct object
     * @param array $data
     * @param integer $limit
     * @param integer $current
     */
    public function __construct($data, $limit = 5, $current = 1)
    {
        $this->data = $data;
        $this->limit = $limit;
        $this->current = $current;
        $this->sourceLength = count($data);
        $this->setAllElements();
        $this->setNumberElements();
    }

    /**
     * Set limit
     * @param integer $limit
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;

        return $this;
    }

    /**
     * Get limit
     * @return integer
     */
    public function getLimit()
    {
        return $this->limit;
    }  

    /**
     * Set current
     * @param integer $current
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setCurrent($current)
    {
        $this->current = $current;

        return $this;
    }

    /**
     * Get current
     * @return integer
     */
    public function getCurrent()
    {
        return $this->current;
    }

    /**
     * Set previous
     * @param integer $previous
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setPrevious($previous)
    {
        $this->previous = $previous;

        return $this;
    }

    /**
     * Get previous
     * @return integer
     */
    public function getPrevious()
    {
        return $this->previous;
    }

    /**
     * Set next
     * @param integer $next
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setNext($next)
    {
        $this->next = $next;

        return $this;
    }

    /**
     * Get next
     * @return integer
     */
    public function getNext()
    {
        return $this->next;
    }

    /**
     * Set last
     * @param integer $last
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setLast($last)
    {
        $this->last = $last;

        return $this;
    }

    /**
     * Get last
     * @return integer
     */
    public function getLast()
    {
        return $this->last;
    }

    /**
     * Set AllElements
     * These are all elements of an array.
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setAllElements()
    {
        $this->allElements = count($this->data);
        return $this;
    }

    /**
     * Set NumberElements
     * This is the number of pages for the paginator.
     * @return \XXX\YourBundle\Library\Paginator
     */
    public function setNumberElements()
    {
        $this->numberElements = ceil ($this->allElements / $this->limit);
        return $this;
    }

    /**
     * Creates an array of data to control.
     * @return array
     */
    public function createPaginatorData()
    {
        $shareArray['limit'] = $this->limit;
        $shareArray['allElements'] = $this->numberElements;
        $shareArray['current'] = $this->current;
        $shareArray['first'] = 1;
        $shareArray['previous'] = $this->current != 0 ? $this->current - 1 : false;
        $shareArray['next'] = $this->current != $this->numberElements ? $this->current + 1 : false;
        $shareArray['last'] = $this->numberElements;
        $shareArray['sourcelength'] = $this->sourceLength;

        return $shareArray;
    }

    /**
     * Returns the elements of the page.
     * @return array
     */
    public function createPaginator()
    {
        $result = array();
        for($i=(($this->current * $this->limit) - $this->limit); $i < ($this->current * $this->limit); $i++){
            if(array_key_exists($i, $this->data)){
                $result[] = $this->data[$i];
            }
        } 
        return $result;
    }
}