Issues (65)

src/AbstractQueryBuilder.php (4 issues)

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-2019, 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\BaseObject implements QueryBuilderInterface
23
{
24
    /**
25
     * @var AbstractConnection
26
     */
27
    public $db;
28
29 2
    public function __construct($connection, $config = [])
30
    {
31 2
        $this->db = $connection;
32 2
        parent::__construct($config);
33 2
    }
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 3
    public function createRequest($query)
47
    {
48 3
        $request = new $this->db->requestClass($this, $query);
49
50 3
        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
$this of type hiqdev\hiart\AbstractQueryBuilder is incompatible with the type yii\db\QueryBuilder expected by parameter $builder of yii\db\Query::prepare(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

61
        return $query->prepare(/** @scrutinizer ignore-type */ $this);
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 1
    public function insert($table, $columns, array $options = [])
92
    {
93 1
        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 1
    public function perform($action, $table, $body, $options = [])
134
    {
135 1
        $query = $this->createQuery($action, $table, $options)->body($body);
136
137 1
        return $this->createRequest($query);
138
    }
139
140 1
    public function createQuery($action, $table, array $options = [])
141
    {
142 1
        $class = $this->db->queryClass;
143
144 1
        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);
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated: since 2.0.14. Use [[InvalidArgumentException]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

178
                throw /** @scrutinizer ignore-deprecated */ new InvalidParamException('Found unknown operator in query: ' . $operator);
Loading history...
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)];
191
                $parts[$attribute] = implode(',', $value);
192
            } else {
193
                $parts[$attribute] = $value;
194
            }
195
        }
196
197
        return $parts;
198
    }
199
200
    protected function buildLikeCondition($operator, $operands)
201
    {
202
        return [$operands[0] . '_like' => $operands[1]];
203
    }
204
205
    protected function buildIlikeCondition($operator, $operands)
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.");
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated: since 2.0.14. Use [[InvalidArgumentException]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

213
            throw /** @scrutinizer ignore-deprecated */ new InvalidParamException("Operator '$operator' requires three operands.");
Loading history...
214
        }
215
216
        return [$operands[0] . '_' . $operator => $operands[1]];
217
    }
218
219
    protected function buildAndCondition($operator, $operands)
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)
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.");
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated: since 2.0.14. Use [[InvalidArgumentException]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

242
            throw /** @scrutinizer ignore-deprecated */ new InvalidParamException("Operator '$operator' requires two operands.");
Loading history...
243
        }
244
245
        list($column, $values) = $operands;
246
247
        if ($column instanceof \Countable && 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
268
        return [$key => $values];
269
    }
270
271
    protected function buildNotInCondition($operator, $operands)
272
    {
273
        return $this->buildInCondition($operator, $operands, true);
274
    }
275
276
    protected function buildEqCondition($operator, $operands)
277
    {
278
        $key = array_shift($operands);
279
280
        return [$key => reset($operands)];
281
    }
282
283
    protected function buildNotEqCondition($operator, $operands)
284
    {
285
        $key = array_shift($operands);
286
287
        return [$key . '_' . $operator => reset($operands)];
288
    }
289
290
    protected function buildCompositeInCondition($operator, $columns, $values)
291
    {
292
        throw new NotSupportedException('composite in is not supported by HiArt.');
293
    }
294
}
295