Completed
Push — master ( 6c60b4...c31ce4 )
by BENOIT
01:38
created

InsertQueryBuilder   B

Complexity

Total Complexity 32

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 6
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 273
rs 8.7272
c 0
b 0
f 0
wmc 32
lcom 6
cbo 2

17 Methods

Rating   Name   Duplication   Size   Complexity  
A load() 0 8 2
A withValues() 0 6 1
A and() 0 8 2
A withMainKeyword() 0 6 1
A into() 0 7 2
A withFlags() 0 6 1
A withAddedFlags() 0 11 3
A withEscaper() 0 6 1
A onDuplicateKeyUpdate() 0 6 1
A end() 0 6 1
B validateValue() 0 21 5
A escape() 0 4 2
A __toString() 0 4 1
A split() 0 11 2
A getValues() 0 19 4
A getColumns() 0 4 1
A __get() 0 7 2
1
<?php
2
declare(strict_types=1);
3
4
namespace BenTools\Where\InsertQuery;
5
6
use function BenTools\FlattenIterator\flatten;
7
8
/**
9
 * Class InsertQueryBuilder
10
 *
11
 * @property $mainKeyword
12
 * @property $flags
13
 * @property $values
14
 * @property $columns
15
 * @property $table
16
 * @property $onDuplicate
17
 * @property $end
18
 * @property $escape
19
 */
20
final class InsertQueryBuilder
21
{
22
23
    /**
24
     * @var string
25
     */
26
    private $mainKeyword = 'INSERT';
27
28
    /**
29
     * @var array
30
     */
31
    private $flags = [];
32
33
    /**
34
     * @var array
35
     */
36
    private $values = [];
37
38
    /**
39
     * @var array
40
     */
41
    private $columns;
42
43
    /**
44
     * @var string
45
     */
46
    private $table;
47
48
    /**
49
     * @var array
50
     */
51
    private $onDuplicate;
52
53
    /**
54
     * @var string
55
     */
56
    private $end = ';';
57
58
    /**
59
     * @var string
60
     */
61
    private $escape;
62
63
    /**
64
     * @param array[] ...$values
65
     * @return InsertQueryBuilder
66
     */
67
    public static function load(array ...$values): self
68
    {
69
        $query = new self;
70
        foreach ($values as $value) {
71
            $query->values[] = $query->validateValue($value);
72
        }
73
        return $query;
74
    }
75
76
    /**
77
     * @param array[] ...$values
78
     * @return InsertQueryBuilder
79
     */
80
    public function withValues(array ...$values): self
81
    {
82
        $clone = clone $this;
83
        $clone->values = $values;
84
        return $clone;
85
    }
86
87
    /**
88
     * @param array[] ...$values
89
     * @return InsertQueryBuilder
90
     */
91
    public function and(array ...$values): self
92
    {
93
        $clone = clone $this;
94
        foreach ($values as $value) {
95
            $clone->values[] = $value;
96
        }
97
        return $clone;
98
    }
99
100
    /**
101
     * @param string $keyword
102
     * @return InsertQueryBuilder
103
     */
104
    public function withMainKeyword(string $keyword): self
105
    {
106
        $clone = clone $this;
107
        $clone->mainKeyword = $keyword;
108
        return $clone;
109
    }
110
111
    /**
112
     * @param string   $table
113
     * @param string[] ...$columns
114
     * @return InsertQueryBuilder
115
     */
116
    public function into(string $table, string ...$columns): self
117
    {
118
        $clone = clone $this;
119
        $clone->table = $table;
120
        $clone->columns = [] === $columns ? null : $columns;
121
        return $clone;
122
    }
123
124
125
    /**
126
     * @param string[] ...$flags
127
     * @return InsertQueryBuilder
128
     */
129
    public function withFlags(string ...$flags): self
130
    {
131
        $clone = clone $this;
132
        $clone->flags = $flags;
133
        return $clone;
134
    }
135
136
    /**
137
     * @param string[] ...$flags
138
     * @return InsertQueryBuilder
139
     */
140
    public function withAddedFlags(string ...$flags): self
141
    {
142
        $clone = clone $this;
143
        $existingFlags = array_map('strtoupper', $clone->flags);
144
        foreach ($flags as $flag) {
145
            if (!in_array(strtoupper($flag), $existingFlags, true)) {
146
                $clone->flags[] = $flag;
147
            }
148
        }
149
        return $clone;
150
    }
151
152
    /**
153
     * @param string|null $escape
154
     * @return InsertQueryBuilder
155
     */
156
    public function withEscaper(string $escape = null): self
157
    {
158
        $clone = clone $this;
159
        $clone->escape = $escape;
160
        return $clone;
161
    }
162
163
    /**
164
     * @param array $updateConditions
165
     * @return InsertQueryBuilder
166
     */
167
    public function onDuplicateKeyUpdate(array $updateConditions = null): self
168
    {
169
        $clone = clone $this;
170
        $clone->onDuplicate = $updateConditions;
0 ignored issues
show
Documentation Bug introduced by
It seems like $updateConditions can be null. However, the property $onDuplicate is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

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

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
171
        return $clone;
172
    }
173
174
    /**
175
     * @param null $end
176
     * @return InsertQueryBuilder
177
     */
178
    public function end($end = null): self
179
    {
180
        $clone = clone $this;
181
        $clone->end = $end;
182
        return $clone;
183
    }
184
185
    /**
186
     * @param array $value
187
     * @return array
188
     * @throws \InvalidArgumentException
189
     */
190
    private function validateValue(array &$value): array
191
    {
192
        if (empty($this->values)) {
193
            return $value;
194
        }
195
        $keys = array_keys($this->values[0]);
196
        $valueKeys = array_keys($value);
197
        if ($valueKeys !== $keys) {
198
            if (count($keys) !== count($valueKeys)) {
199
                throw new \InvalidArgumentException("Invalid value.");
200
            }
201
            uksort($value, function ($key1, $key2) use ($keys) {
202
                return array_search($key1, $keys) <=> array_search($key2, $keys);
203
            });
204
            $valueKeys = array_keys($value);
205
            if ($valueKeys !== $keys) {
206
                throw new \InvalidArgumentException("Invalid value.");
207
            }
208
        }
209
        return $value;
210
    }
211
212
    /**
213
     * @param string $value
214
     * @return string
215
     */
216
    public function escape(string $value): string
217
    {
218
        return null === $this->escape ? $value : $this->escape . $value . $this->escape;
219
    }
220
221
    /**
222
     * @return string
223
     */
224
    public function __toString(): string
225
    {
226
        return InsertQueryStringifier::stringify($this);
227
    }
228
229
    /**
230
     * Split into multiple INSERT statements.
231
     *
232
     * @param int $max
233
     * @return iterable|self[]
234
     */
235
    public function split(int $max): iterable
236
    {
237
        $pos = 0;
238
        $total = count($this->values);
239
        while ($pos < $total) {
240
            $clone = clone $this;
241
            $clone->values = array_slice($this->values, $pos, $max);
242
            $pos += $max;
243
            yield $clone;
244
        }
245
    }
246
247
    /**
248
     * @return array
249
     */
250
    public function getValues(): array
251
    {
252
        $generator = function (array $values, array $columns) {
253
            foreach ($values as $value) {
254
                $valueKeys = array_keys($value);
255
                if ($valueKeys !== $columns) {
256
                    $value = array_intersect_key($value, array_combine($columns, array_fill(0, count($columns), null)));
257
                    $valueKeys = array_keys($value);
258
                    if ($valueKeys !== $columns) {
259
                        uksort($value, function ($key1, $key2) use ($columns) {
260
                            return array_search($key1, $columns) <=> array_search($key2, $columns);
261
                        });
262
                    }
263
                }
264
                yield $value;
265
            }
266
        };
267
        return flatten($generator($this->values, $this->getColumns()))->asArray();
268
    }
269
270
    /**
271
     * @return array
272
     */
273
    public function getColumns()
274
    {
275
        return $this->columns ?? array_keys($this->values[0] ?? []);
276
    }
277
278
    /**
279
     * Read-only properties.
280
     *
281
     * @param $property
282
     * @return mixed
283
     * @throws \InvalidArgumentException
284
     */
285
    public function __get($property)
286
    {
287
        if (!property_exists($this, $property)) {
288
            throw new \InvalidArgumentException(sprintf('Property %s::$%s does not exist.', __CLASS__, $property));
289
        }
290
        return $this->{$property};
291
    }
292
}
293