Passed
Push — main ( a3d595...383cbf )
by Dylan
02:02
created

ListResource::getItems()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 24
rs 8.8333
cc 7
nc 6
nop 1
1
<?php
2
3
namespace Lifeboat\Resource;
4
5
use Lifeboat\Connector;
6
use Lifeboat\Exceptions\OAuthException;
7
use Lifeboat\Models\Model;
8
use Lifeboat\Services\ObjectFactory;
9
use IteratorAggregate;
10
use Generator;
11
12
/**
13
 * Class ResourceList
14
 *
15
 * This class performs some serious magic.
16
 *
17
 * It calls the api in a paginated fashion however, it will keep
18
 * calling the next page automatically until it meets the end of the list,
19
 * or if the search index is found.
20
 *
21
 * Similar to how an infinite scroll would work, if a REST API version of it would exist.
22
 *
23
 * This is done so that we don't overwhelm one API server with humongous requests
24
 * if the developer needs to access a full list of objects.
25
 *
26
 * @package Lifeboat\Resource
27
 */
28
class ListResource extends ApiResource implements IteratorAggregate {
29
30
    const PAGE_PARAM    = 'page';
31
    const LIMIT_PARAM   = 'limit';
32
33
    private string $_url = '';
34
    private array $_params = [];
35
    private array $_items = [];
36
    private int $_max_items = 0;
37
38
    private int $_page_length;
39
40
    /**
41
     * ListResource constructor.
42
     * @param Connector $client
43
     * @param string $url
44
     * @param array $params
45
     * @param int $page_length
46
     */
47
    public function __construct(Connector $client, string $url, array $params = [], int $page_length = 20)
48
    {
49
        parent::__construct($client);
50
        $this->setURL($url);
51
        $this->setParams($params);
52
53
        $this->_page_length = $page_length;
54
    }
55
56
    /**
57
     * @param array $params
58
     * @return $this
59
     */
60
    public function setParams(array $params = []): ListResource
61
    {
62
        $this->_params = $params;
63
        return $this;
64
    }
65
66
    /**
67
     * @return array
68
     */
69
    public function getParams(): array
70
    {
71
        return $this->_params;
72
    }
73
74
    /**
75
     * @param string $url
76
     * @return $this
77
     */
78
    public function setURL(string $url): ListResource
79
    {
80
        $this->_url = $url;
81
        return $this;
82
    }
83
84
    /**
85
     * @return string
86
     */
87
    public function getURL(): string
88
    {
89
        return $this->_url;
90
    }
91
92
    /**
93
     * @param int $page
94
     * @return array
95
     * @throws OAuthException
96
     */
97
    public function getItems(int $page = 1): array
98
    {
99
        if (!array_key_exists($page, $this->_items)) {
100
            $data = $this->getParams();
101
102
            $data[self::PAGE_PARAM]     = $page;
103
            $data[self::LIMIT_PARAM]    = $this->_page_length;
104
105
            $response   = $this->getClient()->curl_api($this->getURL(), 'GET', $data);
106
            $data       = ($response->isValid() && $response->isJSON()) ? $response->getJSON() : [];
107
108
            $this->_max_items = (int) $data['available_items'];
109
110
            if (empty($data['items'])) return $this->_items[$page] = [];
111
112
            foreach ($data['items'] as $item) {
113
                $obj = ObjectFactory::make($this->getClient(), $item);
0 ignored issues
show
Bug introduced by
It seems like $this->getClient() can also be of type null; however, parameter $connector of Lifeboat\Services\ObjectFactory::make() does only seem to accept Lifeboat\Connector, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
                $obj = ObjectFactory::make(/** @scrutinizer ignore-type */ $this->getClient(), $item);
Loading history...
114
                if (!$obj) continue;
115
116
                $this->_items[$page][] = $obj;
117
            }
118
        }
119
120
        return $this->_items[$page];
121
    }
122
123
    /**
124
     * @param mixed $offset
125
     * @return ObjectResource|null
126
     */
127
    public function offsetGet($offset): ?ObjectResource
128
    {
129
        foreach ($this as $i => $obj) if ($i === $offset) return $obj;
130
        return null;
131
    }
132
133
    /**
134
     * @param mixed $offset
135
     * @param mixed $value
136
     */
137
    public function offsetSet($offset, $value)
138
    {
139
        // Do nothing, this is only a reflection object
140
    }
141
142
    /**
143
     * @return int
144
     * @throws OAuthException
145
     */
146
    public function count(): int
147
    {
148
        // Make sure to load the objects first
149
        $this->getItems(1);
150
151
        return $this->_max_items;
152
    }
153
154
    /**
155
     * @param mixed $offset
156
     * @return bool
157
     */
158
    public function offsetExists($offset): bool
159
    {
160
        foreach ($this as $i => $obj) if ($offset === $i) return true;
161
        return false;
162
    }
163
164
    /**
165
     * @param mixed $offset
166
     */
167
    public function offsetUnset($offset)
168
    {
169
        // Do nothing, we cannot modify a reflection object
170
    }
171
172
    /**
173
     * Traverse the objects in chunks as not to overwhelm the API
174
     *
175
     * IMPORTANT:
176
     * If you're reading this and have no clue what on earth is going on...
177
     * DON'T TOUCH IT!
178
     *
179
     * The person who wrote this enjoys PHP in a really sadistic way,
180
     * so much so that he even used 1 letter variable names.
181
     *
182
     * This was done to ensure you don't touch this function.
183
     * Just enjoy using it and trust in the magic.
184
     *
185
     * @return Generator
186
     * @throws OAuthException If client has a problem connecting to the API
187
     */
188
    public function getIterator(): Generator
189
    {
190
        $t = $this->getItems(1);
191
        $c = $this->count();
192
193
        return (function () use ($t, $c) {
194
            $i = 0;
195
            $x = 0;
196
197
            while ($i < $c) {
198
                if ($x === 0) {
199
                    $x = $this->_page_length;
200
                    $t = $this->getItems(floor($i / $this->_page_length) + 1);
0 ignored issues
show
Bug introduced by
floor($i / $this->_page_length) + 1 of type double is incompatible with the type integer expected by parameter $page of Lifeboat\Resource\ListResource::getItems(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

200
                    $t = $this->getItems(/** @scrutinizer ignore-type */ floor($i / $this->_page_length) + 1);
Loading history...
201
                }
202
203
                yield $i => $t[$this->_page_length - $x];
204
205
                $x -= 1;
206
                $i ++;
207
            }
208
        })();
209
    }
210
211
    /**
212
     * @return Model|null
213
     */
214
    public function first(): ?Model
215
    {
216
        foreach ($this as $obj) return $obj;
217
    }
218
}
219