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
ivyforms / backend / src / Services / Entry / Managers / EntryFieldValueManager.php

EntryFieldValueManager.php in The Innovative Form Builder – IvyForms 1.5, at backend/src/Services/Entry/Managers/EntryFieldValueManager.php

334 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace IvyForms\Services\Entry\Managers;
4
5 // phpcs:disable PSR1.Files.SideEffects
6 if (!defined('ABSPATH')) {
7 exit; // Exit if accessed directly
8 }
9
10 use IvyForms\Common\Helpers\DateTimeHelper\DateHelper;
11 use IvyForms\Common\Helpers\DateTimeHelper\TimeHelper;
12 use IvyForms\Common\Helpers\EntryHelper;
13 use IvyForms\Services\Field\FieldService;
14
15 /**
16 * Handles field value formatting and resolution.
17 * Responsible for converting field values for display and processing submission data.
18 */
19 class EntryFieldValueManager
20 {
21 private FieldService $fieldService;
22
23 public function __construct(FieldService $fieldService)
24 {
25 $this->fieldService = $fieldService;
26 }
27
28 /**
29 * Format entry fields with values converted for frontend display.
30 * (e.g., timestamp → HH:MM for time fields)
31 *
32 * @param array<int, array<string, mixed>> $entryFields
33 * @param bool $skipFormatting
34 * @return array<int, array<string, mixed>>
35 * @SuppressWarnings(PHPMD)
36 */
37 public function formatForFrontend(array $entryFields, bool $skipFormatting = false): array
38 {
39 $fieldsById = $this->buildFieldsMap($entryFields);
40
41 // Format each entry field value based on field type
42 foreach ($entryFields as &$entryField) {
43 $fieldId = (int)$entryField['fieldId'];
44 if (!isset($fieldsById[$fieldId])) {
45 continue;
46 }
47
48 $field = $fieldsById[$fieldId];
49 $fieldType = strtolower($field->getType());
50 $value = $entryField['fieldValue'];
51
52 $entryField['fieldValue'] = $this->formatFieldValue($fieldType, $value, $skipFormatting, $field);
53 }
54
55 return $entryFields;
56 }
57
58 /**
59 * Resolve the value for a field based on its type and parent/child relationship.
60 *
61 * @param object $field
62 * @param array<mixed> $submissionData
63 * @param array<int, array<int>> $parentChildrenMap
64 * @param array<int, string|null> $parentSubfieldKeys
65 * @return mixed
66 */
67 public function resolveFieldValue(
68 object $field,
69 array $submissionData,
70 array $parentChildrenMap,
71 array $parentSubfieldKeys
72 ) {
73 $fieldId = $field->getId();
74 $fieldType = $field->getType();
75 $parentId = method_exists($field, 'getParentId') ? $field->getParentId() : null;
76
77 $parentValue = $this->getParentSubfieldValue($fieldId, $parentId, $submissionData, $parentSubfieldKeys);
78 if ($parentValue !== null) {
79 return $parentValue;
80 }
81
82 /**
83 * Filters the resolved field value before standard processing.
84 * Allows field-type-specific value resolution (e.g., password hashing).
85 *
86 * @since 0.1.0
87 *
88 * @param mixed|null $value The resolved value (null to continue with default processing).
89 * @param object $field The field object.
90 * @param mixed[] $submissionData The submission data array.
91 * @param mixed[] $parentChildrenMap Map of parent to children field relationships.
92 * @param mixed[] $parentSubfieldKeys Map of parent subfield keys.
93 * @return mixed The resolved field value or null to continue default processing.
94 */
95 $customValue = apply_filters(
96 'ivyforms/entry/field/resolve_value',
97 null,
98 $field,
99 $submissionData,
100 $parentChildrenMap,
101 $parentSubfieldKeys
102 );
103
104 /**
105 * Filters the resolved field value for a specific field type.
106 * Allows dynamic hook registration per field type (e.g., 'ivyforms/entry/field/resolve_value/password').
107 *
108 * @since 0.1.0
109 *
110 * @param mixed|null $value The resolved value (null to continue with default processing).
111 * @param object $field The field object.
112 * @param mixed[] $submissionData The submission data array.
113 * @param mixed[] $parentChildrenMap Map of parent to children field relationships.
114 * @param mixed[] $parentSubfieldKeys Map of parent subfield keys.
115 * @return mixed The resolved field value or null to continue default processing.
116 */
117 $customValue = apply_filters(
118 "ivyforms/entry/field/resolve_value/{$fieldType}",
119 $customValue,
120 $field,
121 $submissionData,
122 $parentChildrenMap,
123 $parentSubfieldKeys
124 );
125
126 if ($customValue !== null) {
127 return $customValue;
128 }
129
130 return $this->resolveByType(
131 $fieldType,
132 $fieldId,
133 $parentId,
134 $submissionData,
135 $parentChildrenMap,
136 $parentSubfieldKeys
137 );
138 }
139
140 /**
141 * Resolve value based on field type.
142 *
143 * @param string $fieldType
144 * @param int $fieldId
145 * @param int|null $parentId
146 * @param array<mixed> $submissionData
147 * @param array<int, array<int>> $parentChildrenMap
148 * @param array<int, string|null> $parentSubfieldKeys
149 * @return mixed
150 */
151 private function resolveByType(
152 string $fieldType,
153 int $fieldId,
154 ?int $parentId,
155 array $submissionData,
156 array $parentChildrenMap,
157 array $parentSubfieldKeys
158 ) {
159 $isParent = $parentId === null || $parentId === 0;
160
161 if ($isParent) {
162 /**
163 * Filters the resolved value for a parent field before compound resolution.
164 * Allows Pro field types (e.g. likert) to resolve parent values without
165 * hardcoding field types in Lite.
166 *
167 * @since 0.1.0
168 *
169 * @param mixed|null $value The resolved value (null to continue with default processing).
170 * @param string $fieldType The field type.
171 * @param int $fieldId The parent field ID.
172 * @param mixed[] $parentChildrenMap Map of parent to children field relationships.
173 * @param mixed[] $parentSubfieldKeys Map of parent subfield keys.
174 * @param mixed[] $submissionData The submission data array.
175 * @return mixed The resolved parent field value or null to continue default processing.
176 */
177 $parentValue = apply_filters(
178 'ivyforms/entry/resolve_parent_field_value',
179 null,
180 $fieldType,
181 $fieldId,
182 $parentChildrenMap,
183 $parentSubfieldKeys,
184 $submissionData
185 );
186
187 if ($parentValue !== null) {
188 return $parentValue;
189 }
190 }
191
192 if (in_array($fieldType, ['name', 'address'], true) && $isParent) {
193 return $this->resolveParentFieldValue(
194 $fieldId,
195 $parentChildrenMap,
196 $parentSubfieldKeys,
197 $submissionData
198 );
199 }
200
201 if ($fieldType === 'time') {
202 $value = EntryHelper::getDefaultFieldValue($fieldId, $submissionData);
203 return TimeHelper::normalizeTimeValue($value);
204 }
205
206 if ($fieldType === 'date') {
207 $value = EntryHelper::getDefaultFieldValue($fieldId, $submissionData);
208 return DateHelper::normalizeDateValue($value);
209 }
210
211 return EntryHelper::getDefaultFieldValue($fieldId, $submissionData);
212 }
213
214 /**
215 * Resolve value for a parent field (name, address).
216 *
217 * @param int $fieldId
218 * @param array<int, array<int>> $parentChildrenMap
219 * @param array<int, string|null> $parentSubfieldKeys
220 * @param array<int|string, mixed> $submissionData
221 * @return string
222 */
223 private function resolveParentFieldValue(
224 int $fieldId,
225 array $parentChildrenMap,
226 array $parentSubfieldKeys,
227 array $submissionData
228 ): string {
229 return $this->getCompoundFieldValue($fieldId, $parentChildrenMap, $parentSubfieldKeys, $submissionData);
230 }
231
232 /**
233 * Build a map of field entities indexed by field ID.
234 *
235 * @param array<int, array<string, mixed>> $entryFields
236 * @return array<int, object>
237 */
238 private function buildFieldsMap(array $entryFields): array
239 {
240 // Extract unique field IDs from entry fields
241 $fieldIds = array_unique(array_column($entryFields, 'fieldId'));
242 if (empty($fieldIds)) {
243 return [];
244 }
245
246 // Single query to load only needed fields
247 return $this->fieldService->getFieldsByIds($fieldIds);
248 }
249
250 /**
251 * Format field value based on field type.
252 *
253 * @param string $fieldType
254 * @param mixed $value
255 * @param bool $skipFormatting
256 * @param object|null $field
257 * @return mixed
258 * @SuppressWarnings(PHPMD)
259 */
260 private function formatFieldValue(string $fieldType, $value, bool $skipFormatting = false, ?object $field = null)
261 {
262 // Convert timestamp to HH:MM format for time fields
263 if ($fieldType === 'time' && is_numeric($value)) {
264 $value = TimeHelper::timestampToTime((int)$value);
265 }
266
267 // Convert timestamp to date format for date fields
268 if ($fieldType === 'date' && is_numeric($value)) {
269 $value = DateHelper::timestampToDate((int)$value);
270 }
271
272 /**
273 * Filters the formatted field value for frontend display.
274 * Allows field-type-specific formatting.
275 *
276 * @since 0.1.0
277 *
278 * @param mixed $value The field value to format.
279 * @param string $fieldType The field type (lowercase).
280 * @return mixed The formatted field value.
281 */
282 $value = apply_filters('ivyforms/entry/field/format_value', $value, $fieldType);
283
284 /**
285 * Filters the formatted field value for a specific field type.
286 * Allows dynamic hook registration per field type.
287 *
288 * @since 0.1.0
289 *
290 * @param mixed $value The field value to format.
291 * @param bool $skipFormatting Whether the field value should be formatted.
292 * @param object|null $field The field entity object.
293 * @return mixed The formatted field value.
294 */
295 return apply_filters("ivyforms/entry/field/format_value/{$fieldType}", $value, $skipFormatting, $field);
296 }
297
298 /**
299 * Get the value of a parent subfield.
300 *
301 * @param int $fieldId
302 * @param int|null $parentId
303 * @param array<int|string, mixed> $submissionData
304 * @param array<int, string|null> $parentSubfieldKeys
305 * @return mixed|null
306 */
307 private function getParentSubfieldValue(
308 int $fieldId,
309 ?int $parentId,
310 array $submissionData,
311 array $parentSubfieldKeys
312 ) {
313 return EntryHelper::getParentSubfieldValue($fieldId, $parentId, $submissionData, $parentSubfieldKeys);
314 }
315
316 /**
317 * Get the compound field value by combining child field values.
318 *
319 * @param int $fieldId
320 * @param array<int, array<int>> $parentChildrenMap
321 * @param array<int, string|null> $parentSubfieldKeys
322 * @param array<int|string, mixed> $submissionData
323 * @return string
324 */
325 private function getCompoundFieldValue(
326 int $fieldId,
327 array $parentChildrenMap,
328 array $parentSubfieldKeys,
329 array $submissionData
330 ): string {
331 return EntryHelper::getCompoundFieldValue($fieldId, $parentChildrenMap, $parentSubfieldKeys, $submissionData);
332 }
333 }
334