summaryrefslogtreecommitdiff
path: root/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst
blob: ce0bee59fe2aba75a1be5f6e71cd7620f17fde06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
==================
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.