PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.1.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.1.2
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / includes / Forms / FormProcessor.php

FormProcessor.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.1.2, at includes/Forms/FormProcessor.php

392 lines 13.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Form Processor Class
4 *
5 * @package EasyInvoice
6 * @author Your Name
7 * @copyright Copyright (c) 2023, Your Company
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 */
11
12 namespace EasyInvoice\Forms;
13
14 /**
15 * Form Processor
16 *
17 * Handles form data processing and mapping to database structure.
18 * Field validation and sanitization are handled by field callbacks.
19 *
20 * @since 1.0.0
21 */
22 class FormProcessor {
23
24 /**
25 * Process form data
26 *
27 * @since 1.0.0
28 * @param array $raw_data Raw form data
29 * @param array $field_definitions Field definitions for reference
30 * @return array Processed data with error handling
31 */
32 public function processFormData(array $raw_data, array $field_definitions): array {
33 $processed_data = [];
34 $errors = [];
35
36 // Handle payment_gateways field specially
37 $payment_gateways_value = '';
38 if (isset($raw_data['payment_gateways_hidden'])) {
39 $payment_gateways_value = $raw_data['payment_gateways_hidden'];
40 } elseif (isset($raw_data['payment_gateways'])) {
41 $payment_gateways_value = $raw_data['payment_gateways'];
42 }
43
44 // Always include payment_gateways in raw_data
45 $raw_data['payment_gateways'] = $payment_gateways_value;
46
47 $known_field_names = [];
48 foreach ($field_definitions as $field) {
49 $field_name = $field['name'] ?? '';
50 if ($field_name) {
51 $known_field_names[] = $field_name;
52 }
53 $required = $field['required'] ?? false;
54 $raw_value = $raw_data[$field_name] ?? '';
55
56 // Check required fields
57 if ($required && empty($raw_value)) {
58 $errors[$field_name] = sprintf(
59 __('%s is required.', 'easy-invoice'),
60 $field['label'] ?? $field_name
61 );
62 continue;
63 }
64
65 // Skip empty non-required fields
66 if (empty($raw_value) && !$required && $raw_value !== '0') {
67 continue;
68 }
69
70 // Apply field-specific sanitization if defined
71 $processed_value = $this->applyFieldSanitization($field, $raw_value);
72
73 // Handle currency fields with "global" option - save "global" as the value
74 if (in_array($field_name, ['currency_code', 'currency_position']) && $processed_value === 'global') {
75 $processed_value = 'global';
76 }
77
78 // Apply field-specific processing if defined
79 $processed_value = $this->applyFieldProcessing($field, $processed_value);
80
81 // Apply field-specific validation if defined
82 $validation_result = $this->applyFieldValidation($field, $processed_value);
83 if ($validation_result !== true) {
84 $errors[$field_name] = $validation_result;
85 continue;
86 }
87
88 $processed_data[$field_name] = $processed_value;
89 }
90
91 // Always include payment_gateways in processed data
92 $processed_data['payment_gateways'] = $payment_gateways_value;
93
94 // Allow plugins to modify the processed data
95 $processed_data = apply_filters('easy_invoice_form_processor_data', $processed_data, $raw_data);
96
97 return [
98 'data' => $processed_data,
99 'errors' => $errors
100 ];
101 }
102
103 /**
104 * Process item data
105 *
106 * @since 1.0.0
107 * @param array $raw_item_data Raw item data
108 * @param array $item_field_definitions Item field definitions
109 * @return array Processed item data
110 */
111 public function processItemData(array $raw_item_data, array $item_field_definitions): array {
112 $processed_item = [];
113
114 // Debug: Log the raw item data
115 error_log("Processing item data: " . print_r($raw_item_data, true));
116
117 foreach ($item_field_definitions as $field) {
118 $field_name = $field['name'] ?? '';
119 $required = $field['required'] ?? false;
120
121 if (empty($field_name)) {
122 continue;
123 }
124
125 $raw_value = $raw_item_data[$field_name] ?? '';
126
127 // Check required fields
128 if ($required && empty($raw_value) && $field['type'] !== 'checkbox') {
129 // For items, we'll skip invalid items rather than throwing errors
130 error_log("Skipping required field {$field_name} - empty value");
131 continue;
132 }
133
134 // For non-required fields, include them even if empty (but not for checkboxes)
135 if ($field['type'] === 'checkbox') {
136 // For checkboxes, only include if they have a value
137 if (isset($raw_item_data[$field_name])) {
138 $processed_value = $this->applyFieldSanitization($field, $raw_value);
139 $processed_item[$field_name] = $processed_value;
140 }
141 } else {
142 // For non-checkbox fields, include them even if empty
143 $processed_value = $this->applyFieldSanitization($field, $raw_value);
144 $processed_item[$field_name] = $processed_value;
145 }
146 }
147
148 // Debug: Log the processed item
149 error_log("Processed item: " . print_r($processed_item, true));
150
151 return $processed_item;
152 }
153
154 /**
155 * Process items data
156 *
157 * @since 1.0.0
158 * @param array $raw_items_data Raw items data
159 * @param array $item_field_definitions Item field definitions
160 * @return array Processed items data
161 */
162 public function processItemsData(array $raw_items_data, array $item_field_definitions): array {
163 $processed_items = [];
164 foreach ($raw_items_data as $item_index => $raw_item_data) {
165 // Ensure all checkbox fields are set
166 foreach ($item_field_definitions as $field) {
167
168 if (($field['type'] ?? '') === 'checkbox') {
169 $field_name = $field['name'] ?? '';
170 if ($field_name && !isset($raw_item_data[$field_name])) {
171 $raw_item_data[$field_name] = '0';
172 }
173 }
174 }
175
176 $processed_item = $this->processItemData($raw_item_data, $item_field_definitions);
177 if (!empty($processed_item)) {
178 $processed_items[] = $processed_item;
179 }
180 }
181
182 return $processed_items;
183 }
184
185 /**
186 * Save form data to database using field configuration
187 *
188 * @since 1.0.0
189 * @param array $form_data Processed form data
190 * @param array $field_definitions Field definitions for reference
191 * @param object $model The model object (Invoice, Quote, etc.)
192 * @return void
193 */
194 public function saveFormDataToDatabase(array $form_data, array $field_definitions, $model): void {
195
196
197 // Process each field from the configuration
198 foreach ($field_definitions as $field) {
199 $field_name = $field['name'] ?? '';
200 if (empty($field_name)) {
201 continue;
202 }
203
204 // Generate database key with _easy_invoice_ prefix
205 $db_key = '_easy_invoice_' . $field_name;
206
207 // Get value from form data
208 $value = $form_data[$field_name] ?? null;
209
210
211
212 // Only save if value exists and is not empty (or is 0)
213 if ($value !== null && $value !== '') {
214 // Use custom save callback if provided
215 if (isset($field['save_callback']) && is_callable($field['save_callback'])) {
216 $field['save_callback']($value, $model);
217 } else {
218 // Default save to meta data
219 if (method_exists($model, 'setMetaData')) {
220 $model->setMetaData($db_key, $value);
221
222
223 }
224 }
225 }
226 }
227
228 // Also process any extra fields that might not be in the configuration
229 foreach ($form_data as $field_name => $value) {
230 if ($value !== null && $value !== '') {
231 $db_key = '_easy_invoice_' . $field_name;
232
233 // Check if this field wasn't already processed above
234 $already_processed = false;
235 foreach ($field_definitions as $field) {
236 if (($field['name'] ?? '') === $field_name) {
237 $already_processed = true;
238 break;
239 }
240 }
241
242 if (!$already_processed && method_exists($model, 'setMetaData')) {
243 $model->setMetaData($db_key, $value);
244 }
245 }
246 }
247 }
248
249 /**
250 * Generate database key from field name
251 *
252 * @since 1.0.0
253 * @param string $field_name The field name
254 * @return string Database key with _easy_invoice_ prefix
255 */
256 public function generateDatabaseKey(string $field_name): string {
257 return '_easy_invoice_' . $field_name;
258 }
259
260 /**
261 * Process field value based on field type
262 *
263 * @since 1.0.0
264 * @param array $field Field configuration
265 * @param mixed $value Raw value
266 * @return mixed Processed value
267 */
268 private function processFieldValue(array $field, $value) {
269 $type = $field['type'] ?? 'text';
270
271 switch ($type) {
272 case 'number':
273 return is_numeric($value) ? floatval($value) : 0;
274
275 case 'checkbox':
276 if (is_string($value)) {
277 $value = strtolower($value);
278 return ($value === '1' || $value === 'true' || $value === 'yes' || $value === 'on') ? '1' : '0';
279 }
280 return ($value == '1' || $value === true) ? '1' : '0';
281
282 case 'payment_gateways':
283 // Handle array of selected payment gateways
284 if (is_array($value)) {
285 // Sanitize each gateway ID and filter out empty values
286 $sanitized_gateways = array_filter(array_map('sanitize_text_field', $value));
287 return implode(',', $sanitized_gateways);
288 } elseif (is_string($value)) {
289 // If it's already a string (comma-separated), sanitize it
290 return sanitize_text_field($value);
291 }
292 return '';
293
294 case 'textarea':
295 return sanitize_textarea_field($value);
296
297 case 'email':
298 return sanitize_email($value);
299
300 case 'url':
301 return esc_url_raw($value);
302
303 case 'date':
304 return sanitize_text_field($value);
305
306 case 'select':
307 case 'text':
308 default:
309 return sanitize_text_field($value);
310 }
311 }
312
313 /**
314 * Apply field-specific sanitization if defined
315 *
316 * @since 1.0.0
317 * @param array $field Field configuration
318 * @param mixed $value Raw value
319 * @return mixed Processed value
320 */
321 private function applyFieldSanitization(array $field, $value) {
322 // Check for sanitize_callback in field configuration
323 $sanitize_callback = $field['sanitize_callback'] ?? null;
324
325 if (is_callable($sanitize_callback)) {
326 return $sanitize_callback($value);
327 }
328
329 // Fallback to basic field type processing
330 return $this->processFieldValue($field, $value);
331 }
332
333 /**
334 * Apply field-specific processing if defined
335 *
336 * @since 1.0.0
337 * @param array $field Field configuration
338 * @param mixed $value Processed value
339 * @return mixed Processed value
340 */
341 private function applyFieldProcessing(array $field, $value) {
342 // Check for process_callback in field configuration
343 $process_callback = $field['process_callback'] ?? null;
344
345 if (is_callable($process_callback)) {
346 return $process_callback($value);
347 }
348
349 return $value;
350 }
351
352 /**
353 * Apply field-specific validation if defined
354 *
355 * @since 1.0.0
356 * @param array $field Field configuration
357 * @param mixed $value Processed value
358 * @return bool|string True if valid, error message if invalid
359 */
360 private function applyFieldValidation(array $field, $value) {
361 // Check for validate_callback in field configuration
362 $validate_callback = $field['validate_callback'] ?? null;
363
364 if (is_callable($validate_callback)) {
365 return $validate_callback($value);
366 }
367
368 return true;
369 }
370
371 /**
372 * Get global currency value for a specific field
373 *
374 * @since 1.0.0
375 * @param string $field_name Field name (currency_code or currency_position)
376 * @return string Global currency value
377 */
378 private function getGlobalCurrencyValue(string $field_name): string {
379 switch ($field_name) {
380 case 'currency_code':
381 return get_option('easy_invoice_currency_code', 'USD');
382 case 'currency_position':
383 $position = get_option('easy_invoice_currency_position', 'left');
384 // Convert from old format if needed
385 if ($position === 'l') return 'before';
386 if ($position === 'r') return 'after';
387 return $position;
388 default:
389 return '';
390 }
391 }
392 }