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 / GoogleSheets / GoogleSheetsDispatcherService.php

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

185 lines 5.9 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\GoogleSheets;
4
5 use IvyForms\Entity\GoogleSheets\GoogleSheetIntegration;
6 use IvyForms\Services\GoogleSheets\Helpers\GoogleSheetsFieldKeyHelper;
7 use IvyForms\Repository\GoogleSheets\GoogleSheetsRepositoryInterface;
8 use IvyForms\Services\Placeholder\PlaceholderService;
9 use IvyForms\Services\Submission\SubmissionDataFormatterService;
10
11 /**
12 * Dispatches form submissions to Google Sheets with retry logic.
13 */
14 class GoogleSheetsDispatcherService
15 {
16 private GoogleSheetsRepositoryInterface $repository;
17 private GoogleSheetsService $googleSheetsService;
18 private SubmissionDataFormatterService $submissionFormatter;
19 private int $maxRetries = 2;
20
21 public function __construct(
22 GoogleSheetsRepositoryInterface $repository,
23 GoogleSheetsService $googleSheetsService,
24 SubmissionDataFormatterService $submissionFormatter
25 ) {
26 $this->repository = $repository;
27 $this->googleSheetsService = $googleSheetsService;
28 $this->submissionFormatter = $submissionFormatter;
29 }
30
31 /**
32 * @param array<string, mixed> $submissionData
33 * @param array<mixed> $formFields
34 */
35 public function dispatchForSubmission(
36 int $formId,
37 array $submissionData,
38 array $formFields
39 ): void {
40 if (!$this->googleSheetsService->isConnected()) {
41 return;
42 }
43
44 $integrations = $this->repository->findEnabledByFormId($formId);
45
46 if ($integrations === []) {
47 return;
48 }
49
50 $formatted = $this->submissionFormatter->formatForIntegrations($submissionData, $formFields);
51 $integrationFieldData = array_merge($formatted['fields'], $formatted['user_inputs']);
52 // UI placeholders use type_fieldIndex (e.g. {{text_1}}); buildFieldData uses the same keys.
53 $fieldData = array_merge(
54 PlaceholderService::buildFieldData($formFields, $submissionData),
55 $integrationFieldData,
56 GoogleSheetsFieldKeyHelper::buildFieldIndexAliases($formFields, $integrationFieldData)
57 );
58 $fieldLabels = [];
59
60 foreach ($formatted['field_metadata'] as $key => $metadata) {
61 $fieldLabels[$key] = $metadata['label'];
62 }
63
64 $generalData = PlaceholderService::buildGeneralData(
65 (int) ($submissionData['id'] ?? 0),
66 $submissionData
67 );
68
69 foreach ($integrations as $integration) {
70 if (!$this->shouldProcess($integration->getSmartLogic(), $submissionData, $formFields)) {
71 continue;
72 }
73
74 $this->syncIntegration(
75 $integration,
76 $fieldData,
77 $generalData,
78 $fieldLabels
79 );
80 }
81 }
82
83 /**
84 * @param array<string, mixed> $smartLogic
85 * @param array<string, mixed> $submissionData
86 * @param array<mixed> $formFields
87 */
88 private function shouldProcess(array $smartLogic, array $submissionData, array $formFields): bool
89 {
90 /**
91 * @param bool $shouldProcess
92 * @param array<string, mixed> $smartLogic
93 * @param array<string, mixed> $submissionData
94 * @param array<mixed> $formFields
95 */
96 return (bool) apply_filters(
97 'ivyforms/google_sheets/should_process',
98 true,
99 $smartLogic,
100 $submissionData,
101 $formFields
102 );
103 }
104
105 /**
106 * @param array<string, mixed> $fieldData
107 * @param array<string, string|int> $generalData
108 * @param array<string, string> $fieldLabels
109 */
110 private function syncIntegration(
111 GoogleSheetIntegration $integration,
112 array $fieldData,
113 array $generalData,
114 array $fieldLabels
115 ): void {
116 $attempt = 0;
117 $lastError = null;
118
119 while ($attempt <= $this->maxRetries) {
120 $result = $this->appendIntegrationRow($integration, $fieldData, $generalData, $fieldLabels);
121
122 if ($result['success']) {
123 $this->updateIntegrationStatus($integration, 'success');
124 return;
125 }
126
127 $lastError = $result;
128 $attempt++;
129 }
130
131 $this->updateIntegrationStatus(
132 $integration,
133 'failed: ' . ($lastError['message'] ?? 'Unknown error')
134 );
135 }
136
137 /**
138 * @param array<string, mixed> $fieldData
139 * @param array<string, string|int> $generalData
140 * @param array<string, string> $fieldLabels
141 * @return array{success: bool, message: string}
142 */
143 private function appendIntegrationRow(
144 GoogleSheetIntegration $integration,
145 array $fieldData,
146 array $generalData,
147 array $fieldLabels
148 ): array {
149 $headers = $this->googleSheetsService->listColumns(
150 $integration->getSpreadsheetId(),
151 $integration->getWorksheetName()
152 );
153
154 if ($headers === []) {
155 $headers = array_map(static function ($mapping) {
156 return $mapping['column'];
157 }, $integration->getFieldMapping());
158 }
159
160 $mappingByColumn = [];
161 foreach ($integration->getFieldMapping() as $mapping) {
162 $mappingByColumn[$mapping['column']] = $mapping['value'];
163 }
164
165 $rowValues = [];
166 foreach ($headers as $header) {
167 $template = $mappingByColumn[$header] ?? '';
168 $rowValues[] = $template !== ''
169 ? PlaceholderService::replacePlaceholders($template, $fieldData, $generalData, $fieldLabels)
170 : '';
171 }
172
173 return $this->googleSheetsService->appendRow(
174 $integration->getSpreadsheetId(),
175 $integration->getWorksheetName(),
176 $rowValues
177 );
178 }
179
180 private function updateIntegrationStatus(GoogleSheetIntegration $integration, string $status): void
181 {
182 $this->repository->updateSyncStatus($integration->getId(), $status);
183 }
184 }
185