Paginação
Uma página, todas as páginas ou um item de cada vez, em todas as listas paginadas.
list, listAll e iterate
Todas as listas paginadas têm três métodos. list obtém uma página e devolve uma OpenEmail\Result\Page. listAll segue o cursor por todas as páginas e devolve um único array. iterate percorre as mesmas páginas um item de cada vez e devolve um Generator, que só obtém a página seguinte quando lá chega. Os três aceitam os filtros da lista, limit:, cursor: e apiKey:.
$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;Os mesmos três nomes repetem-se sempre que um espaço de nomes tem mais do que uma lista, com o nome da lista que percorrem: listEvents, listAllEvents e iterateEvents em emails, listDeliveries, listAllDeliveries e iterateDeliveries em webhooks, e assim por diante.
OpenEmail\Result\Page
itemsarray- As linhas desta página, retiradas do envelope `data` da API, cada uma um array associativo. Vazio quando a página não contém nada.
hasMorebool- Se há outra página a seguir. Quando a API não envia `hasMore`, é true exatamente quando existe um `nextCursor`.
nextCursorstring or null- O que deve devolver como `cursor:` para obter a página seguinte, e null na última.
Uma página é imutável: as suas propriedades são readonly. É também IteratorAggregate e Countable, por isso foreach ($page as $item) percorre as suas linhas e count($page) conta-as.
Um Generator
iterate não pede nada até começar a percorrê-lo, e só pede a página seguinte depois de entregues todos os itens da atual, por isso tudo o que para mais cedo também para os pedidos: break termina o percurso, tal como sair da função que contém o ciclo.
$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;Tudo o que precise de todos os itens, como iterator_to_array() chamado sobre o Generator, lê todas as páginas antes de devolver, tal como listAll.
Um Generator só pode ser percorrido uma vez. Percorrê-lo uma segunda vez lança uma exceção, por isso chame iterate de novo quando precisar outra vez dos itens, ou guarde os itens em vez do Generator.
Retomar a partir de um cursor
Um cursor é opaco. Guarde o nextCursor da última página que leu e devolva-o como cursor: para continuar a partir daí, num pedido posterior ou noutro processo. listAll e iterate também aceitam cursor: e começam o seu percurso a seguir a ele.
$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;}Um cursor pertence à lista e aos filtros de onde veio, por isso envie os mesmos filtros com ele. Um que a lista não consiga situar é recusado com invalid_cursor, e a solução é então recomeçar sem cursor.
limit:
limit: é o tamanho de cada página, não um total. Em list é quantas linhas voltam. Em listAll e iterate é quantas cada pedido pede, por isso um valor maior significa menos idas e voltas para as mesmas linhas. Cada lista tem o seu próprio intervalo e valor predefinido, quase sempre de 1 a 100 com 25 se não enviar nenhum, e um valor fora do intervalo é recusado em vez de ajustado. A referência de cada lista indica o seu intervalo.
Quando um percurso para
- Quando uma página diz que
hasMoreé false. - Quando uma página não traz
nextCursor, já que uma página que diz haver mais sem indicar nenhum cursor ficaria em ciclo para sempre. - Quando a API devolve um cursor que o percurso já seguiu, pelo mesmo motivo.
Cada página é um GET, por isso é repetida por si só, como qualquer leitura, antes de se lançar o que quer que seja. Uma falha que sobreviva às repetições é lançada a partir de listAll, e os itens já obtidos são descartados. Em iterate, os itens das páginas anteriores já foram entregues nessa altura, por isso torne seguro executar duas vezes o que o ciclo faz, ou pagine com list e guarde cada nextCursor para que uma segunda tentativa possa começar onde a primeira parou.
Conversas e rascunhos
threads->list e drafts->list, com os seus listAll e iterate, paginam com o pageToken e o nextPageToken da API em vez de um cursor. O cliente esconde a diferença: passe o token como cursor: e leia-o de nextCursor.
$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;O servidor oferece um token sempre que uma página volta cheia, por isso hasMore pode ser true naquela que acaba por ser a última página, e a chamada seguinte não devolve então nenhum item.
Páginas que trazem mais
Algumas listas respondem com mais do que linhas, e devolvem um objeto próprio de OpenEmail\Result em vez de Page. Cada um é imutável, IteratorAggregate sobre as suas linhas e Countable.
| Método | Devolve | O que acrescenta |
|---|---|---|
| addresses->list | AddressBookPage | addresses em vez de items, mais unrestricted e domains, com hasMore e nextCursor. |
| addresses->listAll | AddressBook | Todos os endereços em addresses, com unrestricted e domains tal como a última página os indicou. É o único listAll que devolve o livro de endereços inteiro em vez de um array. addresses->iterate entrega apenas os endereços. |
| contacts->listPeople | PeoplePage | seen, false quando a chave não consegue ler os endereços vistos no correio. listAllPeople e iteratePeople devolvem apenas as pessoas. |
| tempMail->listMessages | TempMessagesPage | expiresAt, o momento em que a caixa expira. listAllMessages e iterateMessages devolvem apenas as mensagens. |
| templates->listSends | TemplateSends | Paginado por número em vez de por cursor: items, total, page e pageSize. Peça a página seguinte com page:. |
| emails->sendBatch | BatchResult | Não é uma página: items, um para cada mensagem que enviou, com as contagens sent e failed. |
Listas que devolvem o array da API
Algumas listas paginam por deslocamento, por número de página ou por um cursor numérico próprio, e devolvem o corpo descodificado tal como chegou, um array com data, em vez de uma Page. Não têm listAll nem iterate, por isso tem de as paginar você mesmo.
| Método | Pagina com | O que volta |
|---|---|---|
| exports->list | limit: e offset: | data, total e hasMore. |
| imports->listFailures | after: e limit: | data, e nextCursor, um inteiro a devolver como after: que é null na última página. |
| subscriptions->list e subscriptions->listDomains | limit: e offset: | data, total, counts e hasMore. |
| billing->listInvoices | page: e limit: | data, total, page, limit, hasMore e metered. |
$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'] !== []);Uma lista que não é paginada, como languages->list, labels->listColors ou roles->listPermissions, devolve logo as suas linhas como uma lista simples.