123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- ==================
- Resource iterators
- ==================
- Web services often implement pagination in their responses which requires the end-user to issue a series of consecutive
- requests in order to fetch all of the data they asked for. Users of your web service client should not be responsible
- for implementing the logic involved in iterating through pages of results. Guzzle provides a simple resource iterator
- foundation to make it easier on web service client developers to offer a useful abstraction layer.
- Getting an iterator from a client
- ---------------------------------
- ResourceIteratorInterface Guzzle\Service\Client::getIterator($command [, array $commandOptions, array $iteratorOptions ])
- The ``getIterator`` method of a ``Guzzle\Service\ClientInterface`` object provides a convenient interface for
- instantiating a resource iterator for a specific command. This method implicitly uses a
- ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object to create resource iterators. Pass an
- instantiated command object or the name of a command in the first argument. When passing the name of a command, the
- command factory of the client will create the command by name using the ``$commandOptions`` array. The third argument
- may be used to pass an array of options to the constructor of the instantiated ``ResourceIteratorInterface`` object.
- .. code-block:: php
- $iterator = $client->getIterator('get_users');
- foreach ($iterator as $user) {
- echo $user['name'] . ' age ' . $user['age'] . PHP_EOL;
- }
- The above code sample might execute a single request or a thousand requests. As a consumer of a web service, I don't
- care. I just want to iterate over all of the users.
- Iterator options
- ~~~~~~~~~~~~~~~~
- The two universal options that iterators should support are ``limit`` and ``page_size``. Using the ``limit`` option
- tells the resource iterator to attempt to limit the total number of iterated resources to a specific amount. Keep in
- mind that this is not always possible due to limitations that may be inherent to a web service. The ``page_size``
- option is used to tell a resource iterator how many resources to request per page of results. Much like the ``limit``
- option, you can not rely on getting back exactly the number of resources your specify in the ``page_size`` option.
- .. note::
- The ``limit`` and ``page_size`` options can also be specified on an iterator using the ``setLimit($limit)`` and
- ``setPageSize($pageSize)`` methods.
- Resolving iterator class names
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The default resource iterator factory of a client object expects that your iterators are stored under the ``Model``
- folder of your client and that an iterator is names after the CamelCase name of a command followed by the word
- "Iterator". For example, if you wanted to create an iterator for the ``get_users`` command, then your iterator class
- would be ``Model\GetUsersIterator`` and would be stored in ``Model/GetUsersIterator.php``.
- Creating an iterator
- --------------------
- While not required, resource iterators in Guzzle typically iterate using a ``Guzzle\Service\Command\CommandInterface``
- object. ``Guzzle\Service\Resource\ResourceIterator``, the default iterator implementation that you should extend,
- accepts a command object and array of iterator options in its constructor. The command object passed to the resource
- iterator is expected to be ready to execute and not previously executed. The resource iterator keeps a reference of
- this command and clones the original command each time a subsequent request needs to be made to fetch more data.
- Implement the sendRequest method
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The most important thing (and usually the only thing) you need to do when creating a resource iterator is to implement
- the ``sendRequest()`` method of the resource iterator. The ``sendRequest()`` method is called when you begin
- iterating or if there are no resources left to iterate and it you expect to retrieve more resources by making a
- subsequent request. The ``$this->command`` property of the resource iterator is updated with a cloned copy of the
- original command object passed into the constructor of the iterator. Use this command object to issue your subsequent
- requests.
- The ``sendRequest()`` method must return an array of the resources you retrieved from making the subsequent call.
- Returning an empty array will stop the iteration. If you suspect that your web service client will occasionally return
- an empty result set but still requires further iteration, then you must implement a sort of loop in your
- ``sendRequest()`` method that will continue to issue subsequent requests until your reach the end of the paginated
- result set or until additional resources are retrieved from the web service.
- Update the nextToken property
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Beyond fetching more results, the ``sendRequest()`` method is responsible for updating the ``$this->nextToken``
- property of the iterator. Setting this property to anything other than null tells the iterator that issuing a
- subsequent request using the nextToken value will probably return more results. You must continually update this
- value in your ``sendRequest()`` method as each response is received from the web service.
- Example iterator
- ----------------
- Let's say you want to implement a resource iterator for the ``get_users`` command of your web service. The
- ``get_users`` command receives a response that contains a list of users, and if there are more pages of results to
- retrieve, returns a value called ``next_user``. This return value is known as the **next token** and should be used to
- issue subsequent requests.
- Assume the response to a ``get_users`` command returns JSON data that looks like this:
- .. code-block:: javascript
- {
- "users": [
- { "name": "Craig Johnson", "age": 10 },
- { "name": "Tom Barker", "age": 20 },
- { "name": "Bob Mitchell", "age": 74 }
- ],
- "next_user": "Michael Dowling"
- }
- Assume that because there is a ``next_user`` value, there will be more users if a subsequent request is issued. If the
- ``next_user`` value is missing or null, then we know there are no more results to fetch. Let's implement a resource
- iterator for this command.
- .. code-block:: php
- namespace MyService\Model;
- use Guzzle\Service\Resource\ResourceIterator;
- /**
- * Iterate over a get_users command
- */
- class GetUsersIterator extends ResourceIterator
- {
- protected function sendRequest()
- {
- // If a next token is set, then add it to the command
- if ($this->nextToken) {
- $this->command->set('next_user', $this->nextToken);
- }
- // Execute the command and parse the result
- $result = $this->command->execute();
- // Parse the next token
- $this->nextToken = isset($result['next_user']) ? $result['next_user'] : false;
- return $result['users'];
- }
- }
- As you can see, it's pretty simple to implement an iterator. There are a few things that you should notice from this
- example:
- 1. You do not need to create a new command in the ``sendRequest()`` method. A new command object is cloned from the
- original command passed into the constructor of the iterator before the ``sendRequest()`` method is called.
- Remember that the resource iterator expects a command that has not been executed.
- 2. When the ``sendRequest()`` method is first called, you will not have a ``$this->nextToken`` value, so always check
- before setting it on a command. Notice that the next token is being updated each time a request is sent.
- 3. After fetching more resources from the service, always return an array of resources.
|