Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(questions): Pagination #63

Merged
merged 1 commit into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 24 additions & 28 deletions src/Repository/QuestionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
*/
class QuestionRepository extends ServiceEntityRepository
{
public static int $pageSize = 10;
public static int $pageSize = 12;

public function __construct(ManagerRegistry $registry)
{
public function __construct(
ManagerRegistry $registry,
private readonly SearchService $searchService,
) {
parent::__construct($registry, Question::class);
}

Expand Down Expand Up @@ -75,17 +77,17 @@ public function getPreviousPage(int $page): ?int
*
* @return Question[] The list of questions for the given page
*/
public function search(SearchService $searchService, ?string $query, ?string $type, int $page, ?int $pageSize): array
public function search(?string $query, ?string $type, int $page, ?int $pageSize = null): array
{
$filters = [];
if (null !== $type && '' !== $type) {
$escapedType = addslashes($type);
$filters[] = "type = \"$escapedType\"";
}

return $searchService->search($this->getEntityManager(), Question::class, $query ?? '', [
return $this->searchService->search($this->getEntityManager(), Question::class, $query ?? '', [
'limit' => $pageSize ?? self::$pageSize,
'page' => $page,
'offset' => ($page - 1) * ($pageSize ?? self::$pageSize),
'filter' => $filters,
'sort' => ['id:asc'],
]);
Expand All @@ -96,40 +98,34 @@ public function search(SearchService $searchService, ?string $query, ?string $ty
*/
public function reindex(SearchService $searchService): void
{
$searchService->clear(Question::class);
$searchService->index($this->getEntityManager(), $this->findAll());
}

/**
* Calculate the total number of pages for a given query and page size.
* Count the total search results based on a query and type.
*
* @param string|null $query The search query
* @param string|null $type The question type
* @param int|null $pageSize The number of items per page
* @param string|null $query The search query
* @param string|null $type The question type
*
* @return int The total number of pages
* @return int The total result count
*/
public function calculateTotalPages(?string $query, ?string $type, ?int $pageSize): int
public function countSearchResults(?string $query, ?string $type): int
{
// FIXME: This is a naive implementation, it should be optimized
$pageSize ??= self::$pageSize;

$qb = $this->createQueryBuilder('q');
$qb = $qb->select('COUNT(q.id)');

if (null !== $query) {
$qb = $qb->andWhere('q.title LIKE :query')
->setParameter('query', "%$query%");
$filters = [];
if (null !== $type && '' !== $type) {
$escapedType = addslashes($type);
$filters[] = "type = \"$escapedType\"";
}

if (null !== $type) {
$qb = $qb->andWhere('q.type = :type')
->setParameter('type', $type);
}
$result = $this->searchService->rawSearch(Question::class, $query ?? '', [
'filter' => $filters,
'attributesToRetrieve' => ['estimatedTotalHits'],
]);

$questionsCount = $qb->getQuery()->getSingleScalarResult();
\assert(\is_int($questionsCount), 'The questions count should be an integer.');
\assert(isset($result['estimatedTotalHits']) && \is_int($result['estimatedTotalHits']), 'estimatedTotalHits should be set and must be a integer');

return (int) ceil($questionsCount / $pageSize);
return $result['estimatedTotalHits'];
}

/**
Expand Down
32 changes: 10 additions & 22 deletions src/Twig/Components/Questions/FilterableSection.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use App\Entity\Question;
use App\Entity\User;
use App\Repository\QuestionRepository;
use Meilisearch\Bundle\SearchService;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
Expand All @@ -20,8 +19,6 @@ final class FilterableSection
{
use DefaultActionTrait;

public readonly int $pageSize;

public string $title = '題庫一覽';

#[LiveProp]
Expand All @@ -38,9 +35,7 @@ final class FilterableSection

public function __construct(
private readonly QuestionRepository $questionRepository,
private readonly SearchService $searchService,
) {
$this->pageSize = QuestionRepository::$pageSize;
}

/**
Expand All @@ -51,11 +46,9 @@ public function __construct(
public function getQuestions(): array
{
return $this->questionRepository->search(
$this->searchService,
query: $this->query,
type: $this->type,
page: $this->getCurrentPage(),
pageSize: $this->pageSize,
);
}

Expand All @@ -69,26 +62,14 @@ public function getTypesList(): array
return $this->questionRepository->listTypes();
}

/**
* Get the total number of pages.
*/
public function getTotalPages(): int
{
return $this->questionRepository->calculateTotalPages(
query: $this->query,
type: $this->type,
pageSize: $this->pageSize,
);
}

/**
* Get the current page number, clamped to the valid range.
*
* @return int The current page number
*/
public function getCurrentPage(): int
{
return max(min($this->currentPage, $this->getTotalPages()), 1);
return $this->currentPage;
}

/**
Expand All @@ -98,15 +79,22 @@ public function getCurrentPage(): int
*/
public function setCurrentPage(int $page): void
{
$this->currentPage = max(min($page, $this->getTotalPages()), 1);
$this->currentPage = max(1, $page);
}

/**
* Whether there is a next page?
*/
public function hasNext(): bool
{
return $this->getCurrentPage() < $this->getTotalPages();
$total = $this->questionRepository->countSearchResults(
query: $this->query,
type: $this->type,
);

$totalPage = ceil($total / QuestionRepository::$pageSize);

return $this->getCurrentPage() < $totalPage;
}

/**
Expand Down
Loading