Completed
Push — master ( c30967...3bd52a )
by Andrii
03:06
created

AbstractQueryBuilder   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 12.09%

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 7
dl 0
loc 272
ccs 11
cts 91
cp 0.1209
rs 8.2608
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A build() 0 4 1
A createRequest() 0 6 2
buildAuth() 0 1 ?
buildMethod() 0 1 ?
buildUri() 0 1 ?
buildHeaders() 0 1 ?
buildProtocolVersion() 0 1 ?
buildQueryParams() 0 1 ?
buildFormParams() 0 1 ?
buildBody() 0 1 ?
A prepare() 0 4 1
A insert() 0 4 1
A update() 0 6 1
A delete() 0 6 1
B buildCondition() 0 37 5
A buildLikeCondition() 0 4 1
A buildIlikeCondition() 0 4 1
A buildCompareCondition() 0 8 2
A buildBetweenCondition() 0 4 1
A buildNotInCondition() 0 4 1
A buildEqCondition() 0 6 1
A buildNotEqCondition() 0 6 1
A buildCompositeInCondition() 0 4 1
A perform() 0 6 1
A createQuery() 0 6 1
A buildHashCondition() 0 14 3
A buildAndCondition() 0 14 4
D buildInCondition() 0 30 9

How to fix   Complexity   

Complex Class

Complex classes like AbstractQueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractQueryBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * ActiveRecord for API
4
 *
5
 * @link      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\helpers\ArrayHelper;
16
17
/**
18
 * Abstract QueryBuilder.
19
 *
20
 * QueryBuilder builds a request from the specification given as a [[Query]] object.
21
 */
22
abstract class AbstractQueryBuilder extends \yii\base\Object implements QueryBuilderInterface
23
{
24
    /**
25
     * @var AbstractConnection
26
     */
27
    public $db;
28
29 1
    public function __construct($connection, $config = [])
30
    {
31 1
        $this->db = $connection;
32 1
        parent::__construct($config);
33 1
    }
34
35
    /**
36
     * Builds config array to create Command.
37
     * @param Query $query
38
     * @throws NotSupportedException
39
     * @return array
40
     */
41 2
    public function build(Query $query)
42
    {
43 2
        return ['request' => $this->createRequest($query)];
44
    }
45
46 2
    public function createRequest($query)
47
    {
48 2
        $request = new $this->db->requestClass($this, $query);
49
50 2
        return $request instanceof RequestCreatorInterface ? $request->createRequest() : $request;
51
    }
52
53
    /**
54
     * Prepares query before actual building.
55
     * This function for you to redefine.
56
     * It will be called before other build functions.
57
     * @param Query $query
58
     */
59 2
    public function prepare(Query $query)
60
    {
61 2
        return $query->prepare($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<hiqdev\hiart\AbstractQueryBuilder>, 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...
62
    }
63
64
    /**
65
     * This function is for you to provide your authentication.
66
     * @param Query $query
67
     */
68
    abstract public function buildAuth(Query $query);
69
70
    abstract public function buildMethod(Query $query);
71
72
    abstract public function buildUri(Query $query);
73
74
    abstract public function buildHeaders(Query $query);
75
76
    abstract public function buildProtocolVersion(Query $query);
77
78
    abstract public function buildQueryParams(Query $query);
79
80
    abstract public function buildFormParams(Query $query);
81
82
    abstract public function buildBody(Query $query);
83
84
    /**
85
     * Creates insert request.
86
     * @param string $table
87
     * @param array $columns
88
     * @param array $options
89
     * @return AbstractRequest
90
     */
91
    public function insert($table, $columns, array $options = [])
92
    {
93
        return $this->perform('insert', $table, $columns, $options);
94
    }
95
96
    /**
97
     * Creates update request.
98
     * @param string $table
99
     * @param array $columns
100
     * @param array $condition
101
     * @param array $options
102
     * @return AbstractRequest
103
     */
104
    public function update($table, $columns, $condition = [], array $options = [])
105
    {
106
        $query = $this->createQuery('update', $table, $options)->body($columns)->where($condition);
107
108
        return $this->createRequest($query);
109
    }
110
111
    /**
112
     * Creates delete request.
113
     * @param string $table
114
     * @param array $condition
115
     * @param array $options
116
     * @return AbstractRequest
117
     */
118
    public function delete($table, $condition = [], array $options = [])
119
    {
120
        $query = $this->createQuery('delete', $table, $options)->where($condition);
121
122
        return $this->createRequest($query);
123
    }
124
125
    /**
126
     * Creates request for given action.
127
     * @param string $action
128
     * @param string $table
129
     * @param mixed $body
130
     * @param array $options
131
     * @return AbstractRequest
132
     */
133
    public function perform($action, $table, $body, $options = [])
134
    {
135
        $query = $this->createQuery($action, $table, $options)->body($body);
136
137
        return $this->createRequest($query);
138
    }
139
140
    public function createQuery($action, $table, array $options = [])
141
    {
142
        $class = $this->db->queryClass;
143
144
        return $class::instantiate($action, $table, $options);
145
    }
146
147
    public function buildCondition($condition)
148
    {
149
        static $builders = [
150
            'and'     => 'buildAndCondition',
151
            'between' => 'buildBetweenCondition',
152
            'eq'      => 'buildEqCondition',
153
            'ne'      => 'buildNotEqCondition',
154
            'in'      => 'buildInCondition',
155
            'ni'      => 'buildNotInCondition',
156
            'like'    => 'buildLikeCondition',
157
            'ilike'   => 'buildIlikeCondition',
158
            'gt'      => 'buildCompareCondition',
159
            'ge'      => 'buildCompareCondition',
160
            'lt'      => 'buildCompareCondition',
161
            'le'      => 'buildCompareCondition',
162
        ];
163
        if (empty($condition)) {
164
            return [];
165
        }
166
        if (!is_array($condition)) {
167
            throw new NotSupportedException('String conditions in where() are not supported by HiArt.');
168
        }
169
170
        if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
171
            $operator = strtolower($condition[0]);
172
            if (isset($builders[$operator])) {
173
                $method = $builders[$operator];
174
                array_shift($condition); // Shift build condition
175
176
                return $this->$method($operator, $condition);
177
            } else {
178
                throw new InvalidParamException('Found unknown operator in query: ' . $operator);
179
            }
180
        } else {
181
            return $this->buildHashCondition($condition);
182
        }
183
    }
184
185
    protected function buildHashCondition($condition)
186
    {
187
        $parts = [];
188
        foreach ($condition as $attribute => $value) {
189
            if (is_array($value)) { // IN condition
190
                // $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...
191
                $parts[$attribute . 's'] = implode(',', $value);
192
            } else {
193
                $parts[$attribute] = $value;
194
            }
195
        }
196
197
        return $parts;
198
    }
199
200
    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...
201
    {
202
        return [$operands[0] . '_like' => $operands[1]];
203
    }
204
205
    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...
206
    {
207
        return [$operands[0] . '_ilike' => $operands[1]];
208
    }
209
210
    protected function buildCompareCondition($operator, $operands)
211
    {
212
        if (!isset($operands[0], $operands[1])) {
213
            throw new InvalidParamException("Operator '$operator' requires three operands.");
214
        }
215
216
        return [$operands[0] . '_' . $operator => $operands[1]];
217
    }
218
219
    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...
220
    {
221
        $parts = [];
222
        foreach ($operands as $operand) {
223
            if (is_array($operand)) {
224
                $parts = ArrayHelper::merge($this->buildCondition($operand), $parts);
225
            }
226
        }
227
        if (!empty($parts)) {
228
            return $parts;
229
        } else {
230
            return [];
231
        }
232
    }
233
234
    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...
235
    {
236
        throw new NotSupportedException('Between condition is not supported by HiArt.');
237
    }
238
239
    protected function buildInCondition($operator, $operands, $not = false)
240
    {
241
        if (!isset($operands[0], $operands[1])) {
242
            throw new InvalidParamException("Operator '$operator' requires two operands.");
243
        }
244
245
        list($column, $values) = $operands;
246
247
        if (count($column) > 1) {
248
            return $this->buildCompositeInCondition($operator, $column, $values);
249
        } elseif (is_array($column)) {
250
            $column = reset($column);
251
        }
252
253
        foreach ((array) $values as $i => $value) {
254
            if (is_array($value)) {
255
                $values[$i] = $value = isset($value[$column]) ? $value[$column] : null;
256
            }
257
            if ($value === null) {
258
                unset($values[$i]);
259
            }
260
        }
261
262
        if ($not) {
263
            $key = $column . '_ni'; // not in
264
        } else {
265
            $key = $column . '_in';
266
        }
267
        return [$key => $values];
268
    }
269
270
    protected function buildNotInCondition($operator, $operands)
271
    {
272
        return $this->buildInCondition($operator, $operands, true);
273
    }
274
275
    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...
276
    {
277
        $key = array_shift($operands);
278
279
        return [$key => reset($operands)];
280
    }
281
282
    protected function buildNotEqCondition($operator, $operands)
283
    {
284
        $key = array_shift($operands);
285
286
        return [$key . '_' . $operator => reset($operands)];
287
    }
288
289
    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...
290
    {
291
        throw new NotSupportedException('composite in is not supported by HiArt.');
292
    }
293
}
294