Completed
Push — master ( d1730c...d58317 )
by BENOIT
26:43
created

InsertQueryBuilder::load()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
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
     * @throws \InvalidArgumentException
67
     */
68
    public static function load(array ...$values): self
69
    {
70
        if (0 === count($values)) {
71
            throw new \InvalidArgumentException("At least 1 value is needed.");
72
        }
73
74
        $query = new self;
75
        foreach ($values as $value) {
76
            $query->values[] = $query->validateValue($value);
77
        }
78
        return $query;
79
    }
80
81
    /**
82
     * @param string $keyword
83
     * @return InsertQueryBuilder
84
     */
85
    public function withMainKeyword(string $keyword): self
86
    {
87
        $clone = clone $this;
88
        $clone->mainKeyword = $keyword;
89
        return $clone;
90
    }
91
92
    /**
93
     * @param string   $table
94
     * @param string[] ...$columns
95
     * @return InsertQueryBuilder
96
     */
97
    public function into(string $table, string ...$columns): self
98
    {
99
        $clone = clone $this;
100
        $clone->table = $table;
101
        $clone->columns = [] === $columns ? null : $columns;
102
        return $clone;
103
    }
104
105
106
    /**
107
     * @param string[] ...$flags
108
     * @return InsertQueryBuilder
109
     */
110
    public function withFlags(string ...$flags): self
111
    {
112
        $clone = clone $this;
113
        $clone->flags = $flags;
114
        return $clone;
115
    }
116
117
    /**
118
     * @param string[] ...$flags
119
     * @return InsertQueryBuilder
120
     */
121
    public function withAddedFlags(string ...$flags): self
122
    {
123
        $clone = clone $this;
124
        $existingFlags = array_map('strtoupper', $clone->flags);
125
        foreach ($flags as $flag) {
126
            if (!in_array(strtoupper($flag), $existingFlags, true)) {
127
                $clone->flags[] = $flag;
128
            }
129
        }
130
        return $clone;
131
    }
132
133
    /**
134
     * @param string|null $escape
135
     * @return InsertQueryBuilder
136
     */
137
    public function withEscaper(string $escape = null): self
138
    {
139
        $clone = clone $this;
140
        $clone->escape = $escape;
141
        return $clone;
142
    }
143
144
    /**
145
     * @param array $updateConditions
146
     * @return InsertQueryBuilder
147
     */
148
    public function onDuplicateKeyUpdate(array $updateConditions = null): self
149
    {
150
        $clone = clone $this;
151
        $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...
152
        return $clone;
153
    }
154
155
    /**
156
     * @param null $end
157
     * @return InsertQueryBuilder
158
     */
159
    public function end($end = null): self
160
    {
161
        $clone = clone $this;
162
        $clone->end = $end;
163
        return $clone;
164
    }
165
166
    /**
167
     * @param array $value
168
     * @return array
169
     * @throws \InvalidArgumentException
170
     */
171
    private function validateValue(array &$value): array
172
    {
173
        if (empty($this->values)) {
174
            return $value;
175
        }
176
        $keys = array_keys($this->values[0]);
177
        $valueKeys = array_keys($value);
178
        if ($valueKeys !== $keys) {
179
            if (count($keys) !== count($valueKeys)) {
180
                throw new \InvalidArgumentException("Invalid value.");
181
            }
182
            uksort($value, function ($key1, $key2) use ($keys) {
183
                return array_search($key1, $keys) <=> array_search($key2, $keys);
184
            });
185
            $valueKeys = array_keys($value);
186
            if ($valueKeys !== $keys) {
187
                throw new \InvalidArgumentException("Invalid value.");
188
            }
189
        }
190
        return $value;
191
    }
192
193
    /**
194
     * @param string $value
195
     * @return string
196
     */
197
    public function escape(string $value): string
198
    {
199
        return null === $this->escape ? $value : $this->escape . $value . $this->escape;
200
    }
201
202
    /**
203
     * @return string
204
     */
205
    public function __toString(): string
206
    {
207
        return InsertQueryStringifier::stringify($this);
208
    }
209
210
    /**
211
     * Split into multiple INSERT statements.
212
     *
213
     * @param int $max
214
     * @return iterable|self[]
215
     */
216
    public function split(int $max): iterable
217
    {
218
        $pos = 0;
219
        $total = count($this->values);
220
        while ($pos < $total) {
221
            $clone = clone $this;
222
            $clone->values = array_slice($this->values, $pos, $max);
223
            $pos += $max;
224
            yield $clone;
225
        }
226
    }
227
228
    /**
229
     * @return array
230
     */
231
    public function getValues(): array
232
    {
233
        $generator = function (array $values, array $columns) {
234
            foreach ($values as $value) {
235
                $valueKeys = array_keys($value);
236
                if ($valueKeys !== $columns) {
237
                    $value = array_intersect_key($value, array_combine($columns, array_fill(0, count($columns), null)));
238
                    $valueKeys = array_keys($value);
239
                    if ($valueKeys !== $columns) {
240
                        uksort($value, function ($key1, $key2) use ($columns) {
241
                            return array_search($key1, $columns) <=> array_search($key2, $columns);
242
                        });
243
                    }
244
                }
245
                yield $value;
246
            }
247
        };
248
        return flatten($generator($this->values, $this->getColumns()))->asArray();
249
    }
250
251
    /**
252
     * @return array
253
     */
254
    public function getColumns()
255
    {
256
        return $this->columns ?? array_keys($this->values[0] ?? []);
257
    }
258
259
    /**
260
     * Read-only properties.
261
     *
262
     * @param $property
263
     * @return mixed
264
     * @throws \InvalidArgumentException
265
     */
266
    public function __get($property)
267
    {
268
        if (!property_exists($this, $property)) {
269
            throw new \InvalidArgumentException(sprintf('Property %s::$%s does not exist.', __CLASS__, $property));
270
        }
271
        return $this->{$property};
272
    }
273
}
274