Passed
Push — next ( 3b41f7...f014dd )
by Bas
02:34
created

RunsQueries::run()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3.004

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 34
ccs 12
cts 13
cp 0.9231
rs 9.7
cc 3
nc 4
nop 3
crap 3.004
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
     *
48
     * @return bool
49
     */
50 136
    public function statement($query, $bindings = []): bool
51
    {
52 136
        [$query, $bindings] = $this->handleQueryBuilder(
0 ignored issues
show
Bug introduced by
It seems like handleQueryBuilder() 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

52
        /** @scrutinizer ignore-call */ 
53
        [$query, $bindings] = $this->handleQueryBuilder(
Loading history...
53 136
            $query,
54
            $bindings
55
        );
56
57 136
        return $this->run($query, $bindings, function ($query, $bindings) {
58 136
            if ($this->pretending()) {
59
                return true;
60
            }
61
62 136
            $statement = $this->arangoClient->prepare($query, $bindings);
63
64 136
            $statement->execute();
65
66 135
            $affectedDocumentCount = $statement->getWritesExecuted();
67 135
            $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

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

179
    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...
180
    {
181
        // Usage of a separate DB to read date isn't supported at this time
182 156
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
183
184 156
        [$query, $bindings] = $this->handleQueryBuilder(
185 156
            $query,
186
            $bindings
187
        );
188
189 156
        return $this->run($query, $bindings, function () use ($query, $bindings) {
190 156
            if ($this->pretending()) {
191
                return [];
192
            }
193
194 156
            $statement = $this->arangoClient->prepare($query, $bindings);
195 156
            $statement->execute();
196
197 151
            return $statement->fetchAll();
198 156
        });
199
    }
200
201
    /**
202
     * Get a new query builder instance.
203
     *
204
     * @return QueryBuilder
205
     */
206 124
    public function query(): QueryBuilder
207
    {
208 124
        return new QueryBuilder(
209 124
            $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

209
            /** @scrutinizer ignore-type */ $this,
Loading history...
210 124
            $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

210
            $this->/** @scrutinizer ignore-call */ 
211
                   getQueryGrammar(),
Loading history...
211 124
            $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

211
            $this->/** @scrutinizer ignore-call */ 
212
                   getPostProcessor()
Loading history...
212
        );
213
    }
214
215
    /**
216
     * Run a SQL statement and log its execution context.
217
     *
218
     * @param  string  $query
219
     * @param  array  $bindings
220
     * @param  Closure  $callback
221
     * @return mixed
222
     *
223
     * @throws QueryException
224
     */
225 157
    protected function run($query, $bindings, Closure $callback)
226
    {
227 157
        foreach ($this->beforeExecutingCallbacks as $beforeExecutingCallback) {
228
            $beforeExecutingCallback($query, $bindings, $this);
229
        }
230
231 157
        $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

231
        $this->/** @scrutinizer ignore-call */ 
232
               reconnectIfMissingConnection();
Loading history...
232
233 157
        $start = microtime(true);
234
235
        // Here we will run this query. If an exception occurs we'll determine if it was
236
        // caused by a connection that has been lost. If that is the cause, we'll try
237
        // to re-establish connection and re-run the query with a fresh connection.
238
        try {
239 157
            $result = $this->runQueryCallback($query, $bindings, $callback);
240 6
        } catch (QueryException $e) {
241 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

241
            /** @scrutinizer ignore-call */ 
242
            $result = $this->handleQueryException(
Loading history...
242 6
                $e,
243
                $query,
244
                $bindings,
245
                $callback
246
            );
247
        }
248
249
        // Once we have run the query we will calculate the time that it took to run and
250
        // then log the query, bindings, and execution time so we will report them on
251
        // the event that the developer needs them. We'll log time in milliseconds.
252 151
        $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

252
        $this->/** @scrutinizer ignore-call */ 
253
               logQuery(
Loading history...
253 151
            $query,
254
            $bindings,
255 151
            $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

255
            $this->/** @scrutinizer ignore-call */ 
256
                   getElapsedTime($start)
Loading history...
256
        );
257
258 151
        return $result;
259
    }
260
261
    /**
262
     * Run a SQL statement.
263
     *
264
     * @param  string  $query
265
     * @param  array  $bindings
266
     * @param  Closure  $callback
267
     * @return mixed
268
     *
269
     * @throws QueryException
270
     */
271 157
    protected function runQueryCallback($query, $bindings, Closure $callback)
272
    {
273
        // To execute the statement, we'll simply call the callback, which will actually
274
        // run the SQL against the PDO connection. Then we can calculate the time it
275
        // took to execute and log the query SQL, bindings and time in our memory.
276
        try {
277 157
            return $callback($query, $bindings);
278 6
        } catch (Exception $e) {
279
            // If an exception occurs when attempting to run a query, we'll format the error
280
            // message to include the bindings with SQL, which will make this exception a
281
            // lot more helpful to the developer instead of just the database's errors.
282
283 6
            throw new QueryException(
284 6
                $query,
285 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

285
                $this->/** @scrutinizer ignore-call */ 
286
                       prepareBindings($bindings),
Loading history...
286
                $e
287
            );
288
        }
289
    }
290
}
291