Passed
Push — refactor/improve-static-analys... ( efcf20...37f12c )
by Bas
03:04
created

RunsQueries   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Test Coverage

Coverage 72.5%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 100
c 1
b 0
f 0
dl 0
loc 303
ccs 87
cts 120
cp 0.725
rs 10
wmc 26

11 Methods

Rating   Name   Duplication   Size   Complexity  
A unprepared() 0 15 2
A runQueryCallback() 0 17 2
A run() 0 34 3
A query() 0 6 1
A affectingStatement() 0 28 3
A cursor() 0 17 3
A explain() 0 13 2
A statement() 0 24 3
A select() 0 3 1
A handleQueryBuilder() 0 14 3
A execute() 0 24 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Concerns;
6
7
use Closure;
8
use Exception;
9
use LaravelFreelancerNL\Aranguent\Exceptions\NoArangoClientException;
10
use LaravelFreelancerNL\Aranguent\Query\Builder as QueryBuilder;
11
use LaravelFreelancerNL\Aranguent\Exceptions\QueryException;
12
use LaravelFreelancerNL\FluentAQL\QueryBuilder as FluentAqlBuilder;
13
use stdClass;
14
15
trait RunsQueries
16
{
17
    /**
18
     * Run a select statement against the database and returns a generator.
19
     *
20
     * @param  string  $query
21
     * @param  array<mixed>  $bindings
22
     * @param  bool  $useReadPdo
23
     * @return \Generator
24
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
25
     */
26
    public function cursor($query, $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

26
    public function cursor($query, $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...
27
    {
28
29
        // Usage of a separate DB to read date isn't supported at this time
30
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
31
32
        return $this->run($query, $bindings, function ($query, $bindings) {
33
            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

33
            if ($this->/** @scrutinizer ignore-call */ pretending()) {
Loading history...
34
                return [];
35
            }
36
37
            if ($this->arangoClient === null) {
38
                throw new NoArangoClientException();
39
            }
40
            $statement = $this->arangoClient->prepare($query, $bindings);
41
42
            return $statement->execute();
43
        });
44
    }
45
46
    /**
47
     * Execute an SQL statement and return the boolean result.
48
     *
49
     * @param  string  $query
50
     * @param  array<mixed>  $bindings
51
     * @return bool
52
     */
53 28
    public function statement($query, $bindings = [])
54
    {
55 28
        [$query, $bindings] = $this->handleQueryBuilder(
56 28
            $query,
57 28
            $bindings
58 28
        );
59
60 28
        return $this->run($query, $bindings, function ($query, $bindings) {
61 28
            if ($this->pretending()) {
62
                return true;
63
            }
64
65 28
            if ($this->arangoClient === null) {
66
                throw new NoArangoClientException();
67
            }
68 28
            $statement = $this->arangoClient->prepare($query, $bindings);
69
70 28
            $statement->execute();
71
72 27
            $affectedDocumentCount = $statement->getWritesExecuted();
73
74 27
            $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

74
            $this->/** @scrutinizer ignore-call */ 
75
                   recordsHaveBeenModified($changed = $affectedDocumentCount > 0);
Loading history...
75
76 27
            return $changed;
77 28
        });
78
    }
79
80
    /**
81
     * Run an SQL statement and get the number of rows affected.
82
     *
83
     * @param  string  $query
84
     * @param  array<mixed>  $bindings
85
     * @return int
86
     */
87 54
    public function affectingStatement($query, $bindings = [])
88
    {
89 54
        [$query, $bindings] = $this->handleQueryBuilder(
90 54
            $query,
91 54
            $bindings
92 54
        );
93
94 54
        return $this->run($query, $bindings, function () use ($query, $bindings) {
95 54
            if ($this->pretending()) {
96
                return 0;
97
            }
98
99 54
            if ($this->arangoClient === null) {
100
                throw new NoArangoClientException();
101
            }
102
103
            // For update or delete statements, we want to get the number of rows affected
104
            // by the statement and return that back to the developer. We'll first need
105
            // to execute the statement and get the executed writes from the extra.
106 54
            $statement = $this->arangoClient->prepare($query, $bindings);
107
108 54
            $statement->execute();
109
110 54
            $affectedDocumentCount = $statement->getWritesExecuted();
111
112 54
            $this->recordsHaveBeenModified($affectedDocumentCount > 0);
113
114 54
            return $affectedDocumentCount;
115 54
        });
116
    }
117
118
    /**
119
     * Run a raw, unprepared query against the connection.
120
     *
121
     * @param  string  $query
122
     */
123
    public function unprepared($query): bool
124
    {
125
        return $this->run($query, [], function ($query) {
126
            if ($this->pretending()) {
127
                return true;
128
            }
129
130
            $statement = $$this->arangoClient->prepare($query);
131
            $statement->execute();
132
            $affectedDocumentCount = $statement->getWritesExecuted();
133
            $change = $affectedDocumentCount > 0;
134
135
            $this->recordsHaveBeenModified($change);
136
137
            return $change;
138
        });
139
    }
140
141
    /**
142
     * Returns the query execution plan. The query will not be executed.
143
     *
144
     * @param  string  $query
145
     * @param  array<mixed>  $bindings
146
     */
147 1
    public function explain(string|FluentAqlBuilder $query, $bindings = []): stdClass
148
    {
149 1
        [$query, $bindings] = $this->handleQueryBuilder(
150 1
            $query,
151 1
            $bindings
152 1
        );
153
154 1
        if ($this->arangoClient === null) {
155
            throw new NoArangoClientException();
156
        }
157 1
        $statement = $this->arangoClient->prepare($query, $bindings);
158
159 1
        return $statement->explain();
160
    }
161
162
    /**
163
     * @param FluentAqlBuilder|string|QueryBuilder $query
164
     * @param array<mixed> $bindings
165
     * @return array<mixed>
166
     */
167 220
    protected function handleQueryBuilder($query, array $bindings): array
168
    {
169
170 220
        if ($query instanceof FluentAqlBuilder) {
171
            $bindings = $query->binds;
172
            $query = $query->query;
173
        }
174
175 220
        if ($query instanceof QueryBuilder) {
176
            $bindings = $query->getBindings();
177
            $query = $query->toSql();
178
        }
179
180 220
        return [$query, $bindings];
181
    }
182
183
    /**
184
     * Run a select statement against the database.
185
     *
186
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
187
     *
188
     * @param  string|FluentAqlBuilder  $query
189
     * @param  array<mixed>  $bindings
190
     * @param  bool  $useReadPdo
191
     * @return mixed
192
     */
193 214
    public function select($query, $bindings = [], $useReadPdo = true)
194
    {
195 214
        return $this->execute($query, $bindings, $useReadPdo);
196
    }
197
198
    /**
199
     * Run an AQL query against the database and return the results.
200
     *
201
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
202
     *
203
     * @param  string|FluentAqlBuilder  $query
204
     * @param  array<mixed>  $bindings
205
     * @param  bool  $useReadPdo
206
     * @return mixed
207
     */
208 218
    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

208
    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...
209
    {
210
        // Usage of a separate DB to read date isn't supported at this time
211 218
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
212
213 218
        [$query, $bindings] = $this->handleQueryBuilder(
214 218
            $query,
215 218
            $bindings
216 218
        );
217
218 218
        return $this->run($query, $bindings, function () use ($query, $bindings) {
219 218
            if ($this->pretending()) {
220
                return [];
221
            }
222
223 218
            if ($this->arangoClient === null) {
224
                throw new NoArangoClientException();
225
            }
226
227 218
            $statement = $this->arangoClient->prepare($query, $bindings);
228
229 218
            $statement->execute();
230
231 213
            return $statement->fetchAll();
232 218
        });
233
    }
234
235
    /**
236
     * Get a new query builder instance.
237
     */
238 224
    public function query(): QueryBuilder
239
    {
240 224
        return new QueryBuilder(
241 224
            $this,
0 ignored issues
show
Bug introduced by
$this of type LaravelFreelancerNL\Aranguent\Concerns\RunsQueries is incompatible with the type Illuminate\Database\ConnectionInterface expected by parameter $connection of LaravelFreelancerNL\Aran...\Builder::__construct(). ( Ignorable by Annotation )

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

241
            /** @scrutinizer ignore-type */ $this,
Loading history...
242 224
            $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

242
            $this->/** @scrutinizer ignore-call */ 
243
                   getQueryGrammar(),
Loading history...
243 224
            $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

243
            $this->/** @scrutinizer ignore-call */ 
244
                   getPostProcessor()
Loading history...
244 224
        );
245
    }
246
247
    /**
248
     * Run a SQL statement and log its execution context.
249
     *
250
     * @param  string  $query
251
     * @param  array<mixed>  $bindings
252
     * @return mixed
253
     *
254
     * @throws QueryException
255
     */
256 219
    protected function run($query, $bindings, Closure $callback)
257
    {
258 219
        foreach ($this->beforeExecutingCallbacks as $beforeExecutingCallback) {
259
            $beforeExecutingCallback($query, $bindings, $this);
260
        }
261
262 219
        $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

262
        $this->/** @scrutinizer ignore-call */ 
263
               reconnectIfMissingConnection();
Loading history...
263
264 219
        $start = microtime(true);
265
266
        // Here we will run this query. If an exception occurs we'll determine if it was
267
        // caused by a connection that has been lost. If that is the cause, we'll try
268
        // to re-establish connection and re-run the query with a fresh connection.
269
        try {
270 219
            $result = $this->runQueryCallback($query, $bindings, $callback);
271 6
        } catch (QueryException $e) {
272 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

272
            /** @scrutinizer ignore-call */ 
273
            $result = $this->handleQueryException(
Loading history...
273 6
                $e,
274 6
                $query,
275 6
                $bindings,
276 6
                $callback
277 6
            );
278
        }
279
280
        // Once we have run the query we will calculate the time that it took to run and
281
        // then log the query, bindings, and execution time so we will report them on
282
        // the event that the developer needs them. We'll log time in milliseconds.
283 213
        $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

283
        $this->/** @scrutinizer ignore-call */ 
284
               logQuery(
Loading history...
284 213
            $query,
285 213
            $bindings,
286 213
            $this->getElapsedTime((int) $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

286
            $this->/** @scrutinizer ignore-call */ 
287
                   getElapsedTime((int) $start)
Loading history...
287 213
        );
288
289 213
        return $result;
290
    }
291
292
    /**
293
     * Run a SQL statement.
294
     *
295
     * @param  string  $query
296
     * @param  array<mixed>  $bindings
297
     * @return mixed
298
     *
299
     * @throws QueryException
300
     */
301 219
    protected function runQueryCallback($query, $bindings, Closure $callback)
302
    {
303
        // To execute the statement, we'll simply call the callback, which will actually
304
        // run the SQL against the PDO connection. Then we can calculate the time it
305
        // took to execute and log the query SQL, bindings and time in our memory.
306
        try {
307 219
            return $callback($query, $bindings);
308 6
        } catch (Exception $e) {
309
            // If an exception occurs when attempting to run a query, we'll format the error
310
            // message to include the bindings with SQL, which will make this exception a
311
            // lot more helpful to the developer instead of just the database's errors.
312
313 6
            throw new QueryException(
314 6
                (string) $this->getName(),
0 ignored issues
show
Bug introduced by
It seems like getName() 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

314
                (string) $this->/** @scrutinizer ignore-call */ getName(),
Loading history...
315 6
                $query,
316 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

316
                $this->/** @scrutinizer ignore-call */ 
317
                       prepareBindings($bindings),
Loading history...
317 6
                $e
318 6
            );
319
        }
320
    }
321
}
322