Есть ли способ определить первый элемент результатов разбивки на страницы? - PullRequest
0 голосов
/ 01 февраля 2019

Я настраиваю проект Symfony, показывающий постраничные посты блога с интерфейсом администратора для управления всем этим.В этом администраторе также можно «выделить» один из этих общедоступных блогов, чтобы этот выделенный элемент отображался в первой позиции только на первой странице.

Мне нужно одинаковое количество элементов на каждой странице, и этопроблема, с которой я имею дело.

Я использую PagerFanta , поэтому я создал AbstractRepository с функцией "paginate".

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

В моем репозитории блоговЯ сделал построитель запросов, чтобы получить все общедоступные посты за исключением выделенного, потому что я могу получить его другим способом, чтобы отобразить его в верхней части первой страницы.

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

Итак, сначала я попытался изменить ограничение исмещение в соответствии с текущей страницей, но я логически потерял один элемент между первой и второй страницей.

Затем я попытался включить подсвеченный блог в построителе запросов, но не знаю, как определить его как первый результатесли текущая страница является первой.

Любая идея о том, как заставить первый результат быть выделенным постом только на елина первой странице?Или другой чистый и подходящий способ отображения результатов, как и ожидалось?

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

Хорошо, молодец .Если бы я мог дать совет, хотя.В репо вам не нужно получать ObjectManager ($this->getEntityManager()), так как репо уже для типа Entity , Item в этом случае.Вы должны использовать Критерии . практические примеры документов

Вы должны иметь ObjectManager в любом контроллере, который у вас есть, поэтому вы должны сделать:

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

Для использования Paginator выследует использовать QueryBuilder , с выражениями, как здесь

В качестве практического примера, 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, в противном случае возвращает 2-й параметр по умолчанию (или null).Вы должны сделать что-то подобное.

0 голосов
/ 02 февраля 2019

Я отвечаю про себя, потому что мне удалось сделать то, что мне было нужно.В случае, если кто-то имеет дело с той же проблемой, вот как я это сделал.

Я больше не использую PagerFanta, но инструмент Doctrine Paginator .Вместо того, чтобы исключить мою выделенную статью из моего запроса, я заменил исходную 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: это единственный способ, который я нашел, чтобы сделать ORDER BY a.id = :highlightedId DESCс учением.Как вы можете видеть, я сделал DQL, но я также возможен с QueryBuilder.

Надеюсь, это поможет!:)

...