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/Services/Field/FieldService.php +22 -70 0.8 → 1.5 View file →
@@ -1,13 +1,8 @@
1 1 <?php
2 2
3 3 namespace IvyForms\Services\Field;
4 4
5 -// phpcs:disable PSR1.Files.SideEffects
6 -if (!defined('ABSPATH')) {
7 - exit; // Exit if accessed directly
8 -}
9 -
10 5 use IvyForms\Common\Exceptions\QueryExecutionException;
11 6 use IvyForms\Common\Exceptions\ValidationException;
12 7 use IvyForms\Entity\Field\Field;
13 8 use IvyForms\Common\Exceptions\InvalidArgumentException;
@@ -14,11 +9,11 @@
14 9 use IvyForms\Factory\Field\FieldFactory;
15 10 use IvyForms\Factory\FieldOptions\FieldOptionsFactory;
16 11 use IvyForms\Repository\Field\FieldRepositoryInterface;
17 12 use IvyForms\Repository\FieldOptions\FieldOptionsRepositoryInterface;
18 -use IvyForms\Repository\EntryField\EntryFieldRepositoryInterface;
19 13 use IvyForms\Services\Translations\BackendStrings;
20 14 use IvyForms\Common\Helpers\FieldHelper;
15 +use IvyForms\Services\Media\ImageService;
21 16
22 17 /**
23 18 * @SuppressWarnings("TooManyPublicMethods")
24 19 */
@@ -25,19 +20,19 @@
25 20 class FieldService
26 21 {
27 22 private FieldRepositoryInterface $fieldRepository;
28 23 private FieldOptionsRepositoryInterface $fieldOptionsRepository;
29 - private EntryFieldRepositoryInterface $entryFieldRepository;
24 + private ImageService $imageService;
30 25
31 26 // Constructor injection for repositories only via PHP-DI
32 27 public function __construct(
33 28 FieldRepositoryInterface $fieldRepository,
34 29 FieldOptionsRepositoryInterface $fieldOptionsRepository,
35 - EntryFieldRepositoryInterface $entryFieldRepository
30 + ImageService $imageService
36 31 ) {
37 32 $this->fieldRepository = $fieldRepository;
38 33 $this->fieldOptionsRepository = $fieldOptionsRepository;
39 - $this->entryFieldRepository = $entryFieldRepository;
34 + $this->imageService = $imageService;
40 35 }
41 36
42 37 /**
43 38 * Create a new field.
@@ -165,13 +160,21 @@
165 160 */
166 161 public function duplicateFields(int $originalFormId, int $newFormId): void
167 162 {
168 163 $fields = $this->getAllFields($originalFormId);
164 + $fieldIdMap = [];
169 165 foreach ($fields as $field) {
170 166 $fieldData = $field->toArray();
167 + $oldId = $fieldData['id'];
171 168 unset($fieldData['id']);
172 169 $fieldData['formId'] = $newFormId;
173 - $this->addField(FieldFactory::create($fieldData));
170 + FieldHelper::remapParentIdForDuplication($fieldData, $fieldIdMap);
171 + $newFieldId = $this->addField(FieldFactory::create($fieldData));
172 + $fieldIdMap[$oldId] = $newFieldId;
173 + // Duplicate field options using helper
174 + if (FieldHelper::fieldTypeHasOptions($field->getType())) {
175 + FieldHelper::duplicateFieldOptions($oldId, $newFieldId, $this->fieldOptionsRepository);
176 + }
174 177 }
175 178 }
176 179
177 180 /**
@@ -187,8 +190,11 @@
187 190 foreach ($fields as $fieldData) {
188 191 $fieldData['formId'] = $fieldData['formId'] ?? $formId;
189 192 // Resolve parentId using fieldIndex if available
190 193 FieldHelper::resolveParentId($fieldData, $parentMap);
194 + // Process htmlContent / rich text default values to upload base64 images
195 + FieldHelper::processFieldHtmlContent($fieldData, $this->imageService);
196 + FieldHelper::processRichTextDefaultValue($fieldData, $this->imageService);
191 197 $field = FieldFactory::create($fieldData);
192 198 $fieldId = $this->addField($field);
193 199 // If this is a parent field (no parentId), store its DB id in the map
194 200 if ($field->getParentId() === null) {
@@ -193,15 +199,9 @@
193 199 // If this is a parent field (no parentId), store its DB id in the map
194 200 if ($field->getParentId() === null) {
195 201 $parentMap[$field->getIndex()] = $fieldId;
196 202 }
197 - if (
198 - in_array(
199 - $field->getType(),
200 - ['radio', 'checkbox', 'select', 'multi-select'],
201 - true
202 - ) && !empty($fieldData['fieldOptions'])
203 - ) {
203 + if (FieldHelper::fieldTypeHasOptions($field->getType()) && !empty($fieldData['fieldOptions'])) {
204 204 foreach ($fieldData['fieldOptions'] as $optionData) {
205 205 // Always overwrite fieldId with the actual DB id of the field
206 206 $optionData['fieldId'] = $fieldId;
207 207 $option = FieldOptionsFactory::create($optionData);
@@ -224,8 +224,11 @@
224 224 $parentMap = [];
225 225 foreach ($fields as $fieldData) {
226 226 $fieldData['formId'] = $fieldData['formId'] ?? $formId;
227 227 FieldHelper::resolveParentId($fieldData, $parentMap);
228 + // Process htmlContent / rich text default values to upload base64 images
229 + FieldHelper::processFieldHtmlContent($fieldData, $this->imageService);
230 + FieldHelper::processRichTextDefaultValue($fieldData, $this->imageService);
228 231 $field = FieldFactory::create($fieldData);
229 232 $fieldId = $field->getId();
230 233
231 234 if ($fieldId === 0) { // new field
@@ -272,9 +275,9 @@
272 275 $fields = $this->fieldRepository->getFieldsById($formId);
273 276 $fieldsWithOptions = [];
274 277 foreach ($fields as $field) {
275 278 $fieldArr = $field->toArray();
276 - if (in_array($field->getType(), ['radio', 'checkbox', 'select', 'multi-select'], true)) {
279 + if (FieldHelper::fieldTypeHasOptions($field->getType())) {
277 280 $options = $this->fieldOptionsRepository->getByFieldId($field->getId());
278 281 $fieldArr['fieldOptions'] = array_map(static fn($option) => $option->toArray(), $options);
279 282 }
280 283 $fieldsWithOptions[] = $fieldArr;
@@ -319,42 +322,8 @@
319 322 }
320 323 }
321 324
322 325 /**
323 - * Check for duplicate field values in submission.
324 - * @param Field[] $formFields
325 - * @param array<string,mixed> $submissionData
326 - * @param int $formId
327 - * @return array{0: bool, 1: array<int|string,bool>}
328 - */
329 - public function checkDuplicateFieldValues(
330 - array $formFields,
331 - array $submissionData,
332 - int $formId
333 - ): array {
334 - $duplicateErrors = [];
335 - $isDuplicate = false;
336 - $fieldValues = [];
337 -
338 - foreach ($formFields as $field) {
339 - $fid = $field->getId();
340 - $fidStr = (string)$fid;
341 - if (isset($submissionData[$fidStr])) {
342 - $fieldValues[$fidStr] = $submissionData[$fidStr];
343 - }
344 - if ($field->getFieldAdvancedSettings()->isNoDuplicates() && !empty($fieldValues[$fidStr])) {
345 - $val = $fieldValues[$fidStr];
346 - if ($this->entryFieldRepository->checkDuplicateValue($formId, $fid, $val)) {
347 - $duplicateErrors[$fidStr] = true;
348 - $isDuplicate = true;
349 - }
350 - }
351 - }
352 -
353 - return [$isDuplicate, $duplicateErrors];
354 - }
355 -
356 - /**
357 326 * Validate that all fields have allowed types.
358 327 * @param Field[]|mixed[] $formFields
359 328 * @throws InvalidArgumentException
360 329 */
@@ -359,24 +328,7 @@
359 328 * @throws InvalidArgumentException
360 329 */
361 330 public function validateFieldsType(array $formFields): void
362 331 {
363 - // Validate field types
364 - $allowedTypes = FieldType::getAllowedTypes();
365 - foreach ($formFields as $field) {
366 - // Check if field is an instance of Field or array
367 - $fieldType = $field instanceof Field ? $field->getType() : ($field['type'] ?? null);
368 -
369 - if ($fieldType === null) {
370 - throw new InvalidArgumentException(
371 - BackendStrings::getExceptionStrings()['invalid_field_type'] . ' (type not found)'
372 - );
373 - }
374 -
375 - if (!in_array($fieldType, $allowedTypes, true)) {
376 - throw new InvalidArgumentException(
377 - BackendStrings::getExceptionStrings()['invalid_field_type'] . ' ' . $fieldType
378 - );
379 - }
380 - }
332 + FieldType::validateFields($formFields);
381 333 }
382 334 }