有没有办法定义分页结果的第一项?

时间:2019-02-01 20:57:01

标签: symfony doctrine-orm

我正在建立一个Symfony项目,该项目显示带有管理界面的分页博客文章,以管理所有这些内容。在该管理员上,还可以“突出显示”这些公共博客文章之一,以便突出显示的博客文章仅在第一页的第一位置显示。

我需要在每一页上使用相同的商品计数,这就是我要解决的问题。

我正在使用PagerFanta,所以我创建了一个具有“ paginate”功能的AbstractRepository。

protected function paginate(QueryBuilder $qb, $limit = 20, $offset = 0)
{
    if ($limit == 0) {
        throw new \LogicException('$limit must be greater than 0.');
    }

    //Instantiates the pagination object with the result of the query
    $pager = new Pagerfanta(new DoctrineORMAdapter($qb));

    //Sets max data per page
    $pager->setMaxPerPage($limit);

    //Sets the current page
    $pager->setCurrentPage($offset);

    return $pager;
}

在我的博客文章存储库中,我创建了一个querybuilder来获取所有公共博客文章(不包括突出显示的博客文章),因为我可以通过另一种方式将其显示在第一页的顶部。

public function findAllVisible($id, $limit = 3, $offset = 1, $order = 'DESC')
{
    $qb = $this
        ->createQueryBuilder('a')
        ->select('a')
        ->where('a.website = :website')
        ->setParameter('website', 'blog')
        ->andWhere('a.public = :public')
        ->setParameter('public', true)
        ->andWhere('a.id != :id')
        ->setParameter('id', $id)

        ->orderBy('a.dateInsert', $order)
    ;

    return $this->paginate($qb, $limit, $offset);
}

所以我首先尝试根据当前页面更改限制和偏移量,但是在逻辑上丢失了第一页和第二页之间的一项。

然后我尝试将突出显示的博客文章包含在querybuilder中,但是如果当前页面是第一个结果,我不知道如何将其定义为第一个结果。

关于如何将第一个结果强制为仅在首页上突出显示的博客的任何想法?还是另一种干净而适当的方式来显示预期结果?

2 个答案:

答案 0 :(得分:0)

我自己回答,因为我设法做自己需要做的事情。如果有人正在处理相同的问题,这就是我的做法。

除了教义Paginator工具之外,我不再使用PagerFanta。 与其从查询中排除突出显示的文章,不如将我的初始ORDER BY替换为a.id = :highlightedId DESC, a.dateInsert DESC。 现在它可以按预期工作了。

这是我的新存储库功能:

/**
 * Finds all visible articles
 *
 * @param int $highlightedTipId the highlighted tip id
 * @param int $page current page
 * @param int $limit max items per page
 *
 * @throws InvalidArgumentException
 * @throws NotFoundHttpException
 *
 * @return Paginator
 */
public function findAllVisible($highlightedTipId, $limit = 3, $page)
{
    if (!is_numeric($page)) {
        throw new InvalidArgumentException('$page value is incorrect (' . $page . ').');
    }

    if ($page < 1) {
        throw new NotFoundHttpException('Page not found');
    }

    if (!is_numeric($limit)) {
        throw new InvalidArgumentException('$limit value is incorrect (' . $limit . ').');
    }

    $entityManager = $this->getEntityManager();

    $query = $entityManager->createQuery(
        "SELECT
            a,
            CASE WHEN a.id = :id THEN 1 ELSE 0 END AS HIDDEN sortCondition
        FROM App\Entity\Item a
        WHERE
           a INSTANCE OF App\Entity\TipArticle
        AND
            a.website = :website
        AND
            a.public = :public
        ORDER BY
            sortCondition DESC,
            a.dateInsert DESC
        "
    );

    $query->setParameter(':website', 'blog');
    $query->setParameter(':public', true);
    $query->setParameter(':id', $highlightedTipId);

    $firstResult = ($page - 1) * $limit;
    $query
       ->setFirstResult($firstResult)
       ->setMaxResults($limit);
    $paginator = new Paginator($query);

    if (($paginator->count() <= $firstResult) && $page != 1) {
        throw new NotFoundHttpException('Page not found');
    }

    return $paginator;
}

关于这行CASE WHEN a.id = :id THEN 1 ELSE 0 END AS HIDDEN sortCondition的一句话:这是我发现对Doctrine做ORDER BY a.id = :highlightedId DESC的唯一方法。如您所见,我制作了DQL,但使用QueryBuilder也可以。

希望它会有所帮助! :)

答案 1 :(得分:0)

Nice, well done。如果我可以提供一些建议的话。在存储库中,您不需要获取ObjectManager($this->getEntityManager()),因为该存储库已经用于某种类型的实体,在这种情况下为Item。您应该改用Criteriapractical example docs

无论您拥有哪个控制器,都应该安装ObjectManager,这样便可以做到:

$items = $this-getObjectManager()->getRepository(Item::class)->findAllVisible($params)

要使用分页器,应使用QueryBuilder和表达式like here

作为一个实际示例,我的indexAction:

use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator as OrmPaginator;
use DoctrineORMModule\Paginator\Adapter\DoctrinePaginator as OrmAdapter;
use Zend\Paginator\Paginator;

public function indexAction()
{
    // get current page, defaults to 1
    $page           = $this->params()->fromQuery('page', 1);
    // get current page size, defaults to 10
    $pageSize       = $this->params()->fromQuery('pageSize', 10);
    // get ordering, defaults to 'createdAt'
    $orderBy        = $this->params()->fromQuery('orderBy', 'createdAt');
    // get order direction, defaults to Criteria::DESC
    $orderDirection = ($this->params()->fromQuery('orderDirection') === Criteria::ASC)
        ? Criteria::ASC
        : Criteria::DESC;

    $criteria = new Criteria();
    $criteria->setFirstResult($page * $pageSize);
    $criteria->setMaxResults($pageSize);
    $criteria->orderBy([$orderBy => $orderDirection]);

    /** @var QueryBuilder $queryBuilder */
    $queryBuilder = $this->getObjectManager()->createQueryBuilder();
    $queryBuilder->select('a')->from(Article::class, 'a');
    $queryBuilder->addCriteria($criteria);

    $paginator = new Paginator(new OrmAdapter(new OrmPaginator($queryBuilder)));
    $paginator->setCurrentPageNumber($page);     // set current page
    $paginator->setItemCountPerPage($pageSize);  // set item count per page

    return [
        'paginator' => $paginator,
        'queryParams' => $this->params()->fromQuery(), // pass these for your pagination uri's
    ];
}

注意:上面的$this是Zend Framework控制器的一个实例,其中->fromQuery从URI的查询位返回(如果存在)给定的键(如果存在URI),否则返回第二个参数默认值(或null)。您应该做类似的事情。