Completed
Push — master ( d3756e...27ca6f )
by Dmitry
02:19
created

QueryBuilder   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 42
lcom 1
cbo 5
dl 0
loc 221
ccs 0
cts 173
cp 0
rs 8.295
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A build() 0 18 1
A buildLimit() 0 9 3
A buildPage() 0 6 2
A buildOrderBy() 0 6 2
A buildSelect() 0 8 3
B buildCondition() 0 37 5
A buildHashCondition() 0 14 3
A buildLikeCondition() 0 4 1
A buildIlikeCondition() 0 4 1
A buildCompareCondition() 0 8 2
A buildAndCondition() 0 14 4
A buildBetweenCondition() 0 4 1
D buildInCondition() 0 30 9
A buildNotInCondition() 0 4 1
A buildEqCondition() 0 6 1
A buildNotEqCondition() 0 6 1
A buildCompositeInCondition() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like QueryBuilder 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 QueryBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * Tools to use API as ActiveRecord for Yii2
5
 *
6
 * @link      https://github.com/hiqdev/yii2-hiart
7
 * @package   yii2-hiart
8
 * @license   BSD-3-Clause
9
 * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/)
10
 */
11
12
namespace hiqdev\hiart;
13
14
use yii\base\InvalidParamException;
15
use yii\base\NotSupportedException;
16
use yii\helpers\ArrayHelper;
17
18
/**
19
 * QueryBuilder builds an HiArt query based on the specification given as a [[Query]] object.
20
 */
21
class QueryBuilder extends \yii\base\Object
22
{
23
    private $_sort = [
24
        SORT_ASC  => '_asc',
25
        SORT_DESC => '_desc',
26
    ];
27
28
    public $db;
29
30
    public function __construct($connection, $config = [])
31
    {
32
        $this->db = $connection;
33
        parent::__construct($config);
34
    }
35
36
    /**
37
     * @param ActiveQuery $query
38
     *
39
     * @throws NotSupportedException
40
     *
41
     * @return array
42
     */
43
    public function build($query)
44
    {
45
        $parts = [];
46
        $query->prepare();
47
48
        $this->buildSelect($query->select, $parts);
49
        $this->buildLimit($query->limit, $parts);
50
        $this->buildPage($query->offset, $query->limit, $parts);
51
        $this->buildOrderBy($query->orderBy, $parts);
52
53
        $parts = ArrayHelper::merge($parts, $this->buildCondition($query->where));
54
55
        return [
56
            'queryParts' => $parts,
57
            'index'      => $query->index,
58
            'type'       => $query->type,
59
        ];
60
    }
61
62
    public function buildLimit($limit, &$parts)
63
    {
64
        if (!empty($limit)) {
65
            if ($limit === -1) {
66
                $limit = 'ALL';
67
            }
68
            $parts['limit'] = $limit;
69
        }
70
    }
71
72
    public function buildPage($offset, $limit, &$parts)
73
    {
74
        if ($offset > 0) {
75
            $parts['page'] = ceil($offset / $limit) + 1;
76
        }
77
    }
78
79
    public function buildOrderBy($orderBy, &$parts)
80
    {
81
        if (!empty($orderBy)) {
82
            $parts['orderby'] = key($orderBy) . $this->_sort[reset($orderBy)];
83
        }
84
    }
85
86
    public function buildSelect($select, &$parts)
87
    {
88
        if (!empty($select)) {
89
            foreach ($select as $attribute) {
90
                $parts['select'][$attribute] = $attribute;
91
            }
92
        }
93
    }
94
95
    public function buildCondition($condition)
96
    {
97
        static $builders = [
98
            'and'     => 'buildAndCondition',
99
            'between' => 'buildBetweenCondition',
100
            'eq'      => 'buildEqCondition',
101
            'ne'      => 'buildNotEqCondition',
102
            'in'      => 'buildInCondition',
103
            'ni'      => 'buildNotInCondition',
104
            'like'    => 'buildLikeCondition',
105
            'ilike'   => 'buildIlikeCondition',
106
            'gt'      => 'buildCompareCondition',
107
            'ge'      => 'buildCompareCondition',
108
            'lt'      => 'buildCompareCondition',
109
            'le'      => 'buildCompareCondition',
110
        ];
111
        if (empty($condition)) {
112
            return [];
113
        }
114
        if (!is_array($condition)) {
115
            throw new NotSupportedException('String conditions in where() are not supported by HiArt.');
116
        }
117
118
        if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
119
            $operator = strtolower($condition[0]);
120
            if (isset($builders[$operator])) {
121
                $method = $builders[$operator];
122
                array_shift($condition); // Shift build condition
123
124
                return $this->$method($operator, $condition);
125
            } else {
126
                throw new InvalidParamException('Found unknown operator in query: ' . $operator);
127
            }
128
        } else {
129
            return $this->buildHashCondition($condition);
130
        }
131
    }
132
133
    private function buildHashCondition($condition)
134
    {
135
        $parts = [];
136
        foreach ($condition as $attribute => $value) {
137
            if (is_array($value)) { // IN condition
138
                // $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...
139
                $parts[$attribute . 's'] = implode(',', $value);
140
            } else {
141
                $parts[$attribute] = $value;
142
            }
143
        }
144
145
        return $parts;
146
    }
147
148
    private 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...
149
    {
150
        return [$operands[0] . '_like' => $operands[1]];
151
    }
152
153
    private 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...
154
    {
155
        return [$operands[0] . '_ilike' => $operands[1]];
156
    }
157
158
    private function buildCompareCondition($operator, $operands)
159
    {
160
        if (!isset($operands[0], $operands[1])) {
161
            throw new InvalidParamException("Operator '$operator' requires three operands.");
162
        }
163
164
        return [$operands[0] . '_' . $operator => $operands[1]];
165
    }
166
167
    private 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...
168
    {
169
        $parts = [];
170
        foreach ($operands as $operand) {
171
            if (is_array($operand)) {
172
                $parts = \yii\helpers\ArrayHelper::merge($this->buildCondition($operand), $parts);
173
            }
174
        }
175
        if (!empty($parts)) {
176
            return $parts;
177
        } else {
178
            return [];
179
        }
180
    }
181
182
    private 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...
183
    {
184
        throw new NotSupportedException('Between condition is not supported by HiArt.');
185
    }
186
187
    private function buildInCondition($operator, $operands, $not = false)
188
    {
189
        if (!isset($operands[0], $operands[1])) {
190
            throw new InvalidParamException("Operator '$operator' requires two operands.");
191
        }
192
193
        list($column, $values) = $operands;
194
195
        if (count($column) > 1) {
196
            return $this->buildCompositeInCondition($operator, $column, $values);
197
        } elseif (is_array($column)) {
198
            $column = reset($column);
199
        }
200
201
        foreach ((array) $values as $i => $value) {
202
            if (is_array($value)) {
203
                $values[$i] = $value = isset($value[$column]) ? $value[$column] : null;
204
            }
205
            if ($value === null) {
206
                unset($values[$i]);
207
            }
208
        }
209
210
        if ($not) {
211
            $key = $column . '_ni'; // not in
212
        } else {
213
            $key = $column . '_in';
214
        }
215
        return [$key => $values];
216
    }
217
218
    private function buildNotInCondition($operator, $operands)
219
    {
220
        return $this->buildInCondition($operator, $operands, true);
221
    }
222
223
    private 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...
224
    {
225
        $key = array_shift($operands);
226
227
        return [$key => reset($operands)];
228
    }
229
230
    private function buildNotEqCondition($operator, $operands)
231
    {
232
        $key = array_shift($operands);
233
234
        return [$key . '_' . $operator => reset($operands)];
235
    }
236
237
    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...
238
    {
239
        throw new NotSupportedException('composite in is not supported by HiArt.');
240
    }
241
}
242