feat(form-builder): FormFieldBindingService + library-to-field row copy + snapshot writer

WS-5a commit 2 of 4.

FormFieldBindingService owns all writes to the relational binding table.
Validation against config/form_binding.php entity-column registry lives here
(ARCH §6.2).

FormFieldService::insertFromLibrary now calls copyBindings instead of
hydrating JSON — the Q3 row-copy mandate. Library and field bindings share
the same table; insertion is a row-clone operation.

Snapshot writer (FormSubmissionService::buildSnapshot) serialises bindings
via toJsonShape so schema_snapshot JSON keeps its ARCH §4.6.1 / §6.3
contract. No snapshot format change.
API resources source binding output from the relational table via the same
serialiser — external shape preserved.

Tests: service transactional behaviour, copyBindings preservation,
snapshot parity, API resource parity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 18:48:47 +02:00
parent af8a9da038
commit 6933e6d700
9 changed files with 712 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ namespace App\Http\Resources\FormBuilder;
use App\Enums\FormBuilder\FormFieldType;
use App\Models\FormBuilder\FormField;
use App\Models\PersonTag;
use App\Services\FormBuilder\FormFieldBindingService;
use App\Services\FormBuilder\FormLocaleResolver;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -49,7 +50,9 @@ final class FormFieldResource extends JsonResource
'is_unique' => (bool) $this->is_unique,
'is_pii' => (bool) $this->is_pii,
'display_width' => $this->display_width instanceof \BackedEnum ? $this->display_width->value : $this->display_width,
'binding' => $this->binding,
'binding' => app(FormFieldBindingService::class)->toJsonShape(
$this->resource->bindings->first(),
),
'conditional_logic' => $this->conditional_logic,
'role_restrictions' => $this->role_restrictions,
'translations' => $this->translations,