/** * This method iterates over a collection of resources. It sends the operation's request to the API, * parses the response, converts each element into {@see self} and - if pagination is supported - continues * to send requests until an empty collection is received back. * * For paginated collections, it sends subsequent requests according to a marker URL query. The value * of the marker will depend on the last element returned in the previous response. If a limit is * provided, the loop will continue up until that point. * * @param Operation $operation The operation responsible for retrieving a new collection * @param callable $mapFn An optional callback that will be executed on every resource iteration. */ public function enumerate(Operation $operation, callable $mapFn = null) { $limit = $operation->getValue('limit') ?: false; $supportsPagination = $operation->hasParam('marker'); $markerKey = $this->markerKey ?: self::DEFAULT_MARKER_KEY; $count = 0; $moreRequestsRequired = true; $totalReached = function ($count) use($limit) { return $limit && $count >= $limit; }; while ($moreRequestsRequired && $count < 20) { $response = $operation->send(); $body = $response->json(); $json = $this->flatten($body, $this->resourcesKey); foreach ($json as $resourceData) { if ($totalReached($count)) { break; } $count++; $resource = $this->newInstance(); $resource->populateFromArray($resourceData); if ($mapFn) { call_user_func_array($mapFn, [$resource]); } if ($supportsPagination) { $operation->setValue('marker', $resource->{$markerKey}); } (yield $resource); } if ($totalReached($count) || !$supportsPagination || empty($json)) { $moreRequestsRequired = false; } } }