PluginProbe
The Innovative Form Builder – IvyForms / 1.1
The Innovative Form Builder – IvyForms v1.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 / FormImportExportService.php

FormImportExportService.php in The Innovative Form Builder – IvyForms 1.1, at backend/src/Services/Form/FormImportExportService.php

317 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\Form;
4
5 use IvyForms\Common\Helpers\FormImportValidator;
6 use IvyForms\Common\Exceptions\NotFoundException;
7 use IvyForms\Common\Exceptions\QueryExecutionException;
8 use IvyForms\Common\Exceptions\ValidationException;
9 use IvyForms\Services\Field\FieldService;
10 use IvyForms\Services\Notification\NotificationService;
11 use IvyForms\Services\Confirmation\ConfirmationService;
12 use IvyForms\Services\Translations\BackendStrings;
13 use IvyForms\Factory\Form\FormFactory;
14 use IvyForms\Factory\Notification\NotificationFactory;
15 use IvyForms\Factory\Confirmation\ConfirmationFactory;
16
17 class FormImportExportService
18 {
19 private FormService $formService;
20 private FieldService $fieldService;
21 private NotificationService $notificationService;
22 private ConfirmationService $confirmationService;
23
24 public function __construct(
25 FormService $formService,
26 FieldService $fieldService,
27 NotificationService $notificationService,
28 ConfirmationService $confirmationService
29 ) {
30 $this->formService = $formService;
31 $this->fieldService = $fieldService;
32 $this->notificationService = $notificationService;
33 $this->confirmationService = $confirmationService;
34 }
35
36 /**
37 * Export multiple forms
38 * @param array<int> $formIds
39 * @return array<string, mixed>
40 */
41 public function exportForms(array $formIds): array
42 {
43 $exportedForms = [];
44
45 foreach ($formIds as $formId) {
46 $formData = $this->exportSingleForm($formId);
47 if ($formData) {
48 $exportedForms[] = $formData;
49 }
50 }
51
52 $exportPayload = [
53 'version' => IVYFORMS_VERSION,
54 'export_date' => current_time('mysql'),
55 'forms' => $exportedForms,
56 'forms_count' => count($exportedForms)
57 ];
58
59 /**
60 * Allows extending the final form export payload.
61 * Useful for adding top-level metadata or future sections related to export/import.
62 *
63 * @since 0.1.0
64 *
65 * @param array<string, mixed> $exportPayload The complete export payload.
66 * @param array<int> $formIds The requested form IDs.
67 * @param array<int, array<string, mixed>> $exportedForms The exported forms payload.
68 * @return array<string, mixed> Modified export payload.
69 */
70 return apply_filters('ivyforms/form/import_export/export_payload', $exportPayload, $formIds, $exportedForms);
71 }
72
73 /**
74 * Export a single form with all its data
75 * @param int $formId
76 * @return array<string, mixed>
77 * @throws NotFoundException
78 * @throws ValidationException|QueryExecutionException
79 */
80 private function exportSingleForm(int $formId): array
81 {
82 $form = $this->formService->getFormById($formId);
83 $fields = $this->fieldService->getAllFieldsWithOptions($formId);
84 $idToIndexMap = FormImportValidator::buildFieldIdToIndexMap($fields);
85 $notifications = $this->notificationService->getNotificationsByFormId($formId);
86 $confirmations = $this->confirmationService->getConfirmationsById($formId);
87 $confirmation = !empty($confirmations) ? $confirmations[0] : null;
88
89 $exportSections = [
90 'form' => FormImportValidator::prepareFormDataForExport($form),
91 'fields' => FormImportValidator::prepareFieldsDataForExport($fields, $idToIndexMap),
92 'notifications' => FormImportValidator::prepareNotificationsDataForExport($notifications),
93 'confirmation' => FormImportValidator::prepareConfirmationDataForExport($confirmation),
94 ];
95
96 /**
97 * Allows extending exported sections for a single form.
98 * Useful for adding future sections like entries, styles, or other custom form data.
99 *
100 * @since 0.1.0
101 *
102 * @param array<string, mixed> $exportSections The current export sections for a single form.
103 * @param int $formId The source form ID.
104 * @param mixed $form The source form entity.
105 * @param array<int, array<string, mixed>> $fields The source form fields.
106 * @param array<int, mixed> $notifications The source notifications.
107 * @param mixed $confirmation The source confirmation entity.
108 * @return array<string, mixed> Modified export sections.
109 */
110 return apply_filters(
111 'ivyforms/form/import_export/export_sections',
112 $exportSections,
113 $formId,
114 $form,
115 $fields,
116 $notifications,
117 $confirmation
118 );
119 }
120
121 /**
122 * Import multiple forms
123 * @param array<int, array<string, mixed>> $formsData
124 * @return array<int, array<string, mixed>>
125 */
126 public function importForms(array $formsData): array
127 {
128 /**
129 * Allows preprocessing imported forms data before per-form import starts.
130 * Useful for normalizing or enriching custom import sections.
131 *
132 * @since 0.1.0
133 *
134 * @param array<int, array<string, mixed>> $formsData The validated forms data to import.
135 * @return array<int, array<string, mixed>> Modified forms data.
136 */
137 $formsData = apply_filters('ivyforms/form/import_export/import_forms_data', $formsData);
138
139 $importedForms = [];
140 foreach ($formsData as $formData) {
141 $importedForm = $this->importSingleForm($formData);
142 if ($importedForm) {
143 $importedForms[] = $importedForm;
144 }
145 }
146 return $importedForms;
147 }
148
149 /**
150 * Import a single form with all its data
151 * @param array<string, mixed> $formData
152 * @return array<string, mixed>|null
153 */
154 private function importSingleForm(array $formData): ?array
155 {
156 /**
157 * Allows preprocessing a single imported form payload before core import runs.
158 * Useful for future custom sections such as entries or styles.
159 *
160 * @since 0.1.0
161 *
162 * @param array<string, mixed> $formData The single form payload being imported.
163 * @return array<string, mixed> Modified single form payload.
164 */
165 $formData = apply_filters('ivyforms/form/import_export/import_form_data', $formData);
166
167 $currentUser = wp_get_current_user();
168 $author = $currentUser->user_login ?? 'admin';
169 $formArray = FormImportValidator::prepareFormArrayForImport(
170 $formData,
171 $author,
172 current_time('mysql'),
173 BackendStrings::getCommonStrings()['submit']
174 );
175 $formId = $this->createFormForImport($formArray);
176 if (!$formId) {
177 return null;
178 }
179
180 /**
181 * Fires after the core form record is created during import.
182 * Useful for importing form-related metadata that only needs the new form ID.
183 *
184 * @since 0.1.0
185 *
186 * @param int $formId The newly created form ID.
187 * @param array<string, mixed> $formData The imported single form payload.
188 * @param array<string, mixed> $formArray The prepared form array used to create the form.
189 */
190 do_action('ivyforms/form/import_export/after_form_created', $formId, $formData, $formArray);
191
192 $formName = $this->resolveImportedFormName($formArray);
193 $this->importFieldsForForm($formData, $formId);
194 $this->importNotificationsForForm($formData, $formId, $formName);
195 $this->importConfirmationForForm($formData, $formId);
196
197 /**
198 * Fires after the core import process finishes for a single form.
199 * Useful for importing dependent sections such as entries or styles.
200 *
201 * @since 0.1.0
202 *
203 * @param int $formId The newly created form ID.
204 * @param array<string, mixed> $formData The imported single form payload.
205 * @param array<string, mixed> $formArray The prepared form array used to create the form.
206 * @param string $formName The resolved imported form name.
207 */
208 do_action('ivyforms/form/import_export/after_import', $formId, $formData, $formArray, $formName);
209
210 return [
211
212 'id' => $formId,
213 'name' => $formName
214 ];
215 }
216
217 /**
218 * Create a form record for import.
219 * @param array<string, mixed> $formArray
220 * @return int|null
221 */
222 private function createFormForImport(array $formArray): ?int
223 {
224 $form = FormFactory::create($formArray);
225 $formId = $this->formService->createForm($form);
226 if (!$formId) {
227 return null;
228 }
229
230 return $formId;
231 }
232
233 /**
234 * Resolve final form name for import response and defaults.
235 * @param array<string, mixed> $formArray
236 * @return string
237 */
238 private function resolveImportedFormName(array $formArray): string
239 {
240 if (!empty($formArray['name'])) {
241 return (string)$formArray['name'];
242 }
243
244 return BackendStrings::getAllFormsStrings()['imported_form'];
245 }
246
247 /**
248 * Import form fields if present.
249 * @param array<string, mixed> $formData
250 * @param int $formId
251 * @return void
252 */
253 private function importFieldsForForm(array $formData, int $formId): void
254 {
255 $fieldsData = $formData['fields'] ?? [];
256 if (empty($fieldsData)) {
257 return;
258 }
259
260 $fieldsData = FormImportValidator::normalizeFieldsForImport($fieldsData);
261
262 $this->fieldService->saveFieldsWithOptions($fieldsData, $formId);
263 }
264
265 /**
266 * Import notifications or create defaults when missing.
267 * @param array<string, mixed> $formData
268 * @param int $formId
269 * @param string $formName
270 * @return void
271 */
272 private function importNotificationsForForm(array $formData, int $formId, string $formName): void
273 {
274 $notificationsData = $formData['notifications'] ?? [];
275 if (empty($notificationsData)) {
276 $adminEmail = get_option('admin_email');
277 $this->notificationService->createDefaultNotificationForForm(
278 $formId,
279 $formName,
280 is_string($adminEmail) ? $adminEmail : ''
281 );
282 return;
283 }
284
285 foreach ($notificationsData as $notificationData) {
286 $notificationData['id'] = 0;
287 $notificationData['formId'] = $formId;
288 $notification = NotificationFactory::create($notificationData);
289 $this->notificationService->createNotification($notification);
290 }
291 }
292
293 /**
294 * Import confirmation or create default one when missing.
295 * @param array<string, mixed> $formData
296 * @param int $formId
297 * @return void
298 */
299 private function importConfirmationForForm(array $formData, int $formId): void
300 {
301 $confirmationData = $formData['confirmation'] ?? null;
302 if (is_object($confirmationData)) {
303 $confirmationData = (array)$confirmationData;
304 }
305
306 if (!is_array($confirmationData) || empty($confirmationData)) {
307 $this->confirmationService->createDefaultConfirmationForForm($formId);
308 return;
309 }
310
311 $confirmationData['id'] = 0;
312 $confirmationData['formId'] = $formId;
313 $confirmation = ConfirmationFactory::create($confirmationData);
314 $this->confirmationService->createConfirmation($confirmation);
315 }
316 }
317