15
15
use Illuminate \Database \Connection ;
16
16
use Illuminate \Http \Client \PendingRequest ;
17
17
use Illuminate \Support \Arr ;
18
+ use Illuminate \Support \Collection ;
18
19
use Illuminate \Support \Facades \Cache ;
20
+ use Symfony \Component \HttpFoundation \File \File ;
19
21
use Illuminate \Support \Facades \Http ;
20
22
use Psr \Http \Message \RequestInterface ;
21
23
use Psr \Http \Message \ResponseInterface ;
@@ -104,7 +106,7 @@ protected function getRecordUrl()
104
106
protected function getLayoutUrl ($ layout = null )
105
107
{
106
108
// Set the connection layout as the layout parameter, otherwise get the layout from the connection
107
- if ($ layout ){
109
+ if ($ layout ) {
108
110
$ this ->setLayout ($ layout );
109
111
}
110
112
return $ this ->getDatabaseUrl () . '/layouts/ ' . $ this ->getLayout ();
@@ -155,17 +157,17 @@ public function uploadToContainerField(FMBaseBuilder $query)
155
157
* The array should be in the format:
156
158
* [ $file, 'myFile.pdf' ]
157
159
*/
158
- if (is_array ($ query ->containerFile )){
160
+ if (is_array ($ query ->containerFile )) {
159
161
// we have a file and file name
160
162
$ file = $ query ->containerFile [0 ];
161
163
$ filename = $ query ->containerFile [1 ];
162
- } else {
164
+ } else {
163
165
$ file = $ query ->containerFile ;
164
166
$ filename = $ file ->getFilename ();
165
167
}
166
168
167
169
// create a stream resource
168
- $ stream = fopen ($ file ->getPath (). '/ ' . $ file ->getFilename (), 'r ' );
170
+ $ stream = fopen ($ file ->getPath () . '/ ' . $ file ->getFilename (), 'r ' );
169
171
170
172
$ request = Http::attach ('upload ' , $ stream , $ filename );
171
173
$ response = $ this ->makeRequest ('post ' , $ url , [], $ request );
@@ -313,6 +315,7 @@ public function editRecord(FMBaseBuilder $query)
313
315
return $ response ;
314
316
}
315
317
318
+
316
319
/**
317
320
* Attempt to emulate a sql update in FileMaker
318
321
*
@@ -331,7 +334,26 @@ public function update($query, $bindings = [])
331
334
// This is just a single record to edit
332
335
try {
333
336
// Do the update
334
- $ this ->editRecord ($ query );
337
+
338
+ // Insert container data before updating text fields since scripts can be attached to the regular record edit
339
+
340
+ // Only attempt to write modified container fields
341
+ // Figure out which fields are containers vs non-containers
342
+ $ textDataFields = $ this ->getNonContainerFieldsForRecordWrite ($ query ->fieldData ) ?? [];
343
+ $ modifiedContainerFields = collect ($ query ->fieldData )->diffKeys ($ textDataFields );
344
+ foreach ($ modifiedContainerFields as $ containerField => $ fileData ) {
345
+ $ eachResponse = $ query ->recordId ($ query ->getRecordId ())->setContainer ($ containerField , $ fileData );
346
+ $ query ->modId ($ this ->getModIdFromFmResponse ($ eachResponse ));
347
+ }
348
+
349
+ // only do an edit record if there are non-container fields to edit
350
+ // It's technically valid in the FileMaker Data API to call edit with no fields to modify to create a
351
+ // record, but that doesn't really match Laravel's behavior. Users who want to do this should
352
+ // call edit() directly.
353
+ if ($ textDataFields ->count () > 0 ) {
354
+ $ this ->editRecord ($ query );
355
+ }
356
+
335
357
} catch (FileMakerDataApiException $ e ) {
336
358
// Record is missing is ok for laravel functions
337
359
// Throw if it isn't error code 101, which is missing record
@@ -346,6 +368,11 @@ public function update($query, $bindings = [])
346
368
return 1 ;
347
369
}
348
370
371
+ protected function getModIdFromFmResponse ($ response )
372
+ {
373
+ return $ response ['response ' ]['modId ' ];
374
+ }
375
+
349
376
/**
350
377
* @param FMBaseBuilder $query
351
378
* @return int
@@ -377,7 +404,7 @@ protected function performFindAndUpdateResults(FMBaseBuilder $query)
377
404
$ builder ->layout ($ query ->from );
378
405
try {
379
406
// Do the update
380
- $ this ->editRecord ($ builder );
407
+ $ this ->update ($ builder );
381
408
// Update if we don't get a record missing exception
382
409
$ updatedCount ++;
383
410
} catch (FileMakerDataApiException $ e ) {
@@ -422,7 +449,7 @@ protected function buildPostDataFromQuery(FMBaseBuilder $query)
422
449
// only set field data if it exists
423
450
if ($ query ->fieldData ) {
424
451
// fieldData needs to have a value if it's there, but an empty object is ok instead of null
425
- $ postData ['fieldData ' ] = $ query ->fieldData ?? json_decode ("{} " );
452
+ $ postData ['fieldData ' ] = $ this -> getNonContainerFieldsForRecordWrite ( $ query ->fieldData ) ?? json_decode ("{} " );
426
453
}
427
454
428
455
// attribute => parameter
@@ -475,6 +502,47 @@ protected function buildPostDataFromQuery(FMBaseBuilder $query)
475
502
return $ postData ;
476
503
}
477
504
505
+ /**
506
+ * Strip out containers and read-only fields to prepare for a write query
507
+ * OR - do the opposite and get ONLY containers
508
+ *
509
+ * @return Collection
510
+ */
511
+ protected function getNonContainerFieldsForRecordWrite ($ fieldArray )
512
+ {
513
+
514
+ $ fieldData = collect ($ fieldArray );
515
+
516
+ // Remove any fields which have been set to write a file, as they should be handled as containers
517
+ foreach ($ fieldData as $ key => $ field ) {
518
+ // remove any containers to be written.
519
+ // users can set the field to be a File, UploadFile, or array [$file, 'MyFile.pdf']
520
+ if ($ this ->isContainer ($ field )) {
521
+ $ fieldData ->forget ($ key );
522
+ }
523
+ }
524
+ return $ fieldData ;
525
+ }
526
+
527
+ protected function isContainer ($ field )
528
+ {
529
+
530
+ // if this is a file then we know it's a container
531
+ if (is_a ($ field , File::class)) {
532
+ return true ;
533
+ }
534
+
535
+ // if it's an array, it could be a file => filename key-value pair.
536
+ // it's a conainer if the first object in the array is a file
537
+ if (is_array ($ field ) && sizeof ($ field ) === 2 && $ this ->isFile ($ field [0 ])) {
538
+ return true ;
539
+ }
540
+
541
+ return false ;
542
+ }
543
+
544
+
545
+
478
546
public function executeScript (FMBaseBuilder $ query )
479
547
{
480
548
$ this ->setLayout ($ query ->from );
@@ -643,7 +711,8 @@ protected function getDefaultQueryGrammar()
643
711
return new FMGrammar ();
644
712
}
645
713
646
- public function getLayoutMetadata ($ layout = null ){
714
+ public function getLayoutMetadata ($ layout = null )
715
+ {
647
716
$ response = $ this ->makeRequest ('get ' , $ this ->getLayoutUrl ($ layout ));
648
717
return $ response ['response ' ];
649
718
}
0 commit comments