问题
I'm working on a Symfony2 project and I decided to use KNPPaginatorBundle to build an easy pagination system. So I created a Product entity and I want to add the paginator to indexAction action (generated by CRUD command).
// 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
);
It works fine but I want to use the Product's repository instead of creating the query directly in the controller. How can I do that ? In fact, directly add the collection of results to the paginate object is just too slow because its load all products then paginate the ArrayCollection.
Thanks in advance.
K4
回答1:
I suggest using QueryBuilder
in your ProductRepository
and then passing that to the paginator:
ProductRepository extends EntityRepository
{
// This will return a QueryBuilder instance
public function findAll()
{
return $this->createQueryBuilder("p");
}
}
In the controller:
$products = $productRepository->findAll();
// Creating pagnination
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$products,
$this->get('request')->query->get('page', 1),
20
);
回答2:
I think in some cases we could use Closure
and pass to it a QueryBuilder
object.
In your ProductRepository
you could do something like this:
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();
}
}
and then 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);
// ...
}
I think it more flexible and you could use findAllPublished
method to get both paginated or NOT paginated results if you need.
Also keep in mind that callable
type hint work in PHP >=5.4
! Please, check docs for more info.
回答3:
In our project we want to avoid using Doctrine queries in controllers. We have also separate layers. Controllers must not access the database. So I included pagination in the Repository.
Here my 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,
));
}
And here is the important code in my 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
}
}
来源:https://stackoverflow.com/questions/22804158/how-to-use-knppaginatorbundle-to-paginate-results-using-doctrine-repository