PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 1.2.4
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v1.2.4
6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 3.6.66 All 195 releases
fluentform / app / Services / FormParser.php

FormParser.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 1.2.4, at app/Services/FormParser.php

426 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Services;
4
5 use FluentForm\Framework\Helpers\ArrayHelper;
6
7 class FormParser
8 {
9 /**
10 * @var \stdClass $form
11 */
12 protected $form;
13
14 /**
15 * @var array $inputTypes
16 */
17 protected $inputTypes;
18
19 /**
20 * @var array flattened inputs.
21 */
22 protected $inputs;
23
24 /**
25 * Form Parser constructor.
26 *
27 * @param \stdClass $form
28 */
29 public function __construct($form)
30 {
31 $this->form = $form;
32
33 // Firing an event so that others can hook into it and add other input types.
34 $this->inputTypes = apply_filters('fluentform_form_input_fields', [
35 'input_text',
36 'input_name',
37 'textarea',
38 'select',
39 'input_radio',
40 'input_checkbox',
41 'input_email',
42 'input_url',
43 'input_password',
44 'input_file',
45 'input_image',
46 'input_date',
47 'select_country',
48 'input_number',
49 'input_repeat',
50 'address',
51 'terms_and_condition',
52 'input_hidden'
53 ]);
54 }
55
56 /**
57 * Get form fields.
58 *
59 * @return array
60 */
61 public function getFields($asArray = false)
62 {
63 $fields = $this->form->form_fields;
64
65 if (! $fields) {
66 return $asArray ? [] : null;
67 }
68
69 return $asArray ? json_decode($fields, true)['fields'] : json_decode($fields, false)->fields;
70 }
71
72 /**
73 * Get flatten form inputs. Flatten implies that all
74 * of the form fields will be in a simple array.
75 *
76 * @return array
77 */
78 public function getInputs($with = [])
79 {
80 $fields = $this->getFields(true);
81 $inputs = [];
82
83 $with = $with ?: ['admin_label', 'element', 'options', 'attributes'];
84
85 $this->inputs = $this->extractor($fields, $inputs, $with);
86
87 return $this->inputs;
88 }
89
90 /**
91 * Extract form fields recursively.
92 *
93 * @param array $fields
94 * @param array $inputs
95 *
96 * @return array
97 */
98 public function extractor($fields = [], &$inputs = [], $with = [])
99 {
100 foreach ($fields as $field) {
101
102 // If the field is a Container (collection of other fields)
103 // then we will recursively call this function to resolve.
104 if ($field['element'] === 'container') {
105 foreach ($field['columns'] as $item) {
106 $this->extractor($item['fields'], $inputs, $with);
107 }
108 }
109
110 // Now the field is supposed to be a flat field.
111 // We can extract the desired keys as we want.
112 else {
113 if (in_array($field['element'], $this->inputTypes)) {
114
115 $response = [];
116
117 if (in_array('admin_label', $with)) {
118 $adminLabel = ArrayHelper::get($field, 'settings.admin_field_label')
119 ?: ArrayHelper::get($field, 'settings.label');
120
121 if (! $adminLabel) {
122 $adminLabel = $field['element'];
123 }
124 $response['admin_label'] = $adminLabel;
125
126 }
127
128 $response['element'] = $field['element'];
129
130 if (in_array('options', $with)) {
131 $response['options'] = ArrayHelper::get($field, 'options', []);
132 }
133
134 if (in_array('attributes', $with)) {
135 $response['attributes'] = ArrayHelper::get($field, 'attributes');
136 }
137
138 if (in_array('rules', $with)) {
139 // There are some rubbish structured fields that needs special
140 // attention. Take it muthafucka, hell with you, dhishooom
141 if (in_array($field['element'], ['input_name', 'address'])) {
142 // Conditionals will be applied as a set not on the individual
143 // fields live in these brave, awesome fields container.
144 $conditionals = ArrayHelper::get($field, 'settings.conditional_logics');
145
146 foreach ($field['fields'] as $customField) {
147 $key = $field['attributes']['name'].'['.$customField['attributes']['name'].']';
148
149 // We have to directly push the rules to the $inputs.
150 $inputs[$key]['rules'] = ArrayHelper::get($customField, 'settings.validation_rules');
151 $inputs[$key]['conditionals'] = $conditionals;
152 $inputs[$key]['element'] = $customField['element'];
153 }
154 } elseif ($field['element'] === 'input_repeat') {
155 // Conditionals will be applied as a set not on the individual
156 // fields live in these brave, awesome fields container.
157 $conditionals = ArrayHelper::get($field, 'settings.conditional_logics');
158
159 foreach ($field['fields'] as $column => $customField) {
160 $key = $field['attributes']['name'].'['.$column.'].*';
161
162 // We have to directly push the rules to the $inputs.
163 $inputs[$key]['rules'] = ArrayHelper::get($customField, 'settings.validation_rules');
164 $inputs[$key]['conditionals'] = $conditionals;
165 $inputs[$key]['element'] = $field['element'];
166 $inputs[$key]['attributes'] = $field['attributes'];
167 }
168 } else {
169 // otherwise push the rules to the temporary $response.
170 $response['rules'] = ArrayHelper::get($field, 'settings.validation_rules');
171 $response['conditionals'] = ArrayHelper::get($field, 'settings.conditional_logics');
172 }
173 }
174
175 if (! in_array($field['element'], ['input_name', 'input_repeat', 'address'])) {
176 $inputs[$field['attributes']['name']] = $response;
177 }
178 }
179 }
180 }
181
182 return $inputs;
183 }
184
185
186 /**
187 * Get flatten form inputs. Flatten implies that all
188 * of the form fields will be in a simple array.
189 *
190 * @todo: We may need to refactor this
191 * @return array
192 */
193 public function getEntryInputs($with = ['admin_label'])
194 {
195 $fields = $this->getFields(true);
196 $inputs = [];
197
198 $this->inputs = $this->entryExtractor($fields, $inputs, $with);
199
200 return $this->inputs;
201 }
202
203 /**
204 * Extract form fields recursively.
205 *
206 * @todo: We may need to refactor this
207 *
208 * @param array $fields
209 * @param array $inputs
210 *
211 * @return array
212 */
213 public function entryExtractor($fields = [], &$inputs = [], $with = [])
214 {
215 foreach ($fields as $field) {
216
217 // If the field is a Container (collection of other fields)
218 // then we will recursively call this function to resolve.
219 if ($field['element'] === 'container') {
220 foreach ($field['columns'] as $item) {
221 $this->entryExtractor($item['fields'], $inputs, $with);
222 }
223 }
224
225 // Now the field is supposed to be a flat field.
226 // We can extract the desired keys as we want.
227 else {
228 if (in_array($field['element'], $this->inputTypes)) {
229 $response = [];
230 $adminLabel = ArrayHelper::get($field, 'settings.admin_field_label')
231 ?: ArrayHelper::get($field, 'settings.label');
232
233 if (! $adminLabel) {
234 $adminLabel = ArrayHelper::get($field, 'attributes.name');
235 }
236
237 $response['admin_label'] = $adminLabel;
238
239 if (in_array('attributes', $with)) {
240 $response['attributes'] = ArrayHelper::get($field, 'attributes');
241 }
242
243 if (in_array('options', $with)) {
244 $response['options'] = ArrayHelper::get($field, 'options');
245 }
246
247 $response['element'] = $field['element'];
248 $inputs[$field['attributes']['name']] = $response;
249 }
250 }
251 }
252
253 return $inputs;
254 }
255
256
257 /**
258 * Get admin labels of the form fields.
259 *
260 * @param array $fields
261 *
262 * @return array
263 */
264 public function getAdminLabels($fields = [])
265 {
266 $fields = $fields ?: $this->getInputs(['admin_label']);
267
268 $labels = [];
269
270 foreach ($fields as $key => $field) {
271 $labels[$key] = $field['admin_label'];
272 }
273
274 return $labels;
275 }
276
277 /**
278 * Get admin labels of the form fields.
279 *
280 * @param array $fields
281 *
282 * @return array
283 */
284 public function getValidations($inputs, $fields = [])
285 {
286 $fields = $fields ?: $this->getInputs(['rules']);
287
288 $rules = [];
289
290 $messages = [];
291
292 foreach ($fields as $fieldName => $field) {
293 // Should evaluate if this field is applicable for validation.
294 // Since there could be scenarios where this field should
295 // not be applicable because of some conditional logics.
296 // And it's possible because of dynamic field rendering
297 // this particular field is hidden for some reason.
298 $accessor = rtrim(str_replace(['[', ']', '*'], ['.'], $fieldName), '.');
299
300 $applicable = ArrayHelper::has($inputs, $accessor);
301
302 if ($applicable) {
303 $formValue = ArrayHelper::get($inputs, $accessor);
304
305 $hasRequiredRule = ArrayHelper::get($field['rules'], 'required.value');
306
307 $repeatField = ArrayHelper::get($field, 'element') === 'input_repeat';
308
309 if ($repeatField) {
310 $repeatFieldAttributeName = ArrayHelper::get($field, 'attributes.name');
311
312 $repeatFieldValueLength = count(ArrayHelper::get($inputs, $repeatFieldAttributeName)[0]);
313
314 $repeatFieldRuleKey = rtrim($fieldName, '.*');
315 }
316
317 foreach ($field['rules'] as $ruleName => $rule) {
318 if ($rule['value']) {
319 // If the field is empty and doesn't have any required rules
320 // then we shouldn't validate it for other rules meaning
321 // giving permission to submit empty value for this.
322 if ($formValue === '' && ! $hasRequiredRule) {
323 continue;
324 }
325
326 // The file type input has rule values in an array. For
327 // that we are taking arrays into consideration.
328 $ruleValue = is_array($rule['value'])
329 ? implode(',', array_filter(array_values(str_replace('|', ',', $rule['value']))))
330 : $rule['value'];
331
332 $logic = $ruleName.':'.$ruleValue;
333
334 if ($repeatField) {
335 for ($i = 0; $i < $repeatFieldValueLength; $i++) {
336 $fieldName = $repeatFieldRuleKey.'['.$i.']';
337
338 $rules[$fieldName] = isset($rules[$fieldName]) ? $rules[$fieldName].'|'.$logic : $logic;
339
340 $messages[$fieldName.'.'.$ruleName] = $rule['message'];
341 }
342 } else {
343 // if there is already a rule for this field we need to
344 // concat current rule to it. else assign current rule.
345 $rules[$fieldName] = isset($rules[$fieldName]) ? $rules[$fieldName].'|'.$logic : $logic;
346
347 $messages[$fieldName.'.'.$ruleName] = $rule['message'];
348 }
349 }
350 }
351 }
352 }
353
354 return [$rules, $messages];
355 }
356
357 /**
358 * Get an element by it's name.
359 *
360 * @param string|array $name
361 * @param array $with
362 *
363 * @return array
364 */
365 public function getElement($name, $with = [])
366 {
367 $this->inputTypes = (array) $name;
368
369 return $this->getInputs($with);
370 }
371
372 /**
373 * Determine whether the form has an element.
374 *
375 * @param $name
376 *
377 * @return bool
378 */
379 public function hasElement($name)
380 {
381 return array_key_exists($name, $this->getElement($name, ['element']));
382 }
383
384 /**
385 * Determine whether the form has any required fields.
386 *
387 * @param array $fields
388 *
389 * @return bool
390 */
391 public function hasRequiredFields($fields = [])
392 {
393 // $fields can be user provided when called this method or,
394 // the current object could have already parsed fields or,
395 // we should parse the form and use the processed result.
396 $fields = $fields ?: $this->inputs ?: $this->getInputs(['rules']);
397
398 $exist = false;
399
400 foreach ($fields as $field) {
401 $exist = ArrayHelper::get($field, 'rules.required.value');
402
403 if ($exist) {
404 break;
405 }
406 }
407
408 return (boolean) $exist;
409 }
410
411 /**
412 * Get an specific field for an element type.
413 *
414 * @param $element
415 * @param $attribute
416 * @param array $with
417 *
418 * @return array|null
419 */
420 public function getField($element, $attribute, $with = [])
421 {
422 $element = $this->getElement($element, $with);
423
424 return array_intersect_key($element, array_flip((array) $attribute));
425 }
426 }