feat(mail) : MailMessagesListController - GET /api/mail/folders/{path}/messages (pagination cursor)
- MailMessageRepository::findByFolderCursor : pagination cursor sentAt DESC, id DESC - cursor base64url(sentAt_iso:id), limit max 100 - folderPath URL-encode (requirements: .+ pour supporter les slashes nested) - securite via MailAccessChecker Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@ namespace App\Repository;
|
||||
|
||||
use App\Entity\MailFolder;
|
||||
use App\Entity\MailMessage;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
@@ -99,4 +101,52 @@ class MailMessageRepository extends ServiceEntityRepository
|
||||
|
||||
return array_column($rows, 'uid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination cursor : retourne $limit messages apres le cursor (sentAt DESC, id DESC).
|
||||
* Cursor format : base64url(sentAt_iso8601:id) - null pour la premiere page.
|
||||
*
|
||||
* @return array{messages: list<MailMessage>, nextCursor: ?string}
|
||||
*/
|
||||
public function findByFolderCursor(MailFolder $folder, int $limit, ?string $cursor): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('m')
|
||||
->andWhere('m.folder = :folder')
|
||||
->setParameter('folder', $folder)
|
||||
->orderBy('m.sentAt', 'DESC')
|
||||
->addOrderBy('m.id', 'DESC')
|
||||
->setMaxResults($limit + 1)
|
||||
;
|
||||
|
||||
if (null !== $cursor) {
|
||||
$decoded = base64_decode(strtr($cursor, '-_', '+/'), true);
|
||||
if (false !== $decoded && str_contains($decoded, ':')) {
|
||||
[$sentAtStr, $idStr] = explode(':', $decoded, 2);
|
||||
$cursorSentAt = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $sentAtStr);
|
||||
$cursorId = (int) $idStr;
|
||||
|
||||
if ($cursorSentAt instanceof DateTimeImmutable) {
|
||||
$qb
|
||||
->andWhere('m.sentAt < :cursorSentAt OR (m.sentAt = :cursorSentAt AND m.id < :cursorId)')
|
||||
->setParameter('cursorSentAt', $cursorSentAt)
|
||||
->setParameter('cursorId', $cursorId)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var list<MailMessage> $results */
|
||||
$results = $qb->getQuery()->getResult();
|
||||
$hasMore = count($results) > $limit;
|
||||
$messages = $hasMore ? array_slice($results, 0, $limit) : $results;
|
||||
$nextCursor = null;
|
||||
|
||||
if ($hasMore && [] !== $messages) {
|
||||
$last = end($messages);
|
||||
$raw = $last->getSentAt()->format(DateTimeInterface::ATOM).':'.$last->getId();
|
||||
$nextCursor = rtrim(strtr(base64_encode($raw), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
return ['messages' => $messages, 'nextCursor' => $nextCursor];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user