Skip to content

Commit db05133

Browse files
committed
add nested fields and builder support
1 parent 588380d commit db05133

File tree

1 file changed

+109
-54
lines changed

1 file changed

+109
-54
lines changed

src/Concerns/CanMapDynamicFields.php

Lines changed: 109 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ private function getAllFieldsIncludingBuilderFields(array $builderBlocks): Colle
207207
private function applyFieldFillMutation(Model $field, array $fieldConfig, object $fieldInstance, array $data, array $builderBlocks): array
208208
{
209209
if (! empty($fieldConfig['methods']['mutateFormDataCallback'])) {
210+
$fieldLocation = $this->determineFieldLocation($field, $builderBlocks);
211+
212+
if ($fieldLocation['isInBuilder']) {
213+
return $this->processBuilderFieldFillMutation($field, $fieldInstance, $data, $fieldLocation['builderData'], $builderBlocks);
214+
}
215+
210216
return $fieldInstance->mutateFormDataCallback($this->record, $field, $data);
211217
}
212218

@@ -217,67 +223,28 @@ private function applyFieldFillMutation(Model $field, array $fieldConfig, object
217223
}
218224

219225
/**
220-
* Apply field-specific mutation logic for form saving.
221-
*
222-
* This method handles both regular fields and fields within builder blocks.
223-
* Builder blocks require special processing because they contain nested data structures.
226+
* Extract builder blocks from record values.
224227
*
225-
* @param Model $field The field model
226-
* @param array $fieldConfig The field configuration
227-
* @param object $fieldInstance The field instance
228-
* @param array $data The form data
229-
* @param array $builderBlocks The builder blocks
230-
* @return array The mutated data
228+
* @return array The builder blocks
231229
*/
232-
private function applyFieldSaveMutation(Model $field, array $fieldConfig, object $fieldInstance, array $data, array $builderBlocks): array
230+
private function extractBuilderBlocksFromRecord(): array
233231
{
234-
if (empty($fieldConfig['methods']['mutateBeforeSaveCallback'])) {
235-
return $data;
236-
}
237-
238-
$fieldLocation = $this->determineFieldLocation($field, $builderBlocks);
239-
240-
if ($fieldLocation['isInBuilder']) {
241-
return $this->processBuilderFieldMutation($field, $fieldInstance, $data, $fieldLocation['builderData'], $builderBlocks);
232+
if (!isset($this->record->values) || !is_array($this->record->values)) {
233+
return [];
242234
}
243235

244-
// Regular field processing
245-
return $fieldInstance->mutateBeforeSaveCallback($this->record, $field, $data);
246-
}
247-
248-
/**
249-
* Determine if a field is inside a builder block and extract its data.
250-
*
251-
* @param Model $field The field to check
252-
* @param array $builderBlocks The builder blocks
253-
* @return array Location information with 'isInBuilder' and 'builderData' keys
254-
*/
255-
private function determineFieldLocation(Model $field, array $builderBlocks): array
256-
{
257-
foreach ($builderBlocks as $builderUlid => $builderBlocks) {
258-
if (is_array($builderBlocks)) {
259-
foreach ($builderBlocks as $block) {
260-
if (isset($block['data']) && is_array($block['data']) && isset($block['data'][$field->ulid])) {
261-
return [
262-
'isInBuilder' => true,
263-
'builderData' => $block['data'],
264-
];
265-
}
266-
}
267-
}
268-
}
236+
$builderFieldUlids = ModelsField::whereIn('ulid', array_keys($this->record->values))
237+
->where('field_type', 'builder')
238+
->pluck('ulid')
239+
->toArray();
269240

270-
return [
271-
'isInBuilder' => false,
272-
'builderData' => null,
273-
];
241+
return collect($this->record->values)
242+
->filter(fn ($value, $key) => in_array($key, $builderFieldUlids))
243+
->toArray();
274244
}
275245

276246
/**
277-
* Process mutation for fields inside builder blocks.
278-
*
279-
* Builder fields require special handling because they're nested within
280-
* a complex data structure that needs to be updated in place.
247+
* Process fill mutation for fields inside builder blocks.
281248
*
282249
* @param Model $field The field model
283250
* @param object $fieldInstance The field instance
@@ -286,14 +253,14 @@ private function determineFieldLocation(Model $field, array $builderBlocks): arr
286253
* @param array $builderBlocks All builder blocks
287254
* @return array The updated form data
288255
*/
289-
private function processBuilderFieldMutation(Model $field, object $fieldInstance, array $data, array $builderData, array $builderBlocks): array
256+
private function processBuilderFieldFillMutation(Model $field, object $fieldInstance, array $data, array $builderData, array $builderBlocks): array
290257
{
291258
// Create a mock record with the builder data for the callback
292259
$mockRecord = $this->createMockRecordForBuilder($builderData);
293260

294261
// Create a temporary data structure for the callback
295262
$tempData = [$this->record->valueColumn => $builderData];
296-
$tempData = $fieldInstance->mutateBeforeSaveCallback($mockRecord, $field, $tempData);
263+
$tempData = $fieldInstance->mutateFormDataCallback($mockRecord, $field, $tempData);
297264

298265
// Update the original data structure with the mutated values
299266
$this->updateBuilderBlocksWithMutatedData($builderBlocks, $field, $tempData);
@@ -513,4 +480,92 @@ private function generateInputName(Model $field, mixed $record, bool $isNested):
513480
{
514481
return $isNested ? "{$field->ulid}" : "{$record->valueColumn}.{$field->ulid}";
515482
}
483+
484+
/**
485+
* Apply field-specific mutation logic for form saving.
486+
*
487+
* This method handles both regular fields and fields within builder blocks.
488+
* Builder blocks require special processing because they contain nested data structures.
489+
*
490+
* @param Model $field The field model
491+
* @param array $fieldConfig The field configuration
492+
* @param object $fieldInstance The field instance
493+
* @param array $data The form data
494+
* @param array $builderBlocks The builder blocks
495+
* @return array The mutated data
496+
*/
497+
private function applyFieldSaveMutation(Model $field, array $fieldConfig, object $fieldInstance, array $data, array $builderBlocks): array
498+
{
499+
if (empty($fieldConfig['methods']['mutateBeforeSaveCallback'])) {
500+
return $data;
501+
}
502+
503+
$fieldLocation = $this->determineFieldLocation($field, $builderBlocks);
504+
505+
if ($fieldLocation['isInBuilder']) {
506+
return $this->processBuilderFieldMutation($field, $fieldInstance, $data, $fieldLocation['builderData'], $builderBlocks);
507+
}
508+
509+
// Regular field processing
510+
return $fieldInstance->mutateBeforeSaveCallback($this->record, $field, $data);
511+
}
512+
513+
/**
514+
* Determine if a field is inside a builder block and extract its data.
515+
*
516+
* @param Model $field The field to check
517+
* @param array $builderBlocks The builder blocks
518+
* @return array Location information with 'isInBuilder' and 'builderData' keys
519+
*/
520+
private function determineFieldLocation(Model $field, array $builderBlocks): array
521+
{
522+
foreach ($builderBlocks as $builderUlid => $builderBlocks) {
523+
if (is_array($builderBlocks)) {
524+
foreach ($builderBlocks as $block) {
525+
if (isset($block['data']) && is_array($block['data']) && isset($block['data'][$field->ulid])) {
526+
return [
527+
'isInBuilder' => true,
528+
'builderData' => $block['data'],
529+
];
530+
}
531+
}
532+
}
533+
}
534+
535+
return [
536+
'isInBuilder' => false,
537+
'builderData' => null,
538+
];
539+
}
540+
541+
/**
542+
* Process mutation for fields inside builder blocks.
543+
*
544+
* Builder fields require special handling because they're nested within
545+
* a complex data structure that needs to be updated in place.
546+
*
547+
* @param Model $field The field model
548+
* @param object $fieldInstance The field instance
549+
* @param array $data The form data
550+
* @param array $builderData The builder block data
551+
* @param array $builderBlocks All builder blocks
552+
* @return array The updated form data
553+
*/
554+
private function processBuilderFieldMutation(Model $field, object $fieldInstance, array $data, array $builderData, array $builderBlocks): array
555+
{
556+
// Create a mock record with the builder data for the callback
557+
$mockRecord = $this->createMockRecordForBuilder($builderData);
558+
559+
// Create a temporary data structure for the callback
560+
$tempData = [$this->record->valueColumn => $builderData];
561+
$tempData = $fieldInstance->mutateBeforeSaveCallback($mockRecord, $field, $tempData);
562+
563+
// Update the original data structure with the mutated values
564+
$this->updateBuilderBlocksWithMutatedData($builderBlocks, $field, $tempData);
565+
566+
// Update the main data structure
567+
$data[$this->record->valueColumn] = array_merge($data[$this->record->valueColumn], $builderBlocks);
568+
569+
return $data;
570+
}
516571
}

0 commit comments

Comments
 (0)