Completed
Pull Request — master (#10)
by Edgard
15:15
created

QueryBuilder::buildInCondition()   D

Complexity

Conditions 9
Paths 30

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 4.909
c 0
b 0
f 0
cc 9
eloc 18
nc 30
nop 3
1
<?php
2
/**
3
 * ActiveRecord for API.
4
 *
5
 * @see      https://github.com/hiqdev/yii2-hiart
6
 * @package   yii2-hiart
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\hiart;
12
13
use yii\base\InvalidParamException;
14
use yii\base\NotSupportedException;
15
use yii\base\Object;
16
use yii\helpers\ArrayHelper;
17
use yii\httpclient\Request;
18
19
/**
20
 * Abstract QueryBuilder.
21
 *
22
 * QueryBuilder builds a request from the specification given as a [[Query]] object.
23
 */
24
class QueryBuilder extends Object
25
{
26
    /**
27
     * @var Connection
28
     */
29
    public $db;
30
31
    public function __construct($connection, $config = [])
32
    {
33
        $this->db = $connection;
34
        parent::__construct($config);
35
    }
36
37
    /**
38
     * @param array $params
39
     * @return Request
40
     */
41
    public function createRequest($params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
42
    {
43
        return $this->db->createRequest();
44
    }
45
46
    /**
47
     * Builds config array to create Command.
48
     * @param Query $query
49
     * @throws NotSupportedException
50
     * @return Request
51
     */
52
    public function build(Query $query, $params = [], array $options = [])
53
    {
54
        $query = $query->prepare($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<hiqdev\hiart\QueryBuilder>, but the function expects a object<yii\db\QueryBuilder>.

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...
55
        $params = empty($params) ? $query->params : array_merge($params, $query->params);
56
57
        $request = $this->createRequest($params);
58
59
        $this->buildAuth($request, $params);
0 ignored issues
show
Unused Code introduced by
The call to the method hiqdev\hiart\QueryBuilder::buildAuth() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
60
        $this->buildMethod($request, 'select', $params);
61
        $this->buildFrom($request, $query->from, $params);
62
        $this->buildWhere($request, $query->where, $params);
63
64
        $this->buildCount($request, $query->count, $params);
65
        $this->buildOrderBy($request, $query->orderBy, $params);
0 ignored issues
show
Unused Code introduced by
The call to the method hiqdev\hiart\QueryBuilder::buildOrderBy() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
66
        $this->buildLimit($request, $query->limit, $query->offset, $params);
0 ignored issues
show
Bug introduced by
It seems like $query->offset can also be of type object<yii\db\Expression>; however, hiqdev\hiart\QueryBuilder::buildLimit() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Unused Code introduced by
The call to the method hiqdev\hiart\QueryBuilder::buildLimit() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
67
68
        $request->setOptions($options);
69
70
        return $request;
71
    }
72
73
    public function buildAuth(Request $request, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
74
    {
75
        return $request;
76
    }
77
78
    public function buildMethod(Request $request, $action, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
79
    {
80
        static $defaultMethods = [
81
            'delete' => 'DELETE',
82
            'get'    => 'GET',
83
            'head'   => 'HEAD',
84
            'insert' => 'POST',
85
            'post'   => 'GET',
86
            'put'    => 'PUT',
87
            'search' => 'GET',
88
            'select' => 'GET',
89
            'update' => 'PUT',
90
        ];
91
92
        $method = isset($defaultMethods[$action]) ? $defaultMethods[$action] : 'POST';
93
        return $request->setMethod($method);
94
    }
95
96
    public function buildFrom(Request $request, $from, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
97
    {
98
        return $request->setUrl($from);
99
    }
100
101
    public function buildWhere(Request $request, $condition, $params = [])
102
    {
103
        $where = $this->buildCondition($condition, $params);
0 ignored issues
show
Unused Code introduced by
The call to QueryBuilder::buildCondition() has too many arguments starting with $params.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
104
105
        if ($where) {
106
            $request->addData($where);
107
        }
108
109
        return $request;
110
    }
111
112
    public function buildCount(Request $request, $count, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
113
    {
114
        if ($count) {
115
            $request->on(Request::EVENT_AFTER_SEND, function (\yii\httpclient\RequestEvent $event) {
116
                $data = $event->response->getData();
117
                $event->response->setData(count($data));
118
            });
119
        }
120
121
        return $request;
122
    }
123
124
    public function buildOrderBy(Request $request, $orderBy, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $orderBy is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
125
    {
126
        return $request;
127
    }
128
129
    public function buildLimit(Request $request, $limit, $offset = 0, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $limit is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $offset is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
130
    {
131
        return $request;
132
    }
133
134
    /**
135
     * Creates insert request.
136
     * @param string $url
137
     * @param array $columns
138
     * @param array $options
139
     * @return AbstractTransport
140
     */
141
    public function insert($url, $columns, &$params = [], array $options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $url is not used and could be removed.

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

Loading history...
142
    {
143
        $request = $this->createRequest();
144
145
        $this->buildMethod($request, 'insert', $params);
146
        $this->buildFrom($request, url, $params);
147
148
        $request->setData($columns);
149
        $request->setOptions($options);
150
151
        return $request;
152
    }
153
154
    /**
155
     * Creates update request.
156
     * @param string $url
157
     * @param array $columns
158
     * @param array $condition
159
     * @param array $options
160
     * @return AbstractTransport
161
     */
162 View Code Duplication
    public function update($url, $columns, $condition = [], &$params = [], array $options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $url is not used and could be removed.

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

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
163
    {
164
        $request = $this->createRequest();
165
        $this->buildMethod($request, 'update', $params);
166
        $this->buildFrom($request, url, $params);
167
168
        $this->buildWhere($request, $condition, $params);
169
170
        $request->addData($columns);
171
        $request->setOptions($options);
172
173
        return $request;
174
    }
175
176
    /**
177
     * Creates delete request.
178
     * @param string $table
179
     * @param array $condition
180
     * @param array $options
181
     * @return AbstractTransport
182
     */
183 View Code Duplication
    public function delete($table, $condition = [], &$params = [], array $options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $table is not used and could be removed.

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

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
    {
185
        $request = $this->createRequest();
186
        $this->buildMethod($request, 'update', $params);
187
        $this->buildFrom($request, url, $params);
188
        $this->buildWhere($request, $condition, $params);
189
190
        $request->setOptions($options);
191
192
        return $request;
193
    }
194
195
    /**
196
     * Creates request for given action.
197
     * @param string $action
198
     * @param string $table
199
     * @param mixed $body
200
     * @param array $options
201
     * @return AbstractTransport
202
     */
203
    public function perform($action, $table, $body, $options = [])
204
    {
205
        $query = $this->createQuery($action, $table, $options)->body($body);
0 ignored issues
show
Documentation Bug introduced by
The method createQuery does not exist on object<hiqdev\hiart\QueryBuilder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
206
207
        return $this->createRequest($query);
208
    }
209
210
    public function buildCondition($condition)
211
    {
212
        static $builders = [
213
            'and'     => 'buildAndCondition',
214
            'between' => 'buildBetweenCondition',
215
            'eq'      => 'buildEqCondition',
216
            'ne'      => 'buildNotEqCondition',
217
            'in'      => 'buildInCondition',
218
            'ni'      => 'buildNotInCondition',
219
            'like'    => 'buildLikeCondition',
220
            'ilike'   => 'buildIlikeCondition',
221
            'gt'      => 'buildCompareCondition',
222
            'ge'      => 'buildCompareCondition',
223
            'lt'      => 'buildCompareCondition',
224
            'le'      => 'buildCompareCondition',
225
        ];
226
        if (empty($condition)) {
227
            return [];
228
        }
229
        if (!is_array($condition)) {
230
            throw new NotSupportedException('String conditions in where() are not supported by HiArt.');
231
        }
232
233
        if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
234
            $operator = strtolower($condition[0]);
235
            if (isset($builders[$operator])) {
236
                $method = $builders[$operator];
237
                array_shift($condition); // Shift build condition
238
239
                return $this->$method($operator, $condition);
240
            } else {
241
                throw new InvalidParamException('Found unknown operator in query: ' . $operator);
242
            }
243
        } else {
244
            return $this->buildHashCondition($condition);
245
        }
246
    }
247
248
    protected function buildHashCondition($condition)
249
    {
250
        $parts = [];
251
        foreach ($condition as $attribute => $value) {
252
            if (is_array($value)) { // IN condition
253
                // $parts[] = [$attribute.'s' => join(',',$value)];
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
254
                $parts[$attribute . 's'] = implode(',', $value);
255
            } else {
256
                $parts[$attribute] = $value;
257
            }
258
        }
259
260
        return $parts;
261
    }
262
263
    protected function buildLikeCondition($operator, $operands)
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

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

Loading history...
264
    {
265
        return [$operands[0] . '_like' => $operands[1]];
266
    }
267
268
    protected function buildIlikeCondition($operator, $operands)
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

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

Loading history...
269
    {
270
        return [$operands[0] . '_ilike' => $operands[1]];
271
    }
272
273
    protected function buildCompareCondition($operator, $operands)
274
    {
275
        if (!isset($operands[0], $operands[1])) {
276
            throw new InvalidParamException("Operator '$operator' requires three operands.");
277
        }
278
279
        return [$operands[0] . '_' . $operator => $operands[1]];
280
    }
281
282
    protected function buildAndCondition($operator, $operands)
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

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

Loading history...
283
    {
284
        $parts = [];
285
        foreach ($operands as $operand) {
286
            if (is_array($operand)) {
287
                $parts = ArrayHelper::merge($this->buildCondition($operand), $parts);
288
            }
289
        }
290
        if (!empty($parts)) {
291
            return $parts;
292
        } else {
293
            return [];
294
        }
295
    }
296
297
    protected function buildBetweenCondition($operator, $operands)
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $operands is not used and could be removed.

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

Loading history...
298
    {
299
        throw new NotSupportedException('Between condition is not supported by HiArt.');
300
    }
301
302
    protected function buildInCondition($operator, $operands, $not = false)
303
    {
304
        if (!isset($operands[0], $operands[1])) {
305
            throw new InvalidParamException("Operator '$operator' requires two operands.");
306
        }
307
308
        list($column, $values) = $operands;
309
310
        if (count($column) > 1) {
311
            return $this->buildCompositeInCondition($operator, $column, $values);
312
        } elseif (is_array($column)) {
313
            $column = reset($column);
314
        }
315
316
        foreach ((array) $values as $i => $value) {
317
            if (is_array($value)) {
318
                $values[$i] = $value = isset($value[$column]) ? $value[$column] : null;
319
            }
320
            if ($value === null) {
321
                unset($values[$i]);
322
            }
323
        }
324
325
        if ($not) {
326
            $key = $column . '_ni'; // not in
327
        } else {
328
            $key = $column . '_in';
329
        }
330
        return [$key => $values];
331
    }
332
333
    protected function buildNotInCondition($operator, $operands)
334
    {
335
        return $this->buildInCondition($operator, $operands, true);
336
    }
337
338
    protected function buildEqCondition($operator, $operands)
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

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

Loading history...
339
    {
340
        $key = array_shift($operands);
341
342
        return [$key => reset($operands)];
343
    }
344
345
    protected function buildNotEqCondition($operator, $operands)
346
    {
347
        $key = array_shift($operands);
348
349
        return [$key . '_' . $operator => reset($operands)];
350
    }
351
352
    protected function buildCompositeInCondition($operator, $columns, $values)
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $columns is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $values is not used and could be removed.

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

Loading history...
353
    {
354
        throw new NotSupportedException('composite in is not supported by HiArt.');
355
    }
356
}
357