PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
← All changes | backend/src/Common/Helpers/FieldHelper.php +181 -0 0.6.1 → 1.5 View file →
@@ -1,0 +1,181 @@
1 +<?php
2 +
3 +namespace IvyForms\Common\Helpers;
4 +
5 +use IvyForms\Common\Exceptions\ValidationException;
6 +use IvyForms\Entity\FieldOptions\FieldOptions;
7 +use IvyForms\Factory\FieldOptions\FieldOptionsFactory;
8 +use IvyForms\Repository\FieldOptions\FieldOptionsRepositoryInterface;
9 +use IvyForms\Services\Media\ImageService;
10 +
11 +/**
12 + * General helper utilities related to Field processing.
13 + */
14 +class FieldHelper
15 +{
16 + /**
17 + * Resolve parentId using fieldIndex -> parentId map if needed.
18 + *
19 + * @param array<string,mixed> $data Passed by reference; parentId modified in place.
20 + * @param array<int,int> $parentMap Map of fieldIndex => newly created parent field id.
21 + */
22 + public static function resolveParentId(array &$data, array $parentMap): void
23 + {
24 + if (!array_key_exists('parentId', $data)) {
25 + return;
26 + }
27 +
28 + $parentId = $data['parentId'];
29 +
30 + //parentId is 0 or null → use current field's fieldIndex
31 + if (($parentId === 0 || $parentId === '0' || $parentId === null) && isset($data['fieldIndex'])) {
32 + $parentIndex = $data['fieldIndex'];
33 + if (isset($parentMap[$parentIndex])) {
34 + $data['parentId'] = $parentMap[$parentIndex];
35 + }
36 + return;
37 + }
38 +
39 + //parentId is numeric → use it as parent's fieldIndex
40 + if (is_numeric($parentId)) {
41 + if (isset($parentMap[$parentId])) {
42 + $data['parentId'] = $parentMap[$parentId];
43 + }
44 + }
45 + }
46 +
47 + /**
48 + * Remap parentId for duplicated field using old-to-new field ID map.
49 + *
50 + * @param array<string,mixed> $fieldData Passed by reference; parentId modified in place.
51 + * @param array<int,int> $fieldIdMap Map of old field ID => new field ID.
52 + */
53 + public static function remapParentIdForDuplication(array &$fieldData, array $fieldIdMap): void
54 + {
55 + if ($fieldData['parentId'] !== null && isset($fieldIdMap[$fieldData['parentId']])) {
56 + $fieldData['parentId'] = $fieldIdMap[$fieldData['parentId']];
57 + }
58 + }
59 +
60 + /**
61 + * Partition field options into new, update, and submitted IDs.
62 + *
63 + * @param array<string, mixed> $fieldData
64 + * @param array<int> $existingOptionIds
65 + * @return array{
66 + * newOptions: array<int, FieldOptions>,
67 + * updateOptions: array<int, FieldOptions>,
68 + * submittedOptionIds: array<int, int>
69 + * }
70 + * @throws ValidationException
71 + */
72 + public static function partitionFieldOptions(array $fieldData, array $existingOptionIds): array
73 + {
74 + $newOptions = [];
75 + $updateOptions = [];
76 + $submittedOptionIds = [];
77 + foreach ($fieldData['fieldOptions'] as $optionData) {
78 + $option = FieldOptionsFactory::create($optionData);
79 + if (
80 + isset($optionData['id']) && $optionData['id'] > 0
81 + && in_array($optionData['id'], $existingOptionIds, true)
82 + ) {
83 + $updateOptions[] = $option;
84 + $submittedOptionIds[] = $optionData['id'];
85 + continue;
86 + }
87 + $newOptions[] = $option;
88 + }
89 + return [
90 + 'newOptions' => $newOptions,
91 + 'updateOptions' => $updateOptions,
92 + 'submittedOptionIds' => $submittedOptionIds,
93 + ];
94 + }
95 +
96 + /**
97 + * Check if a field type has options (radio, checkbox, select, multi-select).
98 + *
99 + * @param string $fieldType
100 + * @return bool
101 + */
102 + public static function fieldTypeHasOptions(string $fieldType): bool
103 + {
104 + return in_array($fieldType, ['radio', 'checkbox', 'select', 'multi-select', 'product', 'rating'], true);
105 + }
106 +
107 + /**
108 + * Check if a field type is a security/CAPTCHA field.
109 + *
110 + * Security fields are used for bot/spam protection and should be excluded
111 + * from stored entries, integration payloads, placeholder lists, and any
112 + * other processing that targets user-submitted data.
113 + *
114 + * @param string $fieldType
115 + * @return bool
116 + */
117 + public static function isSecurityField(string $fieldType): bool
118 + {
119 + return in_array($fieldType, ['recaptcha', 'turnstile', 'hcaptcha'], true);
120 + }
121 +
122 + /**
123 + * Duplicate field options from one field to another.
124 + *
125 + * @param int $oldFieldId
126 + * @param int $newFieldId
127 + * @param FieldOptionsRepositoryInterface $repository
128 + * @return void
129 + * @throws ValidationException
130 + */
131 + public static function duplicateFieldOptions(
132 + int $oldFieldId,
133 + int $newFieldId,
134 + FieldOptionsRepositoryInterface $repository
135 + ): void {
136 + $options = $repository->getByFieldId($oldFieldId);
137 + foreach ($options as $option) {
138 + $optionData = $option->toArray();
139 + unset($optionData['id']);
140 + $optionData['fieldId'] = $newFieldId;
141 + $repository->add(FieldOptionsFactory::create($optionData));
142 + }
143 + }
144 +
145 + /**
146 + * Process htmlContent to upload base64 images
147 + *
148 + * @param array<string, mixed> $fieldData Passed by reference; htmlContent modified in place
149 + * @param ImageService $imageService
150 + * @return void
151 + */
152 + public static function processFieldHtmlContent(array &$fieldData, ImageService $imageService): void
153 + {
154 + if (!empty($fieldData['htmlContent'])) {
155 + $processedContent = $imageService->processImagesInContent(
156 + $fieldData['htmlContent'],
157 + 'field'
158 + );
159 + $fieldData['htmlContent'] = $processedContent;
160 + }
161 + }
162 +
163 + /**
164 + * Upload base64 images in rich text default values before form save.
165 + *
166 + * @param array<string, mixed> $fieldData Passed by reference; defaultValue modified in place
167 + * @param ImageService $imageService
168 + * @return void
169 + */
170 + public static function processRichTextDefaultValue(array &$fieldData, ImageService $imageService): void
171 + {
172 + if (($fieldData['type'] ?? '') !== 'rich_text' || empty($fieldData['defaultValue'])) {
173 + return;
174 + }
175 +
176 + $fieldData['defaultValue'] = $imageService->processImagesInContent(
177 + (string) $fieldData['defaultValue'],
178 + 'field'
179 + );
180 + }
181 +}