2014-04-02 13 views
10

Ich arbeite an einem Symfony2-Projekt und entschied mich, mit KNPPaginatorBundle ein einfaches Paginierungssystem zu erstellen. Also habe ich eine Product-Entity erstellt und möchte den Paginator zur indexAction-Aktion hinzufügen (generiert vom CRUD-Befehl).Wie verwende ich KNPPaginatorBundle, um Ergebnisse mit Doctrine Repository paginieren?

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

Es funktioniert gut, aber ich möchte das Repository des Produkts verwenden, anstatt die Abfrage direkt in der Steuerung zu erstellen. Wie kann ich das machen ? In der Tat, fügen Sie die Auflistung der Ergebnisse direkt zu dem Objekt paginate ist einfach zu langsam, weil alle Produkte laden dann die ArrayCollection paginieren.

Vielen Dank im Voraus.

K4

+1

ich auch dieses Leistungsproblem bemerkt. Das Problem besteht darin, dass dieses Paket die gesamte Array-Sammlung zum Generieren der Seitennummerierung benötigt. Wenn Sie ein unvollständiges Ergebnis angeben, wird die Seitennummerierung falsch sein, da sie aus der gesamten Sammlung berechnet wird. – Chopchop

Antwort

16

schlage ich QueryBuilder in Ihrem ProductRepository und anschließend das vorbei an den paginator:

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

In der Steuerung:

$products = $productRepository->findAll(); 

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

Vielen Dank! Ich habe es getan, und es funktioniert gut! Aber was sind die grundlegenden Unterschiede zwischen createQueryBuilder() und createQuery()? Kann ich createNativeQuery() verwenden? – K4timini

+2

Abfrage erstellen ist, wenn Sie die DQL selbst schreiben möchten, normalerweise würden Sie das tun, wenn die Abfrage einfach genug ist. Verwenden Sie den QueryBuilder, wenn Sie komplexe Abfragen mit Bedingungen erstellen müssen. Die Fluidschnittstelle ist für diese Aufgabe sehr praktisch. Deshalb funktioniert es gut mit dem Paginator, weil die Arbeit mit dem QueryBuilder sehr einfach ist! –

+0

Aber wenn Sie alle finden, wird es zuerst alles in einer Abfrage bekommen. Ist es wahr? –

3

denke ich, in einigen Fällen, die wir verwenden könnten Closure und übergeben Sie ihm ein QueryBuilder Objekt.

In Ihrem ProductRepository Sie so etwas tun könnte:

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

und dann 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); 

    // ... 
} 

Ich denke, es ist flexibler und man konnte findAllPublished Methode verwenden, um beide paginierte zu bekommen oder NICHT paginierte Ergebnisse, wenn Sie brauchen.

Denken Sie auch daran, dass callable Typ Hinweis arbeiten in PHP >=5.4! Bitte überprüfen Sie docs für weitere Informationen.

+0

Intelligente Lösung! – jmunozco

+0

@junozco danke! Aber wahrscheinlich ist nicht die beste;) Eigentlich, diese Lösung bekommt die volle Kontrolle über Abfrage-Generator in einem Controller, der nicht gut ist, denke ich, d. H. Sie sollten es klug verwenden, aber wahrscheinlich etwas eingeschränkter. –

0

In unserem Projekt wollen wir Doctrine-Abfragen in Controllern vermeiden. Wir haben auch getrennte Schichten. Controller dürfen nicht auf die Datenbank zugreifen. Also habe ich die Seitenzählung in das Repository eingefügt.

Hier mein Code in Controller:

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

Und hier ist der wichtige Code in meinem 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

Wo genau die $ pages = floor ($ count/$ resultsPerPage); wird eingesetzt? –

+0

Hat jemand denken, den $ paginator wie oben eine großes Problem instanziert aufweist, ist, Leistung oder auf andere Weise - im Gegensatz es als Dienst zu ziehen, für die Sie irgendwie brauchen würden den knp_paginator Dienst in die Repository-Klasse zu injizieren? – userfuser