2014-04-02 9 views
10

Pracuję nad projektem Symfony2 i postanowiłem użyć KNPPaginatorBundle do zbudowania prostego systemu stronicowania. Stworzyłem więc encję Product i chcę dodać działanie paginatora do indexAction (generowane przez polecenie CRUD).Jak używać KNPPaginatorBundle do dzielenia stron za pomocą repozytorium Doctrine?

// 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 
); 

Działa dobrze, ale chcę użyć repozytorium produktu zamiast tworzyć zapytanie bezpośrednio w kontrolerze. Jak mogę to zrobić ? W rzeczywistości, bezpośrednio dodać zbiór wyników do obiektu paginate jest po prostu zbyt wolny, ponieważ jego ładowanie wszystkich produktów następnie paginacja ArrayCollection.

Z góry dziękuję.

K4

+1

Zauważyłem również tę kwestię wydajności. Problem polega na tym, że ten pakiet potrzebuje całej kolekcji tablic do generowania stronicowania. Jeśli podasz częściowy wynik, paginacja będzie mieć niewłaściwą liczbę stron, ponieważ jest obliczona z całej kolekcji – Chopchop

Odpowiedz

16

Proponuję za pomocą QueryBuilder w ProductRepository a następnie przechodząc że do paginator:

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

W regulatorze:

$products = $productRepository->findAll(); 

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

Dzięki! Zrobiłem to i działa dobrze! Ale jakie są podstawowe różnice między funkcjami createQueryBuilder() i createQuery()?! Czy mogę użyć createNativeQuery()? – K4timini

+2

Utwórz zapytanie, jeśli chcesz sam napisać DQL, normalnie zrobiłbyś to, gdy zapytanie jest wystarczająco proste. Użyj queryBuilder, gdy potrzebujesz skonstruować złożone zapytania z warunkami. Interfejs płynowy jest bardzo wygodny do tego zadania. Dlatego świetnie współpracuje z paginatorem, ponieważ praca z QueryBuilder jest bardzo łatwa! –

+0

Ale jeśli użyjesz znajdź wszystko, to najpierw uzyskasz wszystko w jednym zapytaniu. Czy to prawda? –

3

myślę, że w niektórych przypadkach możemy użyć Closure i przekazać mu obiekt QueryBuilder.

w twojej ProductRepository mógłby zrobić coś takiego:

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(); 
    } 
} 

a potem w 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); 

    // ... 
} 

myślę, że to bardziej elastyczne i można użyć findAllPublished sposób, aby uzyskać zarówno paginowane lub NIE Jeśli chcesz, wyniki są podzielone na strony.

Należy również pamiętać, że podpowiedź typu callable działa w PHP >=5.4! Proszę sprawdzić docs, aby uzyskać więcej informacji.

+0

Inteligentne rozwiązanie! – jmunozco

+0

@jmunozco thanks! Ale prawdopodobnie nie jest najlepszy;) Właściwie to rozwiązanie daje ci pełną kontrolę nad konstruktorem zapytań w kontrolerze, co nie jest dobre, myślę, to znaczy powinieneś używać go mądrze, ale prawdopodobnie zrobić coś bardziej ograniczonego. –

0

W naszym projekcie chcemy uniknąć używania zapytań Doctrine w kontrolerach. Mamy również oddzielne warstwy. Kontrolery nie mogą uzyskać dostępu do bazy danych. Więc umieściłem paginację w repozytorium.

Oto mój kod kontrolera:

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, 
    )); 
} 

I tu jest ważne w moim repozytorium kodu:

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

Gdzie dokładnie $ pages = floor ($ count/$ resultsPerPage); Jest używane? –

+0

Czy ktoś uważa, że ​​posiadanie instancji $ paginator, jak wyżej, jest dużym problemem, wydajnością lub w inny sposób - przeciwstawia się ciągnięciu jej jako usługi, do której trzeba by w jakikolwiek sposób wstrzyknąć usługę knp_paginator do klasy repozytorium? – userfuser

Powiązane problemy