2014-04-02 11 views
10

Sto lavorando a un progetto Symfony2 e ho deciso di utilizzare KNPPaginatorBundle per creare un sistema di impaginazione facile. Così ho creato un'entità prodotto e voglio aggiungere l'impaginatore all'azione indexAction (generata dal comando CRUD).Come utilizzare KNPPaginatorBundle per impaginare i risultati utilizzando Doctrine Repository?

// Retrieving products. 
$em = $this->getDoctrine()->getManager(); 

//$entities = $em->getRepository('LiveDataShopBundle:Product')->findAll(); 

$dql = "SELECT a FROM LiveDataShopBundle:Product a"; 
$entities = $em->createQuery($dql); 

// Creating pagnination 
$paginator = $this->get('knp_paginator'); 
$pagination = $paginator->paginate(
    $entities, 
    $this->get('request')->query->get('page', 1), 
    20 
); 

Funziona bene ma voglio usare repository del prodotto invece di creare la query direttamente nel controller. Come lo posso fare ? Infatti, aggiungere direttamente la raccolta dei risultati all'oggetto paginato è troppo lento perché carica tutti i prodotti quindi impagina ArrayCollection.

Grazie in anticipo.

K4

+1

Ho anche notato questo problema di prestazioni. Il problema è che questo bundle ha bisogno dell'intera collezione di array per generare l'impaginazione. Se fornisci un risultato parziale, la paginazione avrà un numero errato di pagine poiché viene calcolata dall'intera collezione – Chopchop

risposta

16

io suggerisco di usare QueryBuilder nella vostra ProductRepository e passando poi che al Paginator:

ProductRepository extends EntityRepository 
{ 
    // This will return a QueryBuilder instance 
    public function findAll() 
    { 
     return $this->createQueryBuilder("p"); 
    } 
} 

Nel controllore:

$products = $productRepository->findAll(); 

// Creating pagnination 
$paginator = $this->get('knp_paginator'); 
$pagination = $paginator->paginate(
    $products, 
    $this->get('request')->query->get('page', 1), 
    20 
); 
+0

Grazie! L'ho fatto e funziona perfettamente! Ma quali sono le differenze fondamentali tra createQueryBuilder() e createQuery()?! Posso usare createNativeQuery()? – K4timini

+2

Creare una query è quando si vuole scrivere il DQL da soli, normalmente lo si farebbe quando la query è abbastanza semplice. Utilizzare queryBuilder quando è necessario creare query complesse con condizionali. L'interfaccia fluida è molto comoda per questo compito. Ecco perché funziona perfettamente con l'impaginatore, perché lavorare con QueryBuilder è molto semplice! –

+0

Ma se si utilizza find all troverà prima tutto in una query. È vero? –

3

Penso che in alcuni casi potremmo usare Closure e passare ad esso un oggetto QueryBuilder.

Nella tua ProductRepository si potrebbe fare qualcosa di simile:

ProductRepository extends EntityRepository 
{ 
    public function findAllPublished(callable $func = null) 
    { 
     $qb = $this->createQueryBuilder('p'); 

     $qb->where('p.published = 1'); 

     if (is_callable($func)) { 
      return $func($qb); 
     } 

     return $qb->getQuery()->getResult(); 
    } 
} 

e poi in ProductController:

public function indexAction(Request $request) 
{ 
    $em = $this->get('doctrine.orm.entity_manager'); 
    $paginator = $this->get('knp_paginator'); 

    $func = function (QueryBuilder $qb) use ($paginator, $request) { 
     return $paginator->paginate($qb, $request->query->getInt('page', 1), 10); 
    }; 
    $pagination = $em->getRepository('AppBundle:Report')->findAllPublished($func); 

    // ... 
} 

penso che più flessibile e si può usare findAllPublished metodo per ottenere sia impaginato o NOT risultati paginati se necessario.

Ricorda inoltre che il suggerimento tipo callable funziona in PHP >=5.4! Per favore, controlla docs per maggiori informazioni.

+0

Soluzione intelligente! – jmunozco

+0

@jmunozco grazie! Ma probabilmente non è il migliore;) In realtà, questa soluzione ti dà il pieno controllo sul generatore di query in un controller che non è buono, penso, dovresti usarlo in modo intelligente, ma probabilmente fare qualcosa di più limitato. –

0

Nel nostro progetto vogliamo evitare di utilizzare le query di Doctrine nei controller. Abbiamo anche strati separati. I controllori non devono accedere al database. Quindi ho incluso l'impaginazione nel repository.

Qui il mio codice nel controllore:

public function indexAction(Request $request) 
{ 
    $userRepository = $this->get('user_repository'); 
    $page = intval($request->query->get('page', 1)); 
    $pages = 0; 
    $users = $userRepository->findAllPaginated($pages, $page - 1, 10); 

    return $this->render('User:index.html.twig', array(
     'users' => $users, 
     'page' => $page, 
     'pages' => $pages, 
    )); 
} 

Ed ecco il codice di importante nella mia repository:

use Doctrine\ORM\Tools\Pagination\Paginator; 
class UserRepository extends EntityRepository 
{ 
    /** 
    * @return User[] 
    */ 
    public function findAllPaginated(&$pages, $startPage = 0, $resultsPerPage = 5) 
    { 
     $dql = 'SELECT u FROM CoreBundle:User u'; 
     $query = $this->getEntityManager()->createQuery($dql) 
      ->setFirstResult($startPage * $resultsPerPage) 
      ->setMaxResults($resultsPerPage); 

     $paginator = new Paginator($query); 
     $count = $paginator->count(); 
     $pages = floor($count/$resultsPerPage); 

     return $paginator; // on $paginator you can use "foreach", so we can say return value is an array of User 
    } 
} 
+1

Dove esattamente $ pages = floor ($ count/$ resultsPerPage); si usa? –

+0

Qualcuno pensa che avere il $ paginator istanziato come sopra sia un grosso problema, performance o altro - contrario a tirarlo come un servizio, per il quale è necessario in qualche modo iniettare il servizio knp_paginator nella classe repository? – userfuser