Blame view

sources/3rdparty/rackspace/php-opencloud/docs/userguide/Iterators.md 6.04 KB
6d9380f96   Cédric Dupont   Update sources OC...
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
  # Iterators
  
  ## Intro
  
  Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: 
  
  - **ResourceIterator**. The standard iterator class that implements SPL's standard [Iterator](http://php.net/manual/en/class.iterator.php), [ArrayAccess](http://www.php.net/manual/en/class.arrayaccess.php) and [Countable](http://php.net/manual/en/class.countable.php) interfaces. In short, this allows you to traverse this object (using `foreach`), count its internal elements like an array (using `count` or `sizeof`), and access its internal elements like an array (using `$iterator[1]`).
  
  
  - **PaginatedIterator**. This is a child of ResourceIterator, and as such inherits all of its functionality. The difference however is that when it reaches the end of the current collection, it attempts to construct a URL to access the API based on predictive paginated collection templates.
  
  ## Common behaviour
  
  ```php
  $iterator = $computeService->flavorList();
  ```
  
  There are two ways to traverse an iterator. The first is the longer, more traditional way:
  
  ```php
  while ($iterator->valid()) {
  	$flavor = $iterator->current();
      
      // do stuff..
      echo $flavor->id;
      
      $iterator->next();
  }
  ```
  
  There is also a shorter and more intuitive version:
  
  ```php
  foreach ($iterator as $flavor) {
  	// do stuff...
      echo $flavor->id;
  }
  ```
  
  Because the iterator implements PHP's native `Iterator` interface, it can inherit all the native functionality of traversible data structures with `foreach`.
  
  ## Very important note
  
  Until now, users have been expected to do this:
  
  ```php
  while ($flavor = $iterator->next()) {
     // ...
  }
  ```
  
  which is **incorrect**. The single responsibility of `next` is to move the internal pointer forward. It is the job of `current` to retrieve the current element.
  
  For your convenience, these two Iterator classes are fully backward compatible: they exhibit all the functionality you'd expect from a correctly implemented iterator, but they also allow previous behaviour.
  
  ## Using paginated collections
  
  For large collections, such as retrieving DataObjects from CloudFiles/Swift, you need to use pagination. Each resource will have a different limit per page; so once that page is traversed, there needs to be another API call to retrieve to *next* page's resources.
  
  There are two key concepts:
  
  - **limit** is the amount of resources returned per page
  - **marker** is the way you define a starting point. It is some form of identifier that allows the collection to begin from a specific resource
  
  ### Resource classes
  
  When the iterator returns a current element in the internal list, it populates the relevant resource class with all the data returned to the API. In most cases, a `stdClass` object will become an instance of `OpenCloud\Common\PersistentObject`.
  
  In order for this instantiation to happen, the `resourceClass` option must correspond to some method in the parent class that creates the resource. For example, if we specify 'ScalingPolicy' as the `resourceClass`, the parent object (in this case `OpenCloud\Autoscale\Group`, needs to have some method will allows the iterator to instantiate the child resource class. These are all valid:
  
  1. `Group::scalingGroup($data);`
  
  2. `Group::getScalingGroup($data);`
  
  3. `Group::resource('ScalingGroup', $data);`
  
  where `$data` is the standard object. This list runs in order of precedence.
  
  ## Setting up a PaginatedIterator
  
  ```php
  use OpenCloud\Common\Collection\PaginatedIterator;
  
  $service = $client->computeService();
  
  $flavors = PaginatedIterator::factory($service, array(
  	'resourceClass'  => 'Flavor',
      'baseUrl'        => $service->getUrl('flavors')
      'limit.total'    => 350,
      'limit.page'     => 100,
      'key.collection' => 'flavors'
  ));
  
  foreach ($flavors as $flavor) {
  	echo $flavor->getId();
  }
  ```
  
  As you can see, there are a lot of configuration parameters to pass in - and getting it right can be quite fiddly, involving a lot of API research. For this reason, using the convenience methods like `flavorList` is recommended because it hides the complexity.
  
  ### PaginatedIterator options
  
  There are certain configuration options that the paginated iterator needs to work. These are:
  
  Name|Description|Type|Required|Default|
  ---|---|---|---|---|
  resourceClass|The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator.|string|Yes|-
  baseUrl|The base URL that is used for making new calls to the API for new pages|`Guzzle\Http\Url`|Yes|-
  limit.total|The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list|int|No|10000
  limit.page|The amount of resources each page contains|int|No|100
  key.links|Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is `images_links`.|string|No|links
  key.collection|The top-level key for the array of resources. For example, servers are returned with this data structure: `{"servers": [...]}`. The **key.collection** value in this case would be `servers`.|string|No|`null`
  key.collectionElement|Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: `{"keypairs": [ {"keypair": {...}} ] }`. So in this case the collectionElement key would be `keypair`.|string|No|`null`
  key.marker|The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is `id` or `name`.|string|No|name
  request.method|The HTTP method used when making API calls for new pages|string|No|GET
  request.headers|The HTTP headers to send when making API calls for new pages|array|No|`array()`
  request.body|The HTTP entity body to send when making API calls for new pages|`Guzzle\Http\EntityBody`|No|`null`
  request.curlOptions|Additional cURL options to use when making API calls for new pages|array|No|`array()`