Passed
Push — dependabot/github_actions/depe... ( d1016c )
by
unknown
11:16
created

RunsQueries::explain()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 10
ccs 7
cts 7
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Concerns;
6
7
use Closure;
8
use Exception;
9
use Iterator;
10
use LaravelFreelancerNL\Aranguent\Query\Builder as QueryBuilder;
11
use LaravelFreelancerNL\Aranguent\QueryException;
12
use LaravelFreelancerNL\FluentAQL\QueryBuilder as ArangoQueryBuilder;
13
use stdClass;
14
15
trait RunsQueries
16
{
17
    /**
18
     * Run a select statement against the database and returns a generator.
19
     * ($useReadPdo is a dummy to adhere to the interface).
20
     *
21
     * @param  string  $query
22
     * @param  array  $bindings
23
     * @param  bool|null  $useReadPdo
24
     * @return Iterator|null
25
     */
26
    public function cursor($query, $bindings = [], $useReadPdo = null): ?Iterator
0 ignored issues
show
Unused Code introduced by
The parameter $useReadPdo is not used and could be removed. ( Ignorable by Annotation )

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

26
    public function cursor($query, $bindings = [], /** @scrutinizer ignore-unused */ $useReadPdo = null): ?Iterator

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
27
    {
28
        // Usage of a separate DB to read date isn't supported at this time
29
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
30
31
        return $this->run($query, $bindings, function ($query, $bindings) {
32
            if ($this->pretending()) {
0 ignored issues
show
Bug introduced by
It seems like pretending() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

32
            if ($this->/** @scrutinizer ignore-call */ pretending()) {
Loading history...
33
                return [];
34
            }
35
36
            $statement = $this->arangoClient->prepare($query, $bindings);
37
38
            return $statement->execute();
39
        });
40
    }
41
42
    /**
43
     * Execute an AQL statement and return the boolean result.
44
     *
45
     * @param  string|ArangoQueryBuilder  $query
46
     * @param  array  $bindings
47
     * @return bool
48
     */
49 24
    public function statement($query, $bindings = []): bool
50
    {
51 24
        [$query, $bindings] = $this->handleQueryBuilder(
52 24
            $query,
53 24
            $bindings
54 24
        );
55
56 24
        return $this->run($query, $bindings, function ($query, $bindings) {
57 24
            if ($this->pretending()) {
58
                return true;
59
            }
60
61 24
            $statement = $this->arangoClient->prepare($query, $bindings);
62
63 24
            $statement->execute();
64
65 23
            $affectedDocumentCount = $statement->getWritesExecuted();
66 23
            $this->recordsHaveBeenModified($changed = $affectedDocumentCount > 0);
0 ignored issues
show
Bug introduced by
It seems like recordsHaveBeenModified() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

66
            $this->/** @scrutinizer ignore-call */ 
67
                   recordsHaveBeenModified($changed = $affectedDocumentCount > 0);
Loading history...
67
68 23
            return $changed;
69 24
        });
70
    }
71
72
    /**
73
     * Run an AQL statement and get the number of rows affected.
74
     *
75
     * @param  string|ArangoQueryBuilder  $query
76
     * @param  array  $bindings
77
     * @return int
78
     */
79 35
    public function affectingStatement($query, $bindings = []): int
80
    {
81 35
        [$query, $bindings] = $this->handleQueryBuilder(
82 35
            $query,
83 35
            $bindings
84 35
        );
85
86 35
        return $this->run($query, $bindings, function () use ($query, $bindings) {
87 35
            if ($this->pretending()) {
88
                return 0;
89
            }
90
91
            // For update or delete statements, we want to get the number of rows affected
92
            // by the statement and return that back to the developer. We'll first need
93
            // to execute the statement and get the executed writes from the extra.
94 35
            $statement = $this->arangoClient->prepare($query, $bindings);
95
96 35
            $statement->execute();
97
98 35
            $affectedDocumentCount = $statement->getWritesExecuted();
99
100 35
            $this->recordsHaveBeenModified($affectedDocumentCount > 0);
101
102 35
            return $affectedDocumentCount;
103 35
        });
104
    }
105
106
    /**
107
     * Run a raw, unprepared query against the connection.
108
     *
109
     * @param  string  $query
110
     * @return bool
111
     */
112
    public function unprepared($query): bool
113
    {
114
        return $this->run($query, [], function ($query) {
115
            if ($this->pretending()) {
116
                return true;
117
            }
118
119
            $statement = $$this->arangoClient->prepare($query);
120
            $statement->execute();
121
            $affectedDocumentCount = $statement->getWritesExecuted();
122
            $change = $affectedDocumentCount > 0;
123
124
            $this->recordsHaveBeenModified($change);
125
126
            return $change;
127
        });
128
    }
129
130
    /**
131
     * Returns the query execution plan. The query will not be executed.
132
     *
133
     * @param  string  $query
134
     * @param  array<mixed>  $bindings
135
     * @return stdClass
136
     */
137 1
    public function explain(string|ArangoQueryBuilder $query, $bindings = []): stdClass
138
    {
139 1
        [$query, $bindings] = $this->handleQueryBuilder(
140 1
            $query,
141 1
            $bindings
142 1
        );
143
144 1
        $statement = $this->arangoClient->prepare($query, $bindings);
145
146 1
        return $statement->explain();
147
    }
148
149
    /**
150
     * @param  ArangoQueryBuilder|string  $query
151
     * @param  array  $bindings
152
     * @return array
153
     */
154 146
    protected function handleQueryBuilder($query, array $bindings): array
155
    {
156 146
        if ($query instanceof ArangoQueryBuilder) {
157 132
            $bindings = $query->binds;
158 132
            $query = $query->query;
159
        }
160
161 146
        return [$query, $bindings];
162
    }
163
164
    /**
165
     * Run a select statement against the database.
166
     *
167
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
168
     *
169
     * @param  string|ArangoQueryBuilder  $query
170
     * @param  array  $bindings
171
     * @param  bool  $useReadPdo
172
     * @return mixed
173
     */
174 138
    public function select($query, $bindings = [], $useReadPdo = true)
175
    {
176 138
        return $this->execute($query, $bindings, $useReadPdo);
177
    }
178
179
    /**
180
     * Run an AQL query against the database and return the results.
181
     *
182
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
183
     *
184
     * @param  string|ArangoQueryBuilder  $query
185
     * @param  array<mixed>|null  $bindings
186
     * @param  bool  $useReadPdo
187
     * @return mixed
188
     */
189 144
    public function execute($query, ?array $bindings = [], $useReadPdo = true)
0 ignored issues
show
Unused Code introduced by
The parameter $useReadPdo is not used and could be removed. ( Ignorable by Annotation )

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

189
    public function execute($query, ?array $bindings = [], /** @scrutinizer ignore-unused */ $useReadPdo = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
190
    {
191
        // Usage of a separate DB to read date isn't supported at this time
192 144
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
193
194 144
        [$query, $bindings] = $this->handleQueryBuilder(
195 144
            $query,
196 144
            $bindings
197 144
        );
198
199 144
        return $this->run($query, $bindings, function () use ($query, $bindings) {
200 144
            if ($this->pretending()) {
201
                return [];
202
            }
203
204 144
            $statement = $this->arangoClient->prepare($query, $bindings);
205 144
            $statement->execute();
206
207 139
            return $statement->fetchAll();
208 144
        });
209
    }
210
211
    /**
212
     * Get a new query builder instance.
213
     *
214
     * @return QueryBuilder
215
     */
216 133
    public function query(): QueryBuilder
217
    {
218 133
        return new QueryBuilder(
219 133
            $this,
220 133
            $this->getQueryGrammar(),
0 ignored issues
show
Bug introduced by
It seems like getQueryGrammar() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

220
            $this->/** @scrutinizer ignore-call */ 
221
                   getQueryGrammar(),
Loading history...
221 133
            $this->getPostProcessor()
0 ignored issues
show
Bug introduced by
It seems like getPostProcessor() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

221
            $this->/** @scrutinizer ignore-call */ 
222
                   getPostProcessor()
Loading history...
222 133
        );
223
    }
224
225
    /**
226
     * Run a SQL statement and log its execution context.
227
     *
228
     * @param  string  $query
229
     * @param  array  $bindings
230
     * @param  Closure  $callback
231
     * @return mixed
232
     *
233
     * @throws QueryException
234
     */
235 145
    protected function run($query, $bindings, Closure $callback)
236
    {
237 145
        foreach ($this->beforeExecutingCallbacks as $beforeExecutingCallback) {
238
            $beforeExecutingCallback($query, $bindings, $this);
239
        }
240
241 145
        $this->reconnectIfMissingConnection();
0 ignored issues
show
Bug introduced by
It seems like reconnectIfMissingConnection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

241
        $this->/** @scrutinizer ignore-call */ 
242
               reconnectIfMissingConnection();
Loading history...
242
243 145
        $start = microtime(true);
244
245
        // Here we will run this query. If an exception occurs we'll determine if it was
246
        // caused by a connection that has been lost. If that is the cause, we'll try
247
        // to re-establish connection and re-run the query with a fresh connection.
248
        try {
249 145
            $result = $this->runQueryCallback($query, $bindings, $callback);
250 6
        } catch (QueryException $e) {
251 6
            $result = $this->handleQueryException(
0 ignored issues
show
Bug introduced by
It seems like handleQueryException() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

251
            /** @scrutinizer ignore-call */ 
252
            $result = $this->handleQueryException(
Loading history...
252 6
                $e,
253 6
                $query,
254 6
                $bindings,
255 6
                $callback
256 6
            );
257
        }
258
259
        // Once we have run the query we will calculate the time that it took to run and
260
        // then log the query, bindings, and execution time so we will report them on
261
        // the event that the developer needs them. We'll log time in milliseconds.
262 139
        $this->logQuery(
0 ignored issues
show
Bug introduced by
It seems like logQuery() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

262
        $this->/** @scrutinizer ignore-call */ 
263
               logQuery(
Loading history...
263 139
            $query,
264 139
            $bindings,
265 139
            $this->getElapsedTime($start)
0 ignored issues
show
Bug introduced by
It seems like getElapsedTime() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

265
            $this->/** @scrutinizer ignore-call */ 
266
                   getElapsedTime($start)
Loading history...
266 139
        );
267
268 139
        return $result;
269
    }
270
271
    /**
272
     * Run a SQL statement.
273
     *
274
     * @param  string  $query
275
     * @param  array  $bindings
276
     * @param  Closure  $callback
277
     * @return mixed
278
     *
279
     * @throws QueryException
280
     */
281 145
    protected function runQueryCallback($query, $bindings, Closure $callback)
282
    {
283
        // To execute the statement, we'll simply call the callback, which will actually
284
        // run the SQL against the PDO connection. Then we can calculate the time it
285
        // took to execute and log the query SQL, bindings and time in our memory.
286
        try {
287 145
            return $callback($query, $bindings);
288 6
        } catch (Exception $e) {
289
            // If an exception occurs when attempting to run a query, we'll format the error
290
            // message to include the bindings with SQL, which will make this exception a
291
            // lot more helpful to the developer instead of just the database's errors.
292
293 6
            throw new QueryException(
294 6
                $query,
295 6
                $this->prepareBindings($bindings),
0 ignored issues
show
Bug introduced by
It seems like prepareBindings() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

295
                $this->/** @scrutinizer ignore-call */ 
296
                       prepareBindings($bindings),
Loading history...
296 6
                $e
297 6
            );
298
        }
299
    }
300
}
301