ListResource   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 211
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 55
c 2
b 0
f 0
dl 0
loc 211
rs 10
wmc 29

15 Methods

Rating   Name   Duplication   Size   Complexity  
A setURL() 0 4 1
A getURL() 0 3 1
A setClient() 0 4 1
A setParams() 0 4 1
A getClient() 0 3 1
A getParams() 0 3 1
A __construct() 0 7 1
A offsetUnset() 0 2 1
A offsetSet() 0 2 1
A offsetGet() 0 4 3
A first() 0 3 2
A count() 0 6 1
A getIterator() 0 19 3
B getItems() 0 26 8
A offsetExists() 0 4 3
1
<?php
2
3
namespace Lifeboat\Resource;
4
5
use Lifeboat\Connector;
6
use Lifeboat\Exceptions\ApiException;
7
use Lifeboat\Exceptions\OAuthException;
8
use Lifeboat\Models\Model;
9
use Lifeboat\Factory\ObjectFactory;
10
use IteratorAggregate;
11
use Generator;
12
use ArrayAccess;
13
use Countable;
14
15
/**
16
 * Class ResourceList
17
 *
18
 * This class performs some serious magic.
19
 *
20
 * It calls the api in a paginated fashion however, it will keep
21
 * calling the next page automatically until it meets the end of the list,
22
 * or if the search index is found.
23
 *
24
 * Similar to how an infinite scroll would work, if a REST API version of it would exist.
25
 *
26
 * This is done so that we don't overwhelm one API server with humongous requests
27
 * if the developer needs to access a full list of objects.
28
 *
29
 * @package Lifeboat\Resource
30
 * @property Connector $_client
31
 */
32
class ListResource implements IteratorAggregate, ArrayAccess, Countable {
33
34
    const PAGE_PARAM    = 'page';
35
    const LIMIT_PARAM   = 'limit';
36
37
    private $_url = '';
38
    private $_params = [];
39
    protected $_items = [];
40
    protected $_max_items = 0;
41
42
    private $_page_length;
43
    private $_client;
44
45
    /**
46
     * ListResource constructor.
47
     * @param Connector $client
48
     * @param string $url
49
     * @param array $params
50
     * @param int $page_length
51
     */
52
    public function __construct(Connector $client, string $url, array $params = [], int $page_length = 20)
53
    {
54
        $this->setClient($client);
55
        $this->setURL($url);
56
        $this->setParams($params);
57
58
        $this->_page_length = $page_length;
59
    }
60
61
    /**
62
     * @param Connector $client
63
     * @return $this
64
     */
65
    public function setClient(Connector $client): ListResource
66
    {
67
        $this->_client = $client;
68
        return $this;
69
    }
70
71
    /**
72
     * @return Connector
73
     */
74
    public function getClient(): Connector
75
    {
76
        return $this->_client;
77
    }
78
79
    /**
80
     * @param array $params
81
     * @return $this
82
     */
83
    public function setParams(array $params = []): ListResource
84
    {
85
        $this->_params = $params;
86
        return $this;
87
    }
88
89
    /**
90
     * @return array
91
     */
92
    public function getParams(): array
93
    {
94
        return $this->_params;
95
    }
96
97
    /**
98
     * @param string $url
99
     * @return $this
100
     */
101
    public function setURL(string $url): ListResource
102
    {
103
        $this->_url = $url;
104
        return $this;
105
    }
106
107
    /**
108
     * @return string
109
     */
110
    public function getURL(): string
111
    {
112
        return $this->_url;
113
    }
114
115
    /**
116
     * @param int $page
117
     * @return array
118
     * @throws OAuthException
119
     * @throws ApiException
120
     */
121
    public function getItems(int $page = 1): array
122
    {
123
        if (!array_key_exists($page, $this->_items)) {
124
            $data = $this->getParams();
125
126
            $data[self::PAGE_PARAM]     = $page;
127
            $data[self::LIMIT_PARAM]    = $this->_page_length;
128
129
            $response   = $this->getClient()->curl_api($this->getURL(), 'GET', $data);
130
            $data       = ($response->isValid() && $response->isJSON()) ? $response->getJSON() : [];
131
132
            if (empty($data)) throw new ApiException($response->getError());
133
134
            $this->_max_items = (int) $data['available_items'];
135
136
            if (empty($data['items'])) return $this->_items[$page] = [];
137
138
            foreach ($data['items'] as $item) {
139
                $obj = ObjectFactory::make($this->getClient(), $item);
140
                if (!$obj) continue;
141
142
                $this->_items[$page][] = $obj;
143
            }
144
        }
145
146
        return $this->_items[$page];
147
    }
148
149
    /**
150
     * @param mixed $offset
151
     * @return ObjectResource|null
152
     */
153
    public function offsetGet($offset): ?ObjectResource
154
    {
155
        foreach ($this as $i => $obj) if ($i === $offset) return $obj;
156
        return null;
157
    }
158
159
    /**
160
     * @param mixed $offset
161
     * @param mixed $value
162
     */
163
    public function offsetSet($offset, $value)
164
    {
165
        // Do nothing, this is only a reflection object
166
    }
167
168
    /**
169
     * @return int
170
     * @throws OAuthException
171
     */
172
    public function count(): int
173
    {
174
        // Make sure to load the objects first
175
        $this->getItems(1);
176
177
        return $this->_max_items;
178
    }
179
180
    /**
181
     * @param mixed $offset
182
     * @return bool
183
     */
184
    public function offsetExists($offset): bool
185
    {
186
        foreach ($this as $i => $obj) if ($offset === $i) return true;
187
        return false;
188
    }
189
190
    /**
191
     * @param mixed $offset
192
     */
193
    public function offsetUnset($offset)
194
    {
195
        // Do nothing, we cannot modify a reflection object
196
    }
197
198
    /**
199
     * Traverse the objects in chunks as not to overwhelm the API
200
     *
201
     * IMPORTANT:
202
     * If you're reading this and have no clue what on earth is going on...
203
     * DON'T TOUCH IT!
204
     *
205
     * The person who wrote this enjoys PHP in a really sadistic way,
206
     * so much so that he even used 1 letter variable names.
207
     *
208
     * This was done to ensure you don't touch this function.
209
     * Just enjoy using it and trust in the magic.
210
     *
211
     * @return Generator
212
     * @throws OAuthException If client has a problem connecting to the API
213
     */
214
    public function getIterator(): Generator
215
    {
216
        $t = $this->getItems(1);
217
        $c = $this->count();
218
219
        return (function () use ($t, $c) {
220
            $i = 0;
221
            $x = 0;
222
223
            while ($i < $c) {
224
                if ($x === 0) {
225
                    $x = $this->_page_length;
226
                    $t = $this->getItems((int) floor($i / $this->_page_length) + 1);
227
                }
228
229
                yield $i => $t[$this->_page_length - $x];
230
231
                $x -= 1;
232
                $i ++;
233
            }
234
        })();
235
    }
236
237
    /**
238
     * @return Model|null
239
     */
240
    public function first(): ?Model
241
    {
242
        foreach ($this as $obj) return $obj;
243
    }
244
}
245