@@ -37,13 +37,14 @@ public function runBatch($operation, $objectType, $data, $options = [])
37
37
$ batches = [];
38
38
39
39
$ defaults = [
40
- 'externalIdFieldName ' => null ,
41
- 'batchSize ' => 2000 ,
42
- 'batchTimeout ' => 600 ,
43
- 'contentType ' => 'JSON ' ,
44
- 'pollIntervalSeconds ' => 5 ,
45
- 'isBatchedResult ' => false ,
46
- 'concurrencyMode ' => 'Parallel ' ,
40
+ 'externalIdFieldName ' => null ,
41
+ 'batchSize ' => 2000 ,
42
+ 'batchTimeout ' => 600 ,
43
+ 'contentType ' => 'JSON ' ,
44
+ 'pollIntervalSeconds ' => 5 ,
45
+ 'isBatchedResult ' => false ,
46
+ 'concurrencyMode ' => 'Parallel ' ,
47
+ 'Sforce-Enable-PKChunking ' => false ,
47
48
];
48
49
49
50
$ options = array_replace ($ defaults , $ options );
@@ -52,7 +53,7 @@ public function runBatch($operation, $objectType, $data, $options = [])
52
53
$ options ['isBatchedResult ' ] = true ;
53
54
}
54
55
55
- $ job = $ this ->createJob ($ operation , $ objectType , $ options ['externalIdFieldName ' ], $ options ['contentType ' ], $ options ['concurrencyMode ' ]);
56
+ $ job = $ this ->createJob ($ operation , $ objectType , $ options ['externalIdFieldName ' ], $ options ['contentType ' ], $ options ['concurrencyMode ' ], $ options );
56
57
57
58
if ($ job ->id ) {
58
59
//if data is array, we can split it into batches
@@ -62,7 +63,7 @@ public function runBatch($operation, $objectType, $data, $options = [])
62
63
for ($ i = 1 ; $ i <= $ totalNumberOfBatches ; $ i ++) {
63
64
$ batches [] = $ this ->addBatch ($ job ->id , array_splice ($ data , ($ i - 1 ) * $ options ['batchSize ' ], $ options ['batchSize ' ]));
64
65
}
65
- } else { //probably a string query so run in onee batch
66
+ } else { //probably a string query so run in one batch
66
67
$ batches [] = $ this ->addBatch ($ job ->id , $ data );
67
68
}
68
69
} else {
@@ -72,6 +73,10 @@ public function runBatch($operation, $objectType, $data, $options = [])
72
73
$ time = time ();
73
74
$ timeout = $ time + $ options ['batchTimeout ' ];
74
75
76
+ if ($ options ['Sforce-Enable-PKChunking ' ]){
77
+ $ batches = $ this ->allBatchDetails ($ job ->id , $ options ['contentType ' ]);
78
+ }
79
+
75
80
$ batches_finished = [];
76
81
77
82
while (count ($ batches_finished ) < count ($ batches ) && $ time < $ timeout ) {
@@ -82,10 +87,13 @@ public function runBatch($operation, $objectType, $data, $options = [])
82
87
continue ;
83
88
}
84
89
85
- $ batch = $ this ->batchDetails ($ job ->id , $ batch ->id );
86
- if (in_array ($ batch ->state , ['Completed ' , 'Failed ' , 'Not Processed ' ])) {
87
- $ batchResult = $ this ->batchResult ($ job ->id , $ batch ->id , $ options ['isBatchedResult ' ]);
88
- $ batch ->records = $ batchResult ->records ;
90
+ $ batch = $ this ->batchDetails ($ job ->id , $ batch ->id , $ options ['contentType ' ]);
91
+ if (in_array ($ batch ->state , ['Completed ' , 'Failed ' , 'Not Processed ' , 'NotProcessed ' ])) {
92
+
93
+ if (in_array ($ batch ->state , ['Completed ' ])) {
94
+ $ batchResult = $ this ->batchResult ($ job ->id , $ batch ->id , $ options ['isBatchedResult ' ], null , $ options ['contentType ' ]);
95
+ $ batch ->records = $ batchResult ->records ;
96
+ }
89
97
$ batches_finished [] = $ batch ->id ;
90
98
}
91
99
}
@@ -118,7 +126,7 @@ public function runBatch($operation, $objectType, $data, $options = [])
118
126
*
119
127
* @return BulkJobResponse
120
128
*/
121
- public function createJob ($ operation , $ objectType , $ externalIdFieldName = null , $ contentType = 'JSON ' , $ concurrencyMode = 'Parallel ' )
129
+ public function createJob ($ operation , $ objectType , $ externalIdFieldName = null , $ contentType = 'JSON ' , $ concurrencyMode = 'Parallel ' , $ options =[] )
122
130
{
123
131
$ url = '/services/async/ ' .SalesforceConfig::get ('salesforce.api.version ' ).'/job ' ;
124
132
@@ -128,6 +136,12 @@ public function createJob($operation, $objectType, $externalIdFieldName = null,
128
136
'concurrencyMode ' => $ concurrencyMode ,
129
137
];
130
138
139
+ $ headers = [];
140
+
141
+ if (isset ($ options ['Sforce-Enable-PKChunking ' ]) && $ options ['Sforce-Enable-PKChunking ' ]){
142
+ $ headers ['Sforce-Enable-PKChunking ' ] = $ this ->parsePkChunkingHeader ($ options ['Sforce-Enable-PKChunking ' ]);
143
+ }
144
+
131
145
//order of variables matters so this externalIdFieldName has to come before contentType
132
146
if ($ operation == 'upsert ' ) {
133
147
$ json_array ['externalIdFieldName ' ] = $ externalIdFieldName ;
@@ -136,7 +150,8 @@ public function createJob($operation, $objectType, $externalIdFieldName = null,
136
150
$ json_array ['contentType ' ] = $ contentType ;
137
151
138
152
$ result = $ this ->call_api ('post ' , $ url , [
139
- 'json ' => $ json_array ,
153
+ 'json ' => $ json_array ,
154
+ 'headers ' => $ headers ,
140
155
]);
141
156
142
157
if ($ result && is_array ($ result )) {
@@ -146,11 +161,14 @@ public function createJob($operation, $objectType, $externalIdFieldName = null,
146
161
return new BulkJobResponse ();
147
162
}
148
163
149
- public function jobDetails ($ jobId )
164
+ public function jobDetails ($ jobId, $ format = ' json ' )
150
165
{
151
166
$ url = '/services/async/ ' .SalesforceConfig::get ('salesforce.api.version ' ).'/job/ ' .$ jobId ;
152
167
153
- $ result = $ this ->call_api ('get ' , $ url );
168
+ $ result = $ this ->call_api ('get ' , $ url ,
169
+ [
170
+ 'format ' => $ this ->batchResponseFormatFromContentType ($ format ),
171
+ ]);
154
172
155
173
if ($ result && is_array ($ result )) {
156
174
return new BulkJobResponse ($ result );
@@ -191,7 +209,7 @@ public function closeJob($jobId)
191
209
*
192
210
* @return BulkBatchResponse
193
211
*/
194
- public function addBatch ($ jobId , $ data )
212
+ public function addBatch ($ jobId , $ data, $ format = ' json ' )
195
213
{
196
214
if (!$ jobId ) {
197
215
//throw exception
@@ -214,6 +232,7 @@ public function addBatch($jobId, $data)
214
232
$ result = $ this ->call_api ('post ' , $ url , [
215
233
'body ' => $ body ,
216
234
'headers ' => $ headers ,
235
+ 'format ' => $ this ->batchResponseFormatFromContentType ($ format ),
217
236
]);
218
237
219
238
if ($ result && is_array ($ result )) {
@@ -226,6 +245,7 @@ public function addBatch($jobId, $data)
226
245
/**
227
246
* @param $jobId
228
247
* @param $batchId
248
+ * @param $format
229
249
*
230
250
* @return BulkBatchResponse
231
251
*/
@@ -234,7 +254,7 @@ public function batchDetails($jobId, $batchId, $format = 'json')
234
254
$ url = '/services/async/ ' .SalesforceConfig::get ('salesforce.api.version ' ).'/job/ ' .$ jobId .'/batch/ ' .$ batchId ;
235
255
236
256
$ result = $ this ->call_api ('get ' , $ url , [
237
- 'format ' => $ format ,
257
+ 'format ' => $ this -> batchResponseFormatFromContentType ( $ format) ,
238
258
]);
239
259
240
260
if ($ result && is_array ($ result )) {
@@ -246,6 +266,37 @@ public function batchDetails($jobId, $batchId, $format = 'json')
246
266
return new BulkBatchResponse ();
247
267
}
248
268
269
+ /**
270
+ * @param $jobId
271
+ * @param $format
272
+ *
273
+ * @return BulkBatchResponse[]
274
+ */
275
+ public function allBatchDetails ($ jobId , $ format = 'json ' )
276
+ {
277
+ $ batches = [];
278
+
279
+ //TODO: Fix hack to give initial Salesforce batch time to split into many batches by PK
280
+ sleep (10 );
281
+ ////////////////////////////////////////////////////////////////////////////////////////
282
+
283
+ $ url = '/services/async/ ' .SalesforceConfig::get ('salesforce.api.version ' ).'/job/ ' .$ jobId .'/batch ' ;
284
+
285
+ $ result = $ this ->call_api ('get ' , $ url , [
286
+ 'format ' => $ this ->batchResponseFormatFromContentType ($ format ),
287
+ ]);
288
+
289
+ if ($ result && is_array ($ result ) && isset ($ result ['batchInfo ' ]) && !isset ($ result ['batchInfo ' ]['id ' ])) {
290
+ foreach ($ result ['batchInfo ' ] as $ batch ) {
291
+ $ batches [] = new BulkBatchResponse ($ batch );
292
+ }
293
+ } else {
294
+ //throw exception
295
+ }
296
+
297
+ return $ batches ;
298
+ }
299
+
249
300
/**
250
301
* @param $jobId
251
302
* @param $batchId
@@ -261,14 +312,17 @@ public function batchResult($jobId, $batchId, $isBatchedResult = false, $resultI
261
312
262
313
$ url = '/services/async/ ' .SalesforceConfig::get ('salesforce.api.version ' ).'/job/ ' .$ jobId .'/batch/ ' .$ batchId .'/result ' ;
263
314
315
+ $ resultPostArray = [];
316
+
264
317
//if this is a query result, the main result page will have an array of result ids to follow for hte query results
265
318
if ($ resultId ) {
266
319
$ url = $ url .'/ ' .$ resultId ;
320
+ $ resultPostArray ['format ' ] = $ format ;
321
+ }else {
322
+ $ resultPostArray ['format ' ] = $ this ->batchResponseFormatFromContentType ($ format );
267
323
}
268
324
269
- $ result = $ this ->call_api ('get ' , $ url , [
270
- 'format ' => $ format ,
271
- ]);
325
+ $ result = $ this ->call_api ('get ' , $ url , $ resultPostArray );
272
326
273
327
if ($ result && is_array ($ result )) {
274
328
@@ -277,6 +331,13 @@ public function batchResult($jobId, $batchId, $isBatchedResult = false, $resultI
277
331
$ result ['records ' ] = [];
278
332
}
279
333
334
+ if (isset ($ result ['result ' ])){
335
+ if (!is_array ($ result ['result ' ])){
336
+ $ result ['result ' ] = [$ result ['result ' ]];
337
+ }
338
+ $ result = array_merge ($ result , $ result ['result ' ]);
339
+ }
340
+
280
341
//maximum amount of batch records allowed is 10,000
281
342
for ($ i = 0 ; $ i < 10000 ; $ i ++) {
282
343
//skip processing for the rest of the records if they don't exist
@@ -286,7 +347,7 @@ public function batchResult($jobId, $batchId, $isBatchedResult = false, $resultI
286
347
287
348
//batched results return a list of result ids that need to be processed to get the actual data
288
349
if ($ isBatchedResult ) {
289
- $ batchResult = $ this ->batchResult ($ jobId , $ batchId , false , $ result [$ i ]);
350
+ $ batchResult = $ this ->batchResult ($ jobId , $ batchId , false , $ result [$ i ], $ format );
290
351
$ result ['records ' ] = array_merge ($ result ['records ' ], $ batchResult ->records );
291
352
} else {
292
353
//fix boolean values from appearing as
@@ -437,20 +498,15 @@ public function addBinaryBatch($jobId, BinaryBatch $binaryBatch, $contentType =
437
498
return new BulkBatchResponse ();
438
499
}
439
500
440
- /* public function binaryBatchResult($jobId, $batchId, $isBatchedResult = false, $resultId = null, $format='json')
441
- {
442
- $result = $this->batchResult($jobId, $batchId, $isBatchedResult, $resultId, $format);
443
-
444
- if($result->state)
445
- }
446
- */
447
501
protected function batchResponseFormatFromContentType ($ contentType )
448
502
{
449
503
switch (strtoupper ($ contentType )) {
450
504
case 'ZIP_CSV ' :
451
505
case 'ZIP/CSV ' :
452
506
case 'ZIP_XML ' :
453
507
case 'ZIP/XML ' :
508
+ case 'CSV ' :
509
+ case 'XML ' :
454
510
$ return = 'xml ' ;
455
511
break ;
456
512
default :
@@ -460,4 +516,19 @@ protected function batchResponseFormatFromContentType($contentType)
460
516
461
517
return $ return ;
462
518
}
519
+
520
+ protected function parsePkChunkingHeader ($ pk_chunk_header )
521
+ {
522
+ if (is_array ($ pk_chunk_header )){
523
+ $ header_parts = [];
524
+ foreach ($ pk_chunk_header as $ key =>$ value ) {
525
+ $ header_parts [] = $ key .'= ' .$ value ;
526
+ }
527
+
528
+ return implode ('; ' ,$ header_parts );
529
+ }elseif (in_array ($ pk_chunk_header , [true ,'true ' ,'TRUE ' ])){
530
+ return 'TRUE ' ;
531
+ }
532
+ return 'FALSE ' ;
533
+ }
463
534
}
0 commit comments