PluginProbe
The Innovative Form Builder – IvyForms / 1.4.1
The Innovative Form Builder – IvyForms v1.4.1
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 / Form / FormCreateService.php

FormCreateService.php in The Innovative Form Builder – IvyForms 1.4.1, at backend/src/Services/Form/FormCreateService.php

169 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © Melograno Venture Studio. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace IvyForms\Services\Form;
9
10 use IvyForms\Common\Exceptions\InvalidArgumentException;
11 use IvyForms\Common\Exceptions\QueryExecutionException;
12 use IvyForms\Common\Exceptions\ValidationException;
13 use IvyForms\Entity\Form\Form;
14 use IvyForms\Factory\Form\FormFactory;
15 use IvyForms\Services\Confirmation\ConfirmationService;
16 use IvyForms\Services\Field\FieldService;
17 use IvyForms\Services\Notification\NotificationService;
18 use IvyForms\Services\Permissions\AccessRulesFormScopeExtender;
19 use IvyForms\Services\Translations\BackendStrings;
20
21 /**
22 * Shared create path for blank/template and AI-generated forms.
23 */
24 class FormCreateService
25 {
26 private FormService $formService;
27 private FieldService $fieldService;
28 private NotificationService $notificationService;
29 private ConfirmationService $confirmationService;
30 private AccessRulesFormScopeExtender $accessRulesFormScopeExtender;
31
32 public function __construct(
33 FormService $formService,
34 FieldService $fieldService,
35 NotificationService $notificationService,
36 ConfirmationService $confirmationService,
37 AccessRulesFormScopeExtender $accessRulesFormScopeExtender
38 ) {
39 $this->formService = $formService;
40 $this->fieldService = $fieldService;
41 $this->notificationService = $notificationService;
42 $this->confirmationService = $confirmationService;
43 $this->accessRulesFormScopeExtender = $accessRulesFormScopeExtender;
44 }
45
46 /**
47 * Persist a sanitized form payload with defaults, fields, hooks, and access scope.
48 *
49 * @param array<string, mixed> $params
50 *
51 * @return array<string, mixed>
52 *
53 * @throws InvalidArgumentException
54 * @throws QueryExecutionException
55 * @throws ValidationException
56 */
57 public function createFromSanitizedParams(array $params): array
58 {
59 $form = FormFactory::create($params);
60 $formId = $this->formService->createForm($form);
61 if (!$formId) {
62 throw new QueryExecutionException(
63 BackendStrings::getAllFormsStrings()['failed_to_create_form']
64 );
65 }
66
67 try {
68 $confirmationId = $this->persistDefaultsAndFields($formId, $params);
69 } catch (\Throwable $e) {
70 $this->deleteOrphanedForm($formId);
71 throw $e;
72 }
73
74 $form->setId($formId);
75 $this->finalizeCreate($form, $formId);
76
77 return array_merge($form->toArray(), ['confirmationId' => $confirmationId]);
78 }
79
80 /**
81 * @param array<string, mixed> $params
82 *
83 * @throws InvalidArgumentException
84 * @throws QueryExecutionException
85 * @throws ValidationException
86 */
87 private function persistDefaultsAndFields(int $formId, array $params): int
88 {
89 $adminEmail = get_option('admin_email');
90 $formName = is_string($params['name'] ?? null) ? $params['name'] : '';
91 $this->notificationService->createDefaultNotificationForForm(
92 $formId,
93 $formName,
94 $adminEmail
95 );
96
97 $confirmationId = $this->confirmationService->createDefaultConfirmationForForm($formId);
98
99 if (!empty($params['fields'])) {
100 $this->fieldService->saveFieldsWithOptions($params['fields'], $formId);
101 }
102
103 return $confirmationId;
104 }
105
106 private function finalizeCreate(Form $form, int $formId): void
107 {
108 /**
109 * After a form row (and related defaults) are persisted.
110 *
111 * @param Form $form
112 * @param int $formId
113 * @param string $context create|update
114 */
115 do_action('ivyforms/form/after_persist', $form, $formId, 'create');
116
117 $this->accessRulesFormScopeExtender->appendCreatedFormToCurrentUserScope($formId);
118 }
119
120 private function deleteOrphanedForm(int $formId): void
121 {
122 $this->rollbackCreatedFormRecords($formId);
123
124 try {
125 $this->formService->deleteForm($formId);
126 } catch (\Throwable $cleanupError) {
127 $this->logCleanupFailure($formId, $cleanupError);
128 }
129 }
130
131 private function rollbackCreatedFormRecords(int $formId): void
132 {
133 $formIds = [$formId];
134
135 try {
136 $this->notificationService->deleteNotificationsByFormIds($formIds);
137 } catch (\Throwable $cleanupError) {
138 $this->logCleanupFailure($formId, $cleanupError);
139 }
140
141 try {
142 $this->confirmationService->deleteConfirmationsByFormIds($formIds);
143 } catch (\Throwable $cleanupError) {
144 $this->logCleanupFailure($formId, $cleanupError);
145 }
146
147 try {
148 $this->fieldService->deleteFieldsByFormIds($formIds);
149 } catch (\Throwable $cleanupError) {
150 $this->logCleanupFailure($formId, $cleanupError);
151 }
152 }
153
154 private function logCleanupFailure(int $formId, \Throwable $cleanupError): void
155 {
156 if (!defined('WP_DEBUG') || !WP_DEBUG) {
157 return;
158 }
159
160 error_log(
161 sprintf(
162 'IvyForms: failed to delete orphaned form %d after create failure: %s',
163 $formId,
164 $cleanupError->getMessage()
165 )
166 );
167 }
168 }
169