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/Repository/Field/FieldRepository.php +220 -61 0.4 → 1.5 View file →
@@ -1,18 +1,13 @@
1 1 <?php
2 2
3 3 /**
4 - * @copyright © TMS-Plugins. All rights reserved.
4 + * @copyright © Melograno Venture Studio. All rights reserved.
5 5 * @licence See LICENCE.md for license details.
6 6 */
7 7
8 8 namespace IvyForms\Repository\Field;
9 9
10 -// phpcs:disable PSR1.Files.SideEffects
11 -if (!defined('ABSPATH')) {
12 - exit; // Exit if accessed directly
13 -}
14 -
15 10 use IvyForms\Common\Exceptions\InvalidArgumentException;
16 11 use IvyForms\Common\Exceptions\QueryExecutionException;
17 12 use IvyForms\Entity\Field\Field;
18 13 use IvyForms\Factory\Field\FieldFactory;
@@ -52,38 +47,17 @@
52 47 'required' => (int)$data['required'],
53 48 'defaultValue' => $data['defaultValue'],
54 49 'placeholder' => $data['placeholder'],
55 50 'position' => $data['position'],
51 + 'rowIndex' => $data['rowIndex'] ?? 0,
52 + 'columnIndex' => $data['columnIndex'] ?? 0,
53 + 'width' => $data['width'] ?? 100,
56 54 'parentId' => $data['parentId'] ?? null,
57 - 'settings' => json_encode([
58 - 'hideLabel' => (int)$data['hideLabel'],
59 - 'readOnly' => (int)$data['readOnly'],
60 - 'description' => $data['description'],
61 - 'requiredMessage' => $data['requiredMessage'],
62 - 'cssClasses' => $data['cssClasses'],
63 - 'limitMaxLength' => (int)$data['limitMaxLength'],
64 - 'maxLength' => $data['maxLength'],
65 - 'labelPosition' => $data['labelPosition'],
66 - 'noDuplicates' => (int)$data['noDuplicates'],
67 - 'shuffleOptions' => (int)$data['shuffleOptions'],
68 - 'showValues' => (int)$data['showValues'],
69 - 'enableSearch' => (int)$data['enableSearch'],
70 - 'rows' => (int)$data['rows'],
71 - 'confirmFieldEnabled' => (int)($data['confirmFieldEnabled'] ?? 0),
72 - 'confirmFieldLabel' => $data['confirmFieldLabel'] ?? '',
73 - 'confirmFieldPlaceholder' => $data['confirmFieldPlaceholder'] ?? '',
74 - 'confirmFieldHideLabel' => (int)($data['confirmFieldHideLabel'] ?? 0),
75 - 'phoneFormat' => $data['phoneFormat'],
76 - 'phoneAutoDetect' => $data['phoneAutoDetect'],
77 - 'minValue' => (float)$data['minValue'],
78 - 'maxValue' => (float)$data['maxValue'],
79 - 'step' => (float)$data['step'],
80 - 'numberFormat' => $data['numberFormat'] ?? '',
81 - 'visible' => (int)$data['visible'],
82 - ]),
55 + 'pageId' => $data['pageId'] ?? null,
56 + 'settings' => $this->encodeFieldSettings($data, $entity, false),
83 57 'dateCreated' => current_time('mysql')
84 58 ],
85 - ['%d', '%d', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%s', '%s']
59 + ['%d', '%d', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%s']
86 60 );
87 61
88 62 if ($result === false) {
89 63 throw new QueryExecutionException(
@@ -105,8 +79,11 @@
105 79 * @throws QueryExecutionException
106 80 */
107 81 public function update(int $id, $entity): bool
108 82 {
83 + if ($id < 1) {
84 + throw new InvalidArgumentException('Field id must be a positive integer.');
85 + }
109 86
110 87 $data = $entity->toArray();
111 88
112 89 $result = $this->wpdb->update(
@@ -119,38 +96,17 @@
119 96 'required' => (int)$data['required'],
120 97 'defaultValue' => $data['defaultValue'],
121 98 'placeholder' => $data['placeholder'],
122 99 'position' => $data['position'],
100 + 'rowIndex' => $data['rowIndex'] ?? 0,
101 + 'columnIndex' => $data['columnIndex'] ?? 0,
102 + 'width' => $data['width'] ?? 100,
123 103 'parentId' => $data['parentId'] ?? null,
124 - 'settings' => json_encode([
125 - 'hideLabel' => (int)$data['hideLabel'],
126 - 'readOnly' => (int)$data['readOnly'],
127 - 'description' => $data['description'],
128 - 'requiredMessage' => $data['requiredMessage'],
129 - 'cssClasses' => $data['cssClasses'],
130 - 'limitMaxLength' => (int)$data['limitMaxLength'],
131 - 'maxLength' => $data['maxLength'],
132 - 'labelPosition' => $data['labelPosition'],
133 - 'noDuplicates' => (int)$data['noDuplicates'],
134 - 'shuffleOptions' => (int)$data['shuffleOptions'],
135 - 'showValues' => (int)$data['showValues'],
136 - 'enableSearch' => (int)$data['enableSearch'],
137 - 'rows' => (int)$data['rows'],
138 - 'confirmFieldEnabled' => (int)($data['confirmFieldEnabled'] ?? 0),
139 - 'confirmFieldLabel' => $data['confirmFieldLabel'] ?? '',
140 - 'confirmFieldPlaceholder' => $data['confirmFieldPlaceholder'] ?? '',
141 - 'confirmFieldHideLabel' => (int)($data['confirmFieldHideLabel'] ?? 0),
142 - 'phoneFormat' => $data['phoneFormat'],
143 - 'phoneAutoDetect' => $data['phoneAutoDetect'],
144 - 'minValue' => (float)$data['minValue'],
145 - 'maxValue' => (float)$data['maxValue'],
146 - 'step' => (float)$data['step'],
147 - 'numberFormat' => $data['numberFormat'] ?? '',
148 - 'visible' => array_key_exists('visible', $data) ? (int)$data['visible'] : 1,
149 - ]),
104 + 'pageId' => $data['pageId'] ?? null,
105 + 'settings' => $this->encodeFieldSettings($data, $entity, true),
150 106 ],
151 - ['id' => $data['id']],
152 - ['%d', '%d', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%s'],
107 + ['id' => $id],
108 + ['%d', '%d', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%s', '%s'],
153 109 ['%d']
154 110 );
155 111
156 112 if ($result === false) {
@@ -160,8 +116,170 @@
160 116 return $result;
161 117 }
162 118
163 119 /**
120 + * @param mixed[] $data
121 + */
122 + private function encodeFieldSettings(array $data, Field $entity, bool $forUpdate): string
123 + {
124 + $settings = $this->buildFieldSettingsPayload($data, $forUpdate);
125 +
126 + /**
127 + * Allows modification of the settings data before saving to database.
128 + *
129 + * @param mixed[] $settings The current settings array.
130 + * @param mixed[] $data The full field data array.
131 + * @param Field $entity The Field entity instance.
132 + * @return mixed[] The modified settings array.
133 + */
134 + $settings = apply_filters('ivyforms/repository/field/settings', $settings, $data, $entity);
135 +
136 + $encoded = wp_json_encode($settings);
137 + if ($encoded === false) {
138 + throw new QueryExecutionException(
139 + 'Failed to encode field settings: ' . json_last_error_msg()
140 + );
141 + }
142 +
143 + return $encoded;
144 + }
145 +
146 + /**
147 + * @param mixed[] $data
148 + *
149 + * @return mixed[]
150 + */
151 + private function buildFieldSettingsPayload(array $data, bool $forUpdate): array
152 + {
153 + return array_merge(
154 + $this->buildCoreFieldSettings($data, $forUpdate),
155 + $this->buildFileUploadFieldSettings($data)
156 + );
157 + }
158 +
159 + /**
160 + * @param mixed[] $data
161 + *
162 + * @return mixed[]
163 + */
164 + private function buildCoreFieldSettings(array $data, bool $forUpdate): array
165 + {
166 + return array_merge(
167 + $this->buildBaseFieldSettings($data),
168 + [
169 + 'limitRange' => $this->resolveLimitRangeFlag($data, $forUpdate),
170 + 'visible' => $this->resolveVisibleFlag($data, $forUpdate),
171 + ],
172 + $this->buildExtendedFieldSettings($data)
173 + );
174 + }
175 +
176 + /**
177 + * @param mixed[] $data
178 + *
179 + * @return mixed[]
180 + */
181 + private function buildBaseFieldSettings(array $data): array
182 + {
183 + return [
184 + 'hideLabel' => (int) $data['hideLabel'],
185 + 'readOnly' => (int) $data['readOnly'],
186 + 'description' => $data['description'],
187 + 'requiredMessage' => $data['requiredMessage'],
188 + 'cssClasses' => $data['cssClasses'],
189 + 'limitMaxLength' => (int) $data['limitMaxLength'],
190 + 'maxLength' => $data['maxLength'],
191 + 'labelPosition' => $data['labelPosition'],
192 + 'noDuplicates' => (int) $data['noDuplicates'],
193 + 'shuffleOptions' => (int) $data['shuffleOptions'],
194 + 'showValues' => (int) $data['showValues'],
195 + 'enableSearch' => (int) $data['enableSearch'],
196 + 'rows' => (int) $data['rows'],
197 + ];
198 + }
199 +
200 + /**
201 + * @param mixed[] $data
202 + *
203 + * @return mixed[]
204 + */
205 + private function buildExtendedFieldSettings(array $data): array
206 + {
207 + return [
208 + 'confirmFieldEnabled' => (int) ($data['confirmFieldEnabled'] ?? 0),
209 + 'confirmFieldLabel' => $data['confirmFieldLabel'] ?? '',
210 + 'confirmFieldPlaceholder' => $data['confirmFieldPlaceholder'] ?? '',
211 + 'confirmFieldHideLabel' => (int) ($data['confirmFieldHideLabel'] ?? 0),
212 + 'phoneFormat' => $data['phoneFormat'],
213 + 'phoneAutoDetect' => $data['phoneAutoDetect'],
214 + 'minValue' => isset($data['minValue']) ? (float) $data['minValue'] : null,
215 + 'maxValue' => isset($data['maxValue']) ? (float) $data['maxValue'] : null,
216 + 'step' => (float) $data['step'],
217 + 'showGrid' => (int) ($data['showGrid'] ?? 0),
218 + 'numberFormat' => $data['numberFormat'] ?? '',
219 + 'inputPrefix' => $data['inputPrefix'] ?? '',
220 + 'inputSuffix' => $data['inputSuffix'] ?? '',
221 + 'timeFieldType' => $data['timeFieldType'] ?? '',
222 + 'timeFormat' => $data['timeFormat'] ?? '',
223 + 'dateFieldType' => $data['dateFieldType'] ?? '',
224 + 'dateFormat' => $data['dateFormat'] ?? '',
225 + 'minDateValue' => $data['minDateValue'] ?? '',
226 + 'maxDateValue' => $data['maxDateValue'] ?? '',
227 + 'showRatingText' => (int) ($data['showRatingText'] ?? 0),
228 + 'ratingIcon' => $data['ratingIcon'] ?? 'star',
229 + 'htmlContent' => $data['htmlContent'] ?? '',
230 + 'agreementText' => $data['agreementText'] ?? '',
231 + 'limitDateRange' => (int) $data['limitDateRange'],
232 + 'minTimeValue' => $data['minTimeValue'] ?? '',
233 + 'maxTimeValue' => $data['maxTimeValue'] ?? '',
234 + 'limitTimeRange' => (int) $data['limitTimeRange'],
235 + 'nameFieldType' => $data['nameFieldType'] ?? '',
236 + 'subFieldIndex' => isset($data['subFieldIndex']) ? (int) $data['subFieldIndex'] : null,
237 + ];
238 + }
239 +
240 + /**
241 + * @param mixed[] $data
242 + */
243 + private function resolveLimitRangeFlag(array $data, bool $forUpdate): int
244 + {
245 + if (!$forUpdate) {
246 + return (int) $data['limitRange'];
247 + }
248 +
249 + return filter_var($data['limitRange'], FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
250 + }
251 +
252 + /**
253 + * @param mixed[] $data
254 + */
255 + private function resolveVisibleFlag(array $data, bool $forUpdate): int
256 + {
257 + if (!$forUpdate) {
258 + return (int) $data['visible'];
259 + }
260 +
261 + return array_key_exists('visible', $data) ? (int) $data['visible'] : 1;
262 + }
263 +
264 + /**
265 + * @param mixed[] $data
266 + *
267 + * @return mixed[]
268 + */
269 + private function buildFileUploadFieldSettings(array $data): array
270 + {
271 + return [
272 + 'maxFileSizeMb' => isset($data['maxFileSizeMb']) ? (int) $data['maxFileSizeMb'] : 0,
273 + 'saveUploadsTo' => (string) ($data['saveUploadsTo'] ?? 'ivyforms'),
274 + 'allowedFileExtensions' => isset($data['allowedFileExtensions'])
275 + && is_array($data['allowedFileExtensions'])
276 + ? array_values($data['allowedFileExtensions'])
277 + : [],
278 + ];
279 + }
280 +
281 + /**
164 282 * Get fields by form ID
165 283 *
166 284 * @param int $id
167 285 *
@@ -188,8 +306,49 @@
188 306 $row['parentId'] = $row['parentId'] !== null ? (int)$row['parentId'] : null;
189 307 }
190 308 $result[] = call_user_func([static::FACTORY, 'create'], $row);
191 309 }
310 + return $result;
311 + }
312 +
313 + /**
314 + * Get multiple fields by their IDs in a single query.
315 + *
316 + * @param int[] $fieldIds
317 + * @return array<int, object> Indexed by field ID
318 + * @throws QueryExecutionException
319 + */
320 + public function getFieldsByIds(array $fieldIds): array
321 + {
322 + if (empty($fieldIds)) {
323 + return [];
324 + }
325 +
326 + $fieldIds = array_map('intval', $fieldIds);
327 + $placeholders = implode(',', array_fill(0, count($fieldIds), '%d'));
328 + $rows = $this->wpdb->get_results(
329 + $this->wpdb->prepare(
330 + $this->selectQuery() . " WHERE $this->table.id IN ($placeholders) ORDER BY $this->table.position ASC",
331 + ...$fieldIds
332 + ),
333 + ARRAY_A
334 + );
335 +
336 + if ($rows === false) {
337 + throw new QueryExecutionException(
338 + BackendStrings::getExceptionStrings()['unable_find_by_id'] . __CLASS__
339 + );
340 + }
341 +
342 + $result = [];
343 + foreach ($rows as $row) {
344 + if (array_key_exists('parentId', $row)) {
345 + $row['parentId'] = $row['parentId'] !== null ? (int)$row['parentId'] : null;
346 + }
347 + $field = call_user_func([static::FACTORY, 'create'], $row);
348 + $result[$field->getId()] = $field;
349 + }
350 +
192 351 return $result;
193 352 }
194 353
195 354 /**