Есть ли способ определить первый элемент результатов разбивки на страницы?

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

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

Я использую ПейджерФанта, поэтому я создал 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);
}

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
0
870
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

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

Молодец, что разобрался. Не могли бы вы также опубликовать код, который является вашим ответом? Просто смена пагинатора на самом деле не распространяется на сайт вопросов и ответов по коду ;-) Спасибо.

rkeet 03.02.2019 19:47

Привет, разместил несколько комментариев, но удалил их в пользу ответа. Не стесняйтесь использовать его, если хотите. Я подумал, что ваш ответ довольно беспорядочный, поскольку вы смешиваете «нормальные» места. Например, вам не нужно использовать EntityManager в репозитории, и вам действительно не следует создавать запросы вручную (об этом позаботятся и QueryBuilder, и Criteria). Так что подумал, что я предоставлю вам свой ответ. Впрочем, держитесь ;-)

rkeet 04.02.2019 20:59

Красиво, молодец. Если я могу предложить несколько советов, хотя. В репо вам не нужно получать ObjectManager ($this->getEntityManager()), так как репо уже для типа сущности, в данном случае 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, иначе возвращает второй параметр по умолчанию (или null). Вы должны сделать что-то подобное.

Другие вопросы по теме