GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — dev (#4)
by James Ekow Abaka
30:10 queued 28:29
created

QueryOperations::doWith()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 5
cp 0
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
/*
4
 * The MIT License
5
 *
6
 * Copyright 2014-2018 James Ekow Abaka Ainooson
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26
27
namespace ntentan\nibii;
28
29
use ntentan\atiaa\Driver;
30
use ntentan\nibii\exceptions\ModelNotFoundException;
31
use ntentan\utils\Text;
32
33
/**
34
 * Performs operations that query the database.
35
 */
36
class QueryOperations
37
{
38
    /**
39
     * An instance of the record wrapper being used.
40
     *
41
     * @var RecordWrapper
42
     */
43
    private $wrapper;
44
45
    /**
46
     * An instance of the driver adapter used in the database connection.
47
     *
48
     * @var DriverAdapter
49
     */
50
    private $adapter;
51
52
    /**
53
     * An instance of query parameters used to perform the various queries.
54
     *
55
     * @var QueryParameters
56
     */
57
    private $queryParameters;
58
59
    /**
60
     * The name of a method initialized through a dynamic method waiting to be executed.
61
     *
62
     * @var string
63
     */
64
    private $pendingMethod;
65
66
    /**
67
     * Regular expressions for matching dynamic methods.
68
     *
69
     * @var array
70
     */
71
    private $dynamicMethods = [
72
        '/(?<method>filterBy)(?<variable>[A-Z][A-Za-z]+){1}/',
73
        '/(?<method>sort)(?<direction>Asc|Desc)?(By)(?<variable>[A-Z][A-Za-z]+){1}/',
74
        '/(?<method>fetch)(?<first>First)?(With)(?<variable>[A-Za-z]+)/',
75
    ];
76
77
    /**
78
     * An instance of the DataOperations class used for filtered deletes.
79
     *
80
     * @var DataOperations
81
     */
82
    private $dataOperations;
83
84
    /**
85
     * An instance of the Driver class used for establishing database connections.
86
     *
87
     * @var Driver
88
     */
89
    private $driver;
90
91
    /**
92
     * QueryOperations constructor.
93
     *
94
     * @param RecordWrapper  $wrapper
95
     * @param DataOperations $dataOperations
96
     * @param Driver         $driver
97
     *
98
     * @internal param DriverAdapter $adapter
99
     */
100 34
    public function __construct(RecordWrapper $wrapper, DataOperations $dataOperations, Driver $driver)
101
    {
102 34
        $this->wrapper = $wrapper;
103 34
        $this->adapter = $wrapper->getAdapter();
104 34
        $this->dataOperations = $dataOperations;
105 34
        $this->driver = $driver;
106 34
    }
107
108
    /**
109
     * Fetches items from the database.
110
     *
111
     * @param int|array|QueryParameters $query
112
     *
113
     * @return RecordWrapper
114
     */
115 24
    public function doFetch($query = null)
116
    {
117 24
        $parameters = $this->buildFetchQueryParameters($query);
118 24
        $data = $this->adapter->select($parameters);
119 24
        $this->wrapper->setData($data);
120 24
        $this->resetQueryParameters();
121
122 24
        return $this->wrapper;
123
    }
124
125
    /**
126
     * The method takes multiple types of arguments and converts it to a QueryParametersObject.
127
     * When this method receives null, it returns a new instance of QueryParameters. When it receives an integer, it
128
     * returns a QueryParameters object that points the primary key to the integer. When it receives an associative
129
     * array, it builds a series of conditions with array key-value pairs.
130
     *
131
     * @param int|array|QueryParameters $arg
132
     * @param bool                      $instantiate
133
     *
134
     * @return QueryParameters
135
     */
136 26
    private function buildFetchQueryParameters($arg, $instantiate = true)
137
    {
138 26
        if ($arg instanceof QueryParameters) {
139 12
            return $arg;
140
        }
141
142 26
        $parameters = $this->getQueryParameters($instantiate);
143
144 26
        if (is_numeric($arg)) {
145 6
            $description = $this->wrapper->getDescription();
146 6
            $parameters->addFilter($description->getPrimaryKey()[0], $arg);
147 6
            $parameters->setFirstOnly(true);
148 22
        } elseif (is_array($arg)) {
149 6
            foreach ($arg as $field => $value) {
150 6
                $parameters->addFilter($field, $value);
151
            }
152
        }
153
154 26
        return $parameters;
155
    }
156
157
    /**
158
     * Creates a new instance of the QueryParameters if required or just returns an already instance.
159
     *
160
     * @param bool $forceInstantiation
161
     *
162
     * @return QueryParameters
163
     */
164 32
    private function getQueryParameters($forceInstantiation = true)
165
    {
166 32
        if ($this->queryParameters === null && $forceInstantiation) {
167 32
            $this->queryParameters = new QueryParameters($this->wrapper->getDBStoreInformation()['quoted_table']);
168
        }
169
170 32
        return $this->queryParameters;
171
    }
172
173
    /**
174
     * Clears up the query parameters.
175
     */
176 32
    private function resetQueryParameters()
177
    {
178 32
        $this->queryParameters = null;
179 32
    }
180
181
    /**
182
     * Performs the fetch operation and returns just the first item.
183
     *
184
     * @param mixed $id
185
     *
186
     * @return RecordWrapper
187
     */
188 10
    public function doFetchFirst($id = null)
189
    {
190 10
        $this->getQueryParameters()->setFirstOnly(true);
191
192 10
        return $this->doFetch($id);
193
    }
194
195
    /**
196
     * Set the fields that should be returned for each record.
197
     *
198
     * @return RecordWrapper
199
     */
200 12
    public function doFields()
201
    {
202 12
        $fields = [];
203 12
        $arguments = func_get_args();
204 12
        foreach ($arguments as $argument) {
205 12
            if (is_array($argument)) {
206 6
                $fields = array_merge($fields, $argument);
207
            } else {
208 12
                $fields[] = $argument;
209
            }
210
        }
211 12
        $this->getQueryParameters()->setFields($fields);
212
213 12
        return $this->wrapper;
214
    }
215
216
    /**
217
     * Sort the query by a given field in a given directory.
218
     *
219
     * @param string $field
220
     * @param string $direction
221
     */
222
    public function doSortBy($field, $direction = 'ASC')
223
    {
224
        $this->getQueryParameters()->addSort($field, $direction);
225
    }
226
227
    /**
228
     * @param mixed $arguments
229
     *
230
     * @return array
231
     */
232 10
    private function getFilter($arguments)
233
    {
234 10
        if (count($arguments) == 2 && is_array($arguments[1])) {
235 2
            $filter = $arguments[0];
236 2
            $data = $arguments[1];
237
        } else {
238 10
            $filter = array_shift($arguments);
239 10
            $data = $arguments;
240
        }
241
242 10
        return ['filter' => $filter, 'data' => $data];
243
    }
244
245 6
    public function doFilter()
246
    {
247 6
        $arguments = func_get_args();
248 6
        if (count($arguments) == 1 && is_array($arguments[0])) {
249
            foreach ($arguments[0] as $field => $value) {
250
                $this->getQueryParameters()->addFilter($field, $value);
251
            }
252
        } else {
253 6
            $details = $this->getFilter($arguments);
254 6
            $this->getQueryParameters()->setFilter($details['filter'], $details['data']);
255
        }
256
257 6
        return $this->wrapper;
258
    }
259
260 4
    public function doFilterBy()
261
    {
262 4
        $arguments = func_get_args();
263 4
        $details = $this->getFilter($arguments);
264 4
        $this->getQueryParameters()->addFilter($details['filter'], $details['data']);
265
266 4
        return $this->wrapper;
267
    }
268
269 6
    public function doUpdate($data)
270
    {
271 6
        $this->driver->beginTransaction();
272 6
        $parameters = $this->getQueryParameters();
273 6
        $this->adapter->bulkUpdate($data, $parameters);
274 6
        $this->driver->commit();
275 6
        $this->resetQueryParameters();
276 6
    }
277
278 2
    public function doDelete($args = null)
279
    {
280 2
        $this->driver->beginTransaction();
281 2
        $parameters = $this->buildFetchQueryParameters($args);
282
283 2
        if ($parameters === null) {
284
            $primaryKey = $this->wrapper->getDescription()->getPrimaryKey();
285
            $parameters = $this->getQueryParameters();
286
            $data = $this->wrapper->getData();
287
            $keys = [];
288
289
            foreach ($data as $datum) {
290
                if ($this->dataOperations->isItemDeletable($primaryKey, $datum)) {
0 ignored issues
show
Documentation introduced by
$primaryKey is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
291
                    $keys[] = $datum[$primaryKey[0]];
292
                }
293
            }
294
295
            $parameters->addFilter($primaryKey[0], $keys);
296
            $this->adapter->delete($parameters);
297
        } else {
298 2
            $this->adapter->delete($parameters);
299
        }
300
301 2
        $this->driver->commit();
302 2
        $this->resetQueryParameters();
303 2
    }
304
305 10
    public function runDynamicMethod($arguments)
306
    {
307 10
        $arguments = count($arguments) > 1 ? $arguments : ($arguments[0] ?? null);
308 10
        switch ($this->pendingMethod['method']) {
309 10
            case 'filterBy':
310 4
                $this->getQueryParameters()->addFilter(Text::deCamelize($this->pendingMethod['variable']), $arguments);
311
312 4
                return $this->wrapper;
313 8
            case 'sort':
314
                $this->getQueryParameters()->addSort(Text::deCamelize($this->pendingMethod['variable']), $this->pendingMethod['direction']);
315
316
                return $this->wrapper;
317 8
            case 'fetch':
318 8
                $parameters = $this->getQueryParameters();
319 8
                $parameters->addFilter(Text::deCamelize($this->pendingMethod['variable']), $arguments);
320 8
                if ($this->pendingMethod['first'] === 'First') {
321 8
                    $parameters->setFirstOnly(true);
322
                }
323
324 8
                return $this->doFetch();
325
        }
326
    }
327
328 10
    public function initDynamicMethod($method)
329
    {
330 10
        $return = false;
331
332 10
        foreach ($this->dynamicMethods as $regexp) {
333 10
            if (preg_match($regexp, $method, $matches)) {
334 10
                $return = true;
335 10
                $this->pendingMethod = $matches;
0 ignored issues
show
Documentation Bug introduced by
It seems like $matches of type array<integer,string> is incompatible with the declared type string of property $pendingMethod.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
336 10
                break;
337
            }
338
        }
339
340 10
        return $return;
341
    }
342
343
    public function doCount($query = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
344
    {
345
        return $this->adapter->count($this->buildFetchQueryParameters($query));
346
    }
347
348
    public function doLimit($numItems)
349
    {
350
        $this->getQueryParameters()->setLimit($numItems);
351
352
        return $this->wrapper;
353
    }
354
355
    public function doOffset($offset)
356
    {
357
        $this->getQueryParameters()->setOffset($offset);
358
359
        return $this->wrapper;
360
    }
361
362
    public function doWith($model)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
363
    {
364
        if (!isset($this->wrapper->getDescription()->getRelationships()[$model])) {
365
            throw new ModelNotFoundException("Could not find related model [$model]");
366
        }
367
        $relationship = $this->wrapper->getDescription()->getRelationships()[$model];
368
369
        return $relationship->getQuery();
370
    }
371
}
372