Aller à la documentation
PHP

Pagination

Une page, toutes les pages, ou un élément à la fois, sur chaque liste paginée.

list, listAll et iterate

Chaque liste paginée a trois méthodes. list récupère une page et renvoie une OpenEmail\Result\Page. listAll suit le curseur à travers toutes les pages et renvoie un seul tableau. iterate parcourt les mêmes pages un élément à la fois et renvoie un Generator, qui ne récupère la page suivante que lorsque vous y arrivez. Les trois prennent les filtres de la liste, limit:, cursor: et apiKey:.

three_ways.php
$page = $client->emails->list(status: 'failed', limit: 50); foreach ($page as $email) {    echo $email['id'], ' ', $email['lastError'] ?? '', PHP_EOL;} $failures = $client->emails->listAll(status: 'failed'); foreach ($client->emails->iterate(status: 'failed') as $email) {    echo $email['id'], PHP_EOL;} echo count($failures), ' ', $page->hasMore ? 'more to come' : 'that is all', PHP_EOL;

Les trois mêmes noms reviennent partout où un espace de noms a plus d'une liste, nommés d'après la liste qu'ils parcourent : listEvents, listAllEvents et iterateEvents sur emails, listDeliveries, listAllDeliveries et iterateDeliveries sur webhooks, et ainsi de suite.

OpenEmail\Result\Page

itemsarray
Les lignes de cette page, extraites de l'enveloppe `data` de l'API, chacune un tableau associatif. Vide quand la page ne contient rien.
hasMorebool
Indique si une autre page suit. Quand l'API n'envoie pas de `hasMore`, il vaut true exactement quand il y a un `nextCursor`.
nextCursorstring or null
Ce qu'il faut renvoyer comme `cursor:` pour la page suivante, et null sur la dernière.

Une page est immuable : ses propriétés sont readonly. Elle est aussi IteratorAggregate et Countable : foreach ($page as $item) parcourt donc ses lignes et count($page) les compte.

Un Generator

iterate ne demande rien tant que vous ne commencez pas à le parcourir, et il ne demande la page suivante qu'une fois chaque élément de la page courante fourni : tout ce qui s'arrête tôt arrête donc aussi les requêtes. break met fin au parcours, tout comme le retour de la fonction qui contient la boucle.

generator.php
$latest = []; foreach ($client->emails->iterate(status: 'failed', limit: 100) as $email) {    $latest[] = $email;     if (count($latest) === 10) {        break;    }} $invoice = null; foreach ($client->emails->iterate(status: 'failed') as $email) {    if (($email['tags']['invoice'] ?? null) === 'inv_2026_09_4192') {        $invoice = $email;         break;    }} echo count($latest), ' ', $invoice['id'] ?? 'not found', PHP_EOL;

Tout ce qui a besoin de tous les éléments, comme iterator_to_array() appelé sur le Generator, lit toutes les pages avant de retourner, comme le fait listAll.

Un Generator ne peut être parcouru qu'une fois. Le parcourir une seconde fois lève une exception : rappelez donc iterate quand vous avez de nouveau besoin des éléments, ou gardez les éléments plutôt que le Generator.

Reprendre à partir d'un curseur

Un curseur est opaque. Gardez le nextCursor de la dernière page lue et renvoyez-le comme cursor: pour reprendre à partir de là, dans une requête ultérieure ou dans un autre processus. listAll et iterate prennent aussi cursor: et commencent leur parcours après lui.

resume.php
$firstPage = $client->emails->list(status: 'failed', limit: 25);$saved = $firstPage->nextCursor; if ($saved !== null) {    $rest = $client->emails->listAll(status: 'failed', cursor: $saved);    echo count($rest), PHP_EOL;}

Un curseur appartient à la liste et aux filtres dont il provient : envoyez donc les mêmes filtres avec lui. Un curseur que la liste ne peut pas situer est refusé avec invalid_cursor, et la solution est alors de recommencer sans curseur.

limit:

limit: est la taille de chaque page, pas un total. Sur list, c'est le nombre de lignes renvoyées. Sur listAll et iterate, c'est le nombre que demande chaque requête : une valeur plus grande signifie donc moins d'allers-retours pour les mêmes lignes. Chaque liste a sa propre plage et sa propre valeur par défaut, le plus souvent de 1 à 100 avec 25 si vous n'envoyez rien, et une valeur hors de la plage est refusée plutôt que ramenée dans les bornes. La référence de chaque liste indique sa plage.

Quand un parcours s'arrête

  • Quand une page indique que hasMore vaut false.
  • Quand une page ne porte pas de nextCursor, car une page qui annonce une suite sans nommer de curseur bouclerait à l'infini.
  • Quand l'API renvoie un curseur que le parcours a déjà suivi, pour la même raison.

Chaque page est un GET : elle est donc réessayée isolément, comme toute lecture, avant que quoi que ce soit ne lève une exception. Un échec qui survit aux réessais est levé depuis listAll, et les éléments déjà récupérés sont abandonnés. Avec iterate, les éléments des pages précédentes ont déjà été fournis à ce stade : faites donc en sorte que la boucle puisse s'exécuter deux fois sans risque, ou paginez avec list et gardez chaque nextCursor pour qu'une deuxième tentative puisse reprendre là où la première s'est arrêtée.

Fils et brouillons

threads->list et drafts->list, avec leurs listAll et iterate, paginent avec le pageToken et le nextPageToken de l'API plutôt qu'avec un curseur. Le client masque la différence : passez le jeton comme cursor: et lisez-le dans nextCursor.

page_token.php
$page = $client->threads->list(folder: 'inbox', limit: 50);$later = $page->hasMore ? $client->threads->list(folder: 'inbox', limit: 50, cursor: $page->nextCursor) : null; echo count($page), ' ', $later === null ? 0 : count($later), PHP_EOL;

Le serveur propose un jeton chaque fois qu'une page revient pleine : hasMore peut donc valoir true sur ce qui s'avère être la dernière page, et l'appel suivant ne renvoie alors aucun élément.

Les pages qui portent davantage

Quelques listes répondent avec plus que des lignes, et renvoient leur propre objet de OpenEmail\Result à la place de Page. Chacun est immuable, IteratorAggregate sur ses lignes et Countable.

MéthodeRenvoieCe qu'il ajoute
addresses->listAddressBookPageaddresses à la place d'items, plus unrestricted et domains, avec hasMore et nextCursor.
addresses->listAllAddressBookToutes les adresses dans addresses, avec unrestricted et domains tels que la dernière page les a indiqués. C'est le seul listAll qui renvoie le carnet d'adresses complet plutôt qu'un tableau. addresses->iterate ne passe que les adresses.
contacts->listPeoplePeoplePageseen, false quand la clé ne peut pas lire les adresses vues dans le courrier. listAllPeople et iteratePeople ne renvoient que les personnes.
tempMail->listMessagesTempMessagesPageexpiresAt, le moment où la boîte expire. listAllMessages et iterateMessages ne renvoient que les messages.
templates->listSendsTemplateSendsPaginé par numéro plutôt que par curseur : items, total, page et pageSize. Demandez la page suivante avec page:.
emails->sendBatchBatchResultPas une page : items, un par message envoyé, avec les compteurs sent et failed.

Les listes qui renvoient le tableau de l'API

Certaines listes paginent par offset, par numéro de page ou par un curseur numérique qui leur est propre, et renvoient le corps décodé tel quel, un tableau avec data, plutôt qu'une Page. Elles n'ont ni listAll ni iterate : vous les paginez vous-même.

MéthodePagine avecCe qui revient
exports->listlimit: et offset:data, total et hasMore.
imports->listFailuresafter: et limit:data, et nextCursor, un entier à renvoyer comme after:, qui vaut null sur la dernière page.
subscriptions->list et subscriptions->listDomainslimit: et offset:data, total, counts et hasMore.
billing->listInvoicespage: et limit:data, total, page, limit, hasMore et metered.
offset_paging.php
$offset = 0; do {    $batch = $client->subscriptions->list(status: 'active', limit: 50, offset: $offset);     foreach ($batch['data'] as $row) {        echo $row['senderEmail'], ' ', $row['total'], PHP_EOL;    }     $offset += count($batch['data']);} while ($batch['hasMore'] && $batch['data'] !== []);

Une liste qui n'est pas paginée du tout, comme languages->list, labels->listColors ou roles->listPermissions, renvoie directement ses lignes sous forme de simple liste.