PluginProbe
The Innovative Form Builder – IvyForms / 0.4
The Innovative Form Builder – IvyForms v0.4
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 / EntryService.php

EntryService.php in The Innovative Form Builder – IvyForms 0.4, at backend/src/Services/Entry/EntryService.php

387 lines 11.1 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;
4
5 // phpcs:disable PSR1.Files.SideEffects
6 if (!defined('ABSPATH')) {
7 exit; // Exit if accessed directly
8 }
9
10 use IvyForms\Common\Exceptions\NotFoundException;
11 use IvyForms\Common\Exceptions\InvalidArgumentException;
12 use IvyForms\Common\Exceptions\QueryExecutionException;
13 use IvyForms\Common\Exceptions\ValidationException;
14 use IvyForms\Entity\Entry\Entry;
15 use IvyForms\Factory\EntryField\EntryFieldFactory;
16 use IvyForms\Repository\Entry\EntryRepositoryInterface;
17 use IvyForms\Repository\EntryField\EntryFieldRepositoryInterface;
18 use IvyForms\Services\Translations\BackendStrings;
19
20 /**
21 * @SuppressWarnings("TooManyPublicMethods")
22 */
23 class EntryService
24 {
25 private EntryRepositoryInterface $entryRepository;
26 private EntryFieldRepositoryInterface $entryFieldRepository;
27
28 // Constructor injection for the FormRepository via PHP-DI
29 public function __construct(
30 EntryRepositoryInterface $entryRepository,
31 EntryFieldRepositoryInterface $entryFieldRepository
32 ) {
33 $this->entryRepository = $entryRepository;
34 $this->entryFieldRepository = $entryFieldRepository;
35 }
36
37 /**
38 * Create a new entry and its entry fields.
39 *
40 * @param Entry $entryData
41 *
42 * @return int Entry ID
43 */
44 public function createEntry(Entry $entryData): int
45 {
46 // Insert entry and get ID
47 return $this->entryRepository->add($entryData);
48 }
49
50 /**
51 * Add entry fields to the repository.
52 *
53 * @param array<int, object> $formFields
54 * @param int $entryId
55 * @param array<mixed> $submissionData
56 * @throws ValidationException
57 */
58 public function addEntryFields(array $formFields, int $entryId, array $submissionData): void
59 {
60 $parentChildrenMap = $this->buildParentChildrenMap($formFields);
61 $parentSubfieldKeys = $this->buildParentSubfieldKeys($parentChildrenMap, $submissionData);
62
63 foreach ($formFields as $field) {
64 $value = $this->resolveFieldValue($field, $submissionData, $parentChildrenMap, $parentSubfieldKeys);
65 $entryField = EntryFieldFactory::create([
66 'entryId' => $entryId,
67 'fieldId' => $field->getId(),
68 'fieldValue' => $value,
69 ]);
70 $this->entryFieldRepository->add($entryField);
71 }
72 }
73
74 /**
75 * Build parent-children map.
76 *
77 * @param array<int, object> $formFields
78 * @return array<int, array<int>>
79 */
80 private function buildParentChildrenMap(array $formFields): array
81 {
82 $parentChildrenMap = [];
83 foreach ($formFields as $field) {
84 $parentId = method_exists($field, 'getParentId') ? $field->getParentId() : null;
85 if ($parentId && $parentId > 0) {
86 $parentChildrenMap[$parentId][] = $field->getId();
87 }
88 }
89 return $parentChildrenMap;
90 }
91
92 /**
93 * Build subfield key map for each parent.
94 *
95 * @param array<int, array<int>> $parentChildrenMap
96 * @param array<mixed> $submissionData
97 * @return array<int, string|null>
98 */
99 private function buildParentSubfieldKeys(array $parentChildrenMap, array $submissionData): array
100 {
101 $parentSubfieldKeys = [];
102 foreach ($parentChildrenMap as $parentId => $childIds) {
103 $parentValue = $submissionData[$parentId] ?? [];
104 $keys = array_keys($parentValue);
105 foreach ($childIds as $i => $childId) {
106 $parentSubfieldKeys[$childId] = $keys[$i] ?? null;
107 }
108 }
109 return $parentSubfieldKeys;
110 }
111
112 /**
113 * Resolve the value for a field based on its type and parent/child relationship.
114 *
115 * @param object $field
116 * @param array<mixed> $submissionData
117 * @param array<int, array<int>> $parentChildrenMap
118 * @param array<int, string|null> $parentSubfieldKeys
119 * @return mixed
120 */
121 private function resolveFieldValue(
122 object $field,
123 array $submissionData,
124 array $parentChildrenMap,
125 array $parentSubfieldKeys
126 ) {
127 $fieldId = $field->getId();
128 $fieldType = $field->getType();
129 $parentId = method_exists($field, 'getParentId') ? $field->getParentId() : null;
130
131 $parentValue = $this->getParentSubfieldValue($fieldId, $parentId, $submissionData, $parentSubfieldKeys);
132 if ($parentValue !== null) {
133 return $parentValue;
134 }
135
136 if (in_array($fieldType, ['name', 'address'], true) && ($parentId === null || $parentId === 0)) {
137 return $this->getCompoundFieldValue($fieldId, $parentChildrenMap, $parentSubfieldKeys, $submissionData);
138 }
139
140 return $this->getDefaultFieldValue($fieldId, $submissionData);
141 }
142
143 /**
144 * @param int $fieldId
145 * @param int|null $parentId
146 * @param array<int|string, mixed> $submissionData
147 * @param array<int, string|null> $parentSubfieldKeys
148 * @return mixed|null
149 */
150 private function getParentSubfieldValue(
151 int $fieldId,
152 ?int $parentId,
153 array $submissionData,
154 array $parentSubfieldKeys
155 ) {
156 if ($parentId && isset($parentSubfieldKeys[$fieldId])) {
157 $subKey = $parentSubfieldKeys[$fieldId];
158 if (isset($submissionData[$parentId][$subKey])) {
159 return $submissionData[$parentId][$subKey];
160 }
161 }
162 return null;
163 }
164
165 /**
166 * @param int $fieldId
167 * @param array<int, array<int>> $parentChildrenMap
168 * @param array<int, string|null> $parentSubfieldKeys
169 * @param array<int|string, mixed> $submissionData
170 * @return string
171 */
172 private function getCompoundFieldValue(
173 int $fieldId,
174 array $parentChildrenMap,
175 array $parentSubfieldKeys,
176 array $submissionData
177 ): string {
178 $childValues = [];
179 if (!empty($parentChildrenMap[$fieldId])) {
180 foreach ($parentChildrenMap[$fieldId] as $childId) {
181 $subKey = $parentSubfieldKeys[$childId] ?? null;
182 if ($subKey && isset($submissionData[$fieldId][$subKey])) {
183 $val = $submissionData[$fieldId][$subKey];
184 if ($val !== '') {
185 $childValues[] = $val;
186 }
187 }
188 }
189 }
190 return implode(' ', $childValues);
191 }
192
193 /**
194 * @param int $fieldId
195 * @param array<int|string, mixed> $submissionData
196 * @return mixed|string
197 */
198 private function getDefaultFieldValue(int $fieldId, array $submissionData)
199 {
200 return $submissionData[$fieldId] ?? '';
201 }
202
203 /**
204 * Get a specific entry by its ID.
205 *
206 * @param int $entryId
207 *
208 * @return object
209 *
210 * @throws NotFoundException If the entry is not found
211 */
212 public function getEntry(int $entryId): object
213 {
214 $entry = $this->entryRepository->getById($entryId);
215
216 if (!$entry) {
217 throw new NotFoundException(
218 BackendStrings::getExceptionStrings()['entry_not_found']
219 );
220 }
221
222 return $entry;
223 }
224
225 /**
226 * Search for entries based on parameters.
227 *
228 * @param array<string, mixed> $params
229 *
230 * @return array<mixed>
231 */
232 public function searchEntries(array $params): array
233 {
234 return $this->entryRepository->search($params);
235 }
236
237 /**
238 * Get the count of entry filters.
239 *
240 * @param array<string, mixed>|null $params
241 *
242 * @return array<mixed>
243 */
244 public function getFilterCount(?array $params = null): array
245 {
246 return $this->entryRepository->getFilterCount($params);
247 }
248
249 /**
250 * Delete an entry by its ID.
251 *
252 * @param int $id
253 *
254 * @return int
255 */
256 public function deleteEntry(int $id): int
257 {
258 return $this->entryRepository->delete($id);
259 }
260
261 /**
262 * Get all entry fields for a specific entry ID.
263 *
264 * @param int $entryId
265 *
266 * @return array<mixed>
267 */
268 public function getFieldsByEntryId(int $entryId): array
269 {
270 return $this->entryFieldRepository->findBy(['entryId' => $entryId]);
271 }
272
273 /**
274 * Update the starred status of an entry.
275 *
276 * @param int $entryId
277 * @param bool $value
278 *
279 * @return void
280 */
281 public function updateEntryStarred(int $entryId, bool $value): void
282 {
283 $this->entryRepository->updateEntryStarred($entryId, $value);
284 }
285
286 /**
287 * Update the status of an entry.
288 *
289 * @param int $entryId
290 * @param string $status
291 *
292 * @return void
293 */
294 public function updateEntryStatus(int $entryId, string $status): void
295 {
296 $this->entryRepository->updateEntryStatus($entryId, $status);
297 }
298
299 /**
300 * Fetch entries along with their associated fields.
301 *
302 * @param array<int, array<string, mixed>> $entries
303 * @return array<mixed>
304 */
305 public function getEntryFields(array $entries): array
306 {
307 return $this->entryRepository->getEntryFields($entries);
308 }
309
310 /**
311 * Get the count of entries for multiple form IDs.
312 *
313 * @param int[] $formIds
314 * @return array<int, int> formId => count
315 */
316 public function getEntryCountByFormIds(array $formIds): array
317 {
318 return $this->entryRepository->getEntryCountByFormIds($formIds);
319 }
320
321 /**
322 * Delete all entry fields for multiple entry IDs in bulk.
323 *
324 * @param int[] $entryIds
325 * @return int Number of deleted entry fields
326 */
327 public function deleteEntryFieldsByEntryIds(array $entryIds): int
328 {
329 if (empty($entryIds)) {
330 return 0;
331 }
332 $allFields = $this->entryFieldRepository->findIdsByEntryIds($entryIds);
333 if (empty($allFields)) {
334 return 0;
335 }
336 return $this->entryFieldRepository->deleteMany($allFields);
337 }
338
339 /**
340 * Delete multiple entries by their IDs in bulk.
341 *
342 * @param int[] $ids
343 * @return int Number of deleted entries
344 */
345 public function deleteEntriesByIds(array $ids): int
346 {
347 if (empty($ids)) {
348 return 0;
349 }
350 return $this->entryRepository->deleteMany($ids);
351 }
352
353 /**
354 * Delete all entries for multiple form IDs in bulk.
355 *
356 * @param int[] $formIds
357 * @return int Number of deleted entries
358 * @throws InvalidArgumentException
359 */
360 public function deleteEntriesByFormIds(array $formIds): int
361 {
362 if (empty($formIds)) {
363 throw new InvalidArgumentException(
364 BackendStrings::getExceptionStrings()['no_form_ids_provided']
365 );
366 }
367
368 // Delete entry fields by form IDs using a subquery
369 $this->entryFieldRepository->deleteByFormIds($formIds);
370 // Delete entries by formId
371 return $this->entryRepository->deleteManyByForeignKeyValues($formIds);
372 }
373
374 /**
375 * Check if a value already exists in entry fields for a specific field.
376 *
377 * @param int $formId
378 * @param int $fieldId
379 * @param string $value
380 * @return bool
381 */
382 public function checkDuplicateValue(int $formId, int $fieldId, string $value): bool
383 {
384 return $this->entryFieldRepository->checkDuplicateValue($formId, $fieldId, $value);
385 }
386 }
387