Statement::getIterator()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace ArangoClient\Statement;
4
5
use ArangoClient\ArangoClient;
6
use ArangoClient\Exceptions\ArangoException;
7
use ArangoClient\Manager;
8
use ArrayIterator;
9
use IteratorAggregate;
10
use stdClass;
11
12
/**
13
 * Executes queries on ArangoDB
14
 *
15
 * @see https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
16
 *
17
 * @template-implements \IteratorAggregate<mixed>
18
 */
19
class Statement extends Manager implements IteratorAggregate
20
{
21
    /**
22
     * @var array<mixed>
23
     */
24
    protected array $results = [];
25
26
    protected ?stdClass $stats = null;
27
28
    /**
29
     * @var array<mixed>
30
     */
31
    protected array $warnings = [];
32
33
    protected ?int $cursorId = null;
34
35
    protected bool $cursorHasMoreResults = false;
36
37
    protected ?int $count = null;
38
39
    protected ?stdClass $extra = null;
40
41
    /**
42
     * Statement constructor.
43
     *
44
     * @param  array<mixed>|null  $bindVars
45
     * @param  array<mixed>  $options
46
     */
47 16
    public function __construct(protected ArangoClient $arangoClient, protected string $query, protected ?array $bindVars, protected array $options = []) {}
48
49
    /**
50
     * A statement can be used like an array to access the results.
51
     *
52
     * @return ArrayIterator<array-key, mixed>
53
     */
54 1
    public function getIterator(): ArrayIterator
55
    {
56 1
        return new ArrayIterator($this->results);
57
    }
58
59
    /**
60
     * @throws ArangoException
61
     */
62 10
    public function execute(): bool
63
    {
64 10
        $this->results = [];
65
66 10
        $bodyContent = $this->prepareQueryBodyContent();
67
68 10
        $options = [
69 10
            'body' => $bodyContent,
70 10
        ];
71 10
        $results = $this->arangoClient->transactionAwareRequest('post', '/_api/cursor', $options);
72
73 10
        $this->handleQueryResults($results);
74
75 10
        $this->requestOutstandingResults($bodyContent);
76
77 10
        return true;
78
    }
79
80
    /**
81
     * @return array<mixed>
82
     */
83 14
    protected function prepareQueryBodyContent(): array
84
    {
85 14
        $bodyContent = $this->options;
86 14
        $bodyContent['query'] = $this->query;
87 14
        if (!empty($this->bindVars)) {
88
            $bodyContent['bindVars'] = $this->bindVars;
89
        }
90
91 14
        return $bodyContent;
92
    }
93
94 12
    protected function handleQueryResults(stdClass $results): void
95
    {
96 12
        $this->results = array_merge($this->results, (array) $results->result);
97
98 12
        if (property_exists($results, 'extra') && $results->extra !== null) {
99 12
            $this->extra = (object) $results->extra;
100
        }
101
102 12
        if (property_exists($results, 'count') && $results->count !== null) {
103 1
            $this->count = (int) $results->count;
104
        }
105
106 12
        $this->cursorHasMoreResults = (bool) $results->hasMore;
107 12
        $this->cursorId = $results->hasMore ? (int) $results->id : null;
108
    }
109
110
    /**
111
     * @param  array<mixed>  $body
112
     *
113
     * @throws ArangoException
114
     */
115 12
    protected function requestOutstandingResults(array $body): void
116
    {
117 12
        while ($this->cursorHasMoreResults) {
118 1
            $uri = '/_api/cursor/' . (string) $this->cursorId;
119
120 1
            $options = [
121 1
                'body' => $body,
122 1
            ];
123
124 1
            $results = $this->arangoClient->request('put', $uri, $options);
125
126 1
            $this->handleQueryResults($results);
127
        }
128
    }
129
130
    /**
131
     * @throws ArangoException
132
     */
133 1
    public function explain(): stdClass
134
    {
135 1
        $body = $this->prepareQueryBodyContent();
136 1
        $options = [
137 1
            'body' => $body,
138 1
        ];
139
140 1
        return $this->arangoClient->request('post', '/_api/explain', $options);
141
    }
142
143
    /**
144
     * Parse and validate the query, will through an ArangoException if the query is invalid.
145
     *
146
     * @throws ArangoException
147
     */
148 1
    public function parse(): stdClass
149
    {
150 1
        $body = $this->prepareQueryBodyContent();
151 1
        $options = [
152 1
            'body' => $body,
153 1
        ];
154
155 1
        return $this->arangoClient->request('post', '/_api/query', $options);
156
    }
157
158
    /**
159
     * Execute the query and return performance information on the query.
160
     *
161
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
162
     *
163
     * @throws ArangoException
164
     */
165 2
    public function profile(int|bool $mode = 1): ?stdClass
166
    {
167 2
        $bodyContent = $this->prepareQueryBodyContent();
168
169 2
        if (!isset($bodyContent['options']) || !is_array($bodyContent['options'])) {
170 2
            $bodyContent['options'] = [];
171
        }
172 2
        $bodyContent['options']['profile'] = $mode;
173
174 2
        $options = [
175 2
            'body' => $bodyContent,
176 2
        ];
177
178 2
        $results = $this->arangoClient->request('post', '/_api/cursor', $options);
179
180 2
        $this->handleQueryResults($results);
181
182 2
        $this->requestOutstandingResults($bodyContent);
183
184 2
        return $this->extra;
185
    }
186
187 2
    public function setQuery(string $query): self
188
    {
189 2
        $this->query = $query;
190
191 2
        return $this;
192
    }
193
194 1
    public function getQuery(): string
195
    {
196 1
        return $this->query;
197
    }
198
199
    /**
200
     * Fetch all results.
201
     *
202
     * @return array<mixed>
203
     */
204 6
    public function fetchAll(): array
205
    {
206 6
        return $this->results;
207
    }
208
209
    /**
210
     * Return the total number of results. (not just the retrieved results)
211
     * Useful if not all results have been retrieved from the database yet.
212
     */
213 2
    public function getCount(): ?int
214
    {
215 2
        return $this->count;
216
    }
217
218 1
    public function getWritesExecuted(): int
219
    {
220 1
        return (int) $this->extra?->stats?->writesExecuted;
221
    }
222
}
223