【问题标题】:How to use KNPPaginatorBundle to paginate results using Doctrine Repository?如何使用 KNPPaginatorBundle 使用 Doctrine Repository 对结果进行分页?
【发布时间】:2014-05-13 07:20:14
【问题描述】:

我正在做一个 Symfony2 项目,我决定使用 KNPPaginatorBundle 来构建一个简单的分页系统。所以我创建了一个 Product 实体,我想将分页器添加到 indexAction 操作(由 CRUD 命令生成)。

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

它工作正常,但我想使用产品的存储库而不是直接在控制器中创建查询。我怎样才能做到这一点 ? 实际上,直接将结果集合添加到分页对象中太慢了,因为它会加载所有产品然后对 ArrayCollection 进行分页。

提前致谢。

K4

【问题讨论】:

  • 我也注意到了这个性能问题。问题是这个包需要整个数组集合来生成分页。如果您提供部分结果,则分页将有错误的页数,因为它是从整个集合中计算出来的

标签: php symfony pagination knppaginator knppaginatorbundle


【解决方案1】:

我建议在您的ProductRepository 中使用QueryBuilder,然后将其传递给分页器:

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

在控制器中:

$products = $productRepository->findAll();

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

【讨论】:

  • 谢谢!我做到了,而且效果很好!但是, createQueryBuilder() 和 createQuery() 之间的根本区别是什么?!我可以使用 createNativeQuery() 吗?
  • 创建查询是当您想自己编写 DQL 时,通常您会在查询足够简单时执行此操作。当您需要使用条件构造复杂查询时,请使用 queryBuilder。流体界面对于该任务非常方便。这就是它与分页器一起工作的原因,因为使用 QueryBuilder 非常容易!
  • 但是如果你使用 find all 它将首先在一个查询中获取所有内容。是真的吗?
  • 如果您调用findAll(),它将获取每条记录。这违背了分页的目的之一;以减少您要获取的记录数。
【解决方案2】:

我认为在某些情况下我们可以使用 Closure 并将 QueryBuilder 对象传递给它。

在您的ProductRepository 中,您可以执行以下操作:

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

然后在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);

    // ...
}

我认为它更灵活,如果需要,您可以使用findAllPublished 方法来获得分页或不分页的结果。

还要记住 callable 类型提示在 PHP >=5.4 中有效!请查看docs 了解更多信息。

【讨论】:

  • 智能解决方案!
  • @jmunozco 谢谢!但可能不是最好的;)实际上,这个解决方案让您可以完全控制控制器中的查询构建器,我认为这并不好,即您应该聪明地使用它,但可能会做一些更受限制的事情。
【解决方案3】:

在我们的项目中,我们希望避免在控制器中使用 Doctrine 查询。我们也有单独的层。控制器不得访问数据库。所以我在存储库中包含了分页。

这是我在控制器中的代码:

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

这是我存储库中的重要代码:

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
    }
}

【讨论】:

  • $pages = floor($count/$resultsPerPage);用过吗?
  • 有没有人认为像上面那样实例化 $paginator 是一个大问题,性能或其他方面 - 反对将其作为服务拉取,为此您需要以某种方式将 knp_paginator 服务注入存储库类?
猜你喜欢
  • 1970-01-01
  • 2015-04-27
  • 2019-01-26
  • 2017-07-02
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 2012-08-16
相关资源
最近更新 更多