5
5
namespace Yiisoft \Db \QueryBuilder ;
6
6
7
7
use JsonException ;
8
+ use Traversable ;
8
9
use Yiisoft \Db \Constraint \Constraint ;
9
10
use Yiisoft \Db \Exception \Exception ;
10
11
use Yiisoft \Db \Exception \InvalidArgumentException ;
20
21
use function array_diff ;
21
22
use function array_fill_keys ;
22
23
use function array_filter ;
24
+ use function array_key_exists ;
23
25
use function array_keys ;
24
26
use function array_map ;
25
27
use function array_merge ;
26
28
use function array_unique ;
27
29
use function array_values ;
30
+ use function count ;
31
+ use function get_object_vars ;
28
32
use function implode ;
29
33
use function in_array ;
34
+ use function is_array ;
35
+ use function is_object ;
30
36
use function is_string ;
37
+ use function iterator_to_array ;
31
38
use function json_encode ;
32
39
use function preg_match ;
40
+ use function reset ;
33
41
use function sort ;
34
42
35
43
/**
@@ -55,47 +63,22 @@ public function batchInsert(string $table, array $columns, iterable $rows, array
55
63
return '' ;
56
64
}
57
65
58
- $ values = [];
59
- $ columns = $ this ->getNormalizeColumnNames ('' , $ columns );
60
- $ columnNames = array_values ($ columns );
61
- $ columnKeys = array_fill_keys ($ columnNames , false );
62
- $ columnSchemas = $ this ->schema ->getTableSchema ($ table )?->getColumns() ?? [];
63
-
64
- foreach ($ rows as $ row ) {
65
- $ i = 0 ;
66
- $ placeholders = $ columnKeys ;
67
-
68
- foreach ($ row as $ key => $ value ) {
69
- /** @psalm-suppress MixedArrayTypeCoercion */
70
- $ columnName = $ columns [$ key ] ?? (isset ($ columnKeys [$ key ]) ? $ key : $ columnNames [$ i ] ?? $ i );
71
- /** @psalm-suppress MixedArrayTypeCoercion */
72
- if (isset ($ columnSchemas [$ columnName ])) {
73
- $ value = $ columnSchemas [$ columnName ]->dbTypecast ($ value );
74
- }
75
-
76
- if ($ value instanceof ExpressionInterface) {
77
- $ placeholders [$ columnName ] = $ this ->queryBuilder ->buildExpression ($ value , $ params );
78
- } else {
79
- $ placeholders [$ columnName ] = $ this ->queryBuilder ->bindParam ($ value , $ params );
80
- }
81
-
82
- ++$ i ;
83
- }
84
-
85
- $ values [] = '( ' . implode (', ' , $ placeholders ) . ') ' ;
86
- }
66
+ $ columns = $ this ->extractColumnNames ($ rows , $ columns );
67
+ $ values = $ this ->prepareBatchInsertValues ($ table , $ rows , $ columns , $ params );
87
68
88
69
if (empty ($ values )) {
89
70
return '' ;
90
71
}
91
72
92
- $ columnNames = array_map (
93
- [$ this ->quoter , 'quoteColumnName ' ],
94
- $ columnNames ,
95
- );
73
+ $ query = 'INSERT INTO ' . $ this ->quoter ->quoteTableName ($ table );
96
74
97
- return 'INSERT INTO ' . $ this ->quoter ->quoteTableName ($ table )
98
- . ' ( ' . implode (', ' , $ columnNames ) . ') VALUES ' . implode (', ' , $ values );
75
+ if (count ($ columns ) > 0 ) {
76
+ $ quotedColumnNames = array_map ([$ this ->quoter , 'quoteColumnName ' ], $ columns );
77
+
78
+ $ query .= ' ( ' . implode (', ' , $ quotedColumnNames ) . ') ' ;
79
+ }
80
+
81
+ return $ query . ' VALUES ' . implode (', ' , $ values );
99
82
}
100
83
101
84
public function delete (string $ table , array |string $ condition , array &$ params ): string
@@ -144,6 +127,86 @@ public function upsert(
144
127
throw new NotSupportedException (__METHOD__ . ' is not supported by this DBMS. ' );
145
128
}
146
129
130
+ /**
131
+ * Prepare values for batch insert.
132
+ *
133
+ * @param string $table The table name.
134
+ * @param iterable $rows The rows to be batch inserted into the table.
135
+ * @param string[] $columns The column names.
136
+ * @param array $params The binding parameters that will be generated by this method.
137
+ *
138
+ * @return string[] The values.
139
+ */
140
+ protected function prepareBatchInsertValues (string $ table , iterable $ rows , array $ columns , array &$ params ): array
141
+ {
142
+ $ values = [];
143
+ /** @var string[] $columnNames */
144
+ $ columnNames = array_values ($ columns );
145
+ $ columnKeys = array_fill_keys ($ columnNames , false );
146
+ $ columnSchemas = $ this ->schema ->getTableSchema ($ table )?->getColumns() ?? [];
147
+
148
+ foreach ($ rows as $ row ) {
149
+ $ i = 0 ;
150
+ $ placeholders = $ columnKeys ;
151
+
152
+ /** @var int|string $key */
153
+ foreach ($ row as $ key => $ value ) {
154
+ $ columnName = $ columns [$ key ] ?? (isset ($ columnKeys [$ key ]) ? $ key : $ columnNames [$ i ] ?? $ i );
155
+
156
+ if (isset ($ columnSchemas [$ columnName ])) {
157
+ $ value = $ columnSchemas [$ columnName ]->dbTypecast ($ value );
158
+ }
159
+
160
+ if ($ value instanceof ExpressionInterface) {
161
+ $ placeholders [$ columnName ] = $ this ->queryBuilder ->buildExpression ($ value , $ params );
162
+ } else {
163
+ $ placeholders [$ columnName ] = $ this ->queryBuilder ->bindParam ($ value , $ params );
164
+ }
165
+
166
+ ++$ i ;
167
+ }
168
+
169
+ $ values [] = '( ' . implode (', ' , $ placeholders ) . ') ' ;
170
+ }
171
+
172
+ return $ values ;
173
+ }
174
+
175
+ /**
176
+ * Extract column names from columns and rows.
177
+ *
178
+ * @param string $table The column schemas.
179
+ * @param iterable $rows The rows to be batch inserted into the table.
180
+ * @param string[] $columns The column names.
181
+ *
182
+ * @return string[] The column names.
183
+ */
184
+ protected function extractColumnNames (iterable $ rows , array $ columns ): array
185
+ {
186
+ $ columns = $ this ->getNormalizeColumnNames ('' , $ columns );
187
+
188
+ if ($ columns !== [] || !is_array ($ rows )) {
189
+ return $ columns ;
190
+ }
191
+
192
+ $ row = reset ($ rows );
193
+ $ row = match (true ) {
194
+ is_array ($ row ) => $ row ,
195
+ $ row instanceof Traversable => iterator_to_array ($ row ),
196
+ is_object ($ row ) => get_object_vars ($ row ),
197
+ default => [],
198
+ };
199
+
200
+ if (array_key_exists (0 , $ row )) {
201
+ return [];
202
+ }
203
+
204
+ /** @var string[] $columnNames */
205
+ $ columnNames = array_keys ($ row );
206
+
207
+ return array_combine ($ columnNames , $ columnNames );
208
+ }
209
+
147
210
/**
148
211
* Prepare select-subQuery and field names for `INSERT INTO ... SELECT` SQL statement.
149
212
*
0 commit comments