| @@ -1,8 +1,10 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace FluentForm\App\Services\Parser; |
| 4 | 4 | |
| 5 | +use FluentForm\App\Helpers\Helper; | |
| 6 | +use FluentForm\App\Services\ConditionAssesor; | |
| 5 | 7 | use FluentForm\Framework\Helpers\ArrayHelper as Arr; |
| 6 | 8 | |
| 7 | 9 | class Extractor |
| 8 | 10 | { |
| @@ -20,9 +22,9 @@ | ||
| 20 | 22 | */ |
| 21 | 23 | protected $with; |
| 22 | 24 | |
| 23 | 25 | /** |
| 24 | - * The supported form input types defined for Fluent Form. | |
| 26 | + * The supported form input types defined for Fluent Forms. | |
| 25 | 27 | * |
| 26 | 28 | * @var array |
| 27 | 29 | */ |
| 28 | 30 | protected $inputTypes; |
| @@ -86,11 +88,13 @@ | ||
| 86 | 88 | { |
| 87 | 89 | foreach ($fields as $field) { |
| 88 | 90 | // If the field is a Container (collection of other fields) |
| 89 | 91 | // then we will recursively call this function to resolve. |
| 90 | - if ($field['element'] === 'container') { | |
| 91 | - foreach ($field['columns'] as $item) { | |
| 92 | - $this->looper($item['fields']); | |
| 92 | + if (Arr::get($field, 'element') === 'container') { | |
| 93 | + $columns = Arr::get($field, 'columns', []); | |
| 94 | + foreach ($columns as $item) { | |
| 95 | + $itemFields = Arr::get($item, 'fields', []); | |
| 96 | + $this->looper($itemFields); | |
| 93 | 97 | } |
| 94 | 98 | } |
| 95 | 99 | |
| 96 | 100 | // Now the field is supposed to be a flat field. |
| @@ -95,9 +99,10 @@ | ||
| 95 | 99 | |
| 96 | 100 | // Now the field is supposed to be a flat field. |
| 97 | 101 | // We can extract the desired keys as we want. |
| 98 | 102 | else { |
| 99 | - if (in_array($field['element'], $this->inputTypes)) { | |
| 103 | + $element = Arr::get($field, 'element'); | |
| 104 | + if ($element && in_array($element, $this->inputTypes)) { | |
| 100 | 105 | $this->extractField($field); |
| 101 | 106 | } |
| 102 | 107 | } |
| 103 | 108 | } |
| @@ -103,8 +108,58 @@ | ||
| 103 | 108 | } |
| 104 | 109 | } |
| 105 | 110 | |
| 106 | 111 | /** |
| 112 | + * The extractor initializer for getting the extracted data. | |
| 113 | + * | |
| 114 | + * @return array | |
| 115 | + */ | |
| 116 | + public function extractEssentials($formData) | |
| 117 | + { | |
| 118 | + $this->looperEssential($formData, $this->fields); | |
| 119 | + | |
| 120 | + return $this->result; | |
| 121 | + } | |
| 122 | + | |
| 123 | + /** | |
| 124 | + * The recursive looper method to loop each | |
| 125 | + * of the fields and extract it's data. | |
| 126 | + * | |
| 127 | + * @param array $fields | |
| 128 | + */ | |
| 129 | + protected function looperEssential($formData, $fields = []) | |
| 130 | + { | |
| 131 | + foreach ($fields as $field) { | |
| 132 | + $field['conditionals'] = Arr::get($field, 'settings.conditional_logics', []); | |
| 133 | + | |
| 134 | + $matched = ConditionAssesor::evaluate($field, $formData); | |
| 135 | + | |
| 136 | + if (!$matched) { | |
| 137 | + continue; | |
| 138 | + } | |
| 139 | + | |
| 140 | + // If the field is a Container (collection of other fields) | |
| 141 | + // then we will recursively call this function to resolve. | |
| 142 | + if (Arr::get($field, 'element') === 'container') { | |
| 143 | + $columns = Arr::get($field, 'columns', []); | |
| 144 | + foreach ($columns as $item) { | |
| 145 | + $itemFields = Arr::get($item, 'fields', []); | |
| 146 | + $this->looperEssential($formData, $itemFields); | |
| 147 | + } | |
| 148 | + } | |
| 149 | + | |
| 150 | + // Now the field is supposed to be a flat field. | |
| 151 | + // We can extract the desired keys as we want. | |
| 152 | + else { | |
| 153 | + $element = Arr::get($field, 'element'); | |
| 154 | + if ($element && in_array($element, $this->inputTypes)) { | |
| 155 | + $this->extractField($field); | |
| 156 | + } | |
| 157 | + } | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + /** | |
| 107 | 162 | * Extract the form field. |
| 108 | 163 | * |
| 109 | 164 | * @param array $field |
| 110 | 165 | * @return $this |
| @@ -155,9 +210,9 @@ | ||
| 155 | 210 | * @return $this |
| 156 | 211 | */ |
| 157 | 212 | protected function setElement() |
| 158 | 213 | { |
| 159 | - $this->result[$this->attribute]['element'] = $this->field['element']; | |
| 214 | + $this->result[$this->attribute]['element'] = Arr::get($this->field, 'element', ''); | |
| 160 | 215 | return $this; |
| 161 | 216 | } |
| 162 | 217 | |
| 163 | 218 | |
| @@ -201,12 +256,14 @@ | ||
| 201 | 256 | */ |
| 202 | 257 | protected function setOptions() |
| 203 | 258 | { |
| 204 | 259 | if (in_array('options', $this->with)) { |
| 205 | - $options = Arr::get($this->field, 'options', []); | |
| 260 | + $options = Arr::get($this->field, 'options', []); | |
| 206 | 261 | |
| 207 | - if(!$options) { | |
| 208 | - $newOptions = Arr::get($this->field, 'settings.advanced_options', []); | |
| 262 | + if(!$options) { | |
| 263 | + $newOptions = \FluentForm\App\Helpers\Helper::flattenAdvancedOptions( | |
| 264 | + Arr::get($this->field, 'settings.advanced_options', []) | |
| 265 | + ); | |
| 209 | 266 | |
| 210 | 267 | if( |
| 211 | 268 | !$newOptions |
| 212 | 269 | && Arr::get($this->field,'element') == 'multi_payment_component' |
| @@ -282,8 +339,10 @@ | ||
| 282 | 339 | $this->field, |
| 283 | 340 | 'settings.validation_rules' |
| 284 | 341 | ); |
| 285 | 342 | |
| 343 | + $this->handleMaxLengthValidation(); | |
| 344 | + | |
| 286 | 345 | $this->result[$this->attribute]['conditionals'] = Arr::get( |
| 287 | 346 | $this->field, |
| 288 | 347 | 'settings.conditional_logics' |
| 289 | 348 | ); |
| @@ -291,68 +350,95 @@ | ||
| 291 | 350 | |
| 292 | 351 | return $this; |
| 293 | 352 | } |
| 294 | 353 | |
| 295 | - /** | |
| 296 | - * Handle the child fields of the custom field. | |
| 297 | - * | |
| 298 | - * @return $this | |
| 299 | - */ | |
| 300 | - protected function handleCustomField() | |
| 354 | + protected function handleMaxLengthValidation() | |
| 301 | 355 | { |
| 302 | - // If this field is a custom field we'll assume it has it's child fields | |
| 303 | - // under the `fields` key. Then we are gonna modify those child fields' | |
| 304 | - // attribute `name`, `label` & `conditional_logics` properties using | |
| 305 | - // the parent field. The current implementation will modify those | |
| 306 | - // properties in a way so that we can use dot notation to access. | |
| 307 | - $customFields = Arr::get($this->field, 'fields'); | |
| 356 | + $maxLength = Arr::get($this->field, 'attributes.maxlength'); | |
| 357 | + $fieldHasMaxValidation = Arr::get($this->field, 'settings.validation_rules.max'); | |
| 358 | + $shouldSetMaxValidation = $maxLength && !$fieldHasMaxValidation; | |
| 308 | 359 | |
| 309 | - if ($customFields) { | |
| 310 | - $parentAttribute = Arr::get($this->field, 'attributes.name'); | |
| 311 | - | |
| 312 | - $parentConditionalLogics = Arr::get($this->field, 'settings.conditional_logics', []); | |
| 313 | - | |
| 314 | - $isAddressOrNameField = in_array(Arr::get($this->field, 'element'), ['address', 'input_name']); | |
| 315 | - | |
| 316 | - $isRepeatField = Arr::get($this->field, 'element') === 'input_repeat' || Arr::get($this->field, 'element') == 'repeater_field'; | |
| 317 | - | |
| 318 | - foreach ($customFields as $index => $customField) { | |
| 319 | - // If the current field is in fact `address` || `name` field | |
| 320 | - // then we have to only keep the enabled child fields | |
| 321 | - // by the user from the form editor settings. | |
| 322 | - if ($isAddressOrNameField) { | |
| 323 | - if (!Arr::get($customField, 'settings.visible', false)) { | |
| 324 | - unset($customFields[$index]); | |
| 325 | - continue; | |
| 326 | - } | |
| 327 | - } | |
| 328 | - | |
| 329 | - // Depending on whether the parent field is a repeat field or not | |
| 330 | - // the modified attribute name of the child field will vary. | |
| 331 | - if ($isRepeatField) { | |
| 332 | - $modifiedAttribute = $parentAttribute.'['.$index.'].*'; | |
| 333 | - } else { | |
| 334 | - $modifiedAttribute = $parentAttribute.'['.Arr::get($customField, 'attributes.name').']'; | |
| 335 | - } | |
| 336 | - | |
| 337 | - $modifiedLabel = $parentAttribute.'['.Arr::get($customField, 'settings.label').']'; | |
| 338 | - | |
| 339 | - $customField['attributes']['name'] = $modifiedAttribute; | |
| 340 | - | |
| 341 | - $customField['settings']['label'] = $modifiedLabel; | |
| 342 | - | |
| 343 | - // Now, we'll replace the `conditional_logics` property | |
| 344 | - $customField['settings']['conditional_logics'] = $parentConditionalLogics; | |
| 345 | - | |
| 346 | - // Now that this field's properties are handled we can pass | |
| 347 | - // it to the extract field method to extract it's data. | |
| 348 | - $this->extractField($customField); | |
| 360 | + if ($shouldSetMaxValidation) { | |
| 361 | + $message = Helper::getGlobalDefaultMessage('max'); | |
| 362 | + if (!$message) { | |
| 363 | + $message = __('Validation fails for maximum value', 'fluentform'); | |
| 349 | 364 | } |
| 365 | + $this->result[$this->attribute]['rules']['max'] = [ | |
| 366 | + 'value' => $maxLength, | |
| 367 | + 'message' => $message, | |
| 368 | + ]; | |
| 350 | 369 | } |
| 351 | 370 | |
| 352 | 371 | return $this; |
| 353 | 372 | } |
| 354 | - | |
| 373 | + | |
| 374 | + /** | |
| 375 | + * Handle the child fields of complex form fields (address, name, repeater). | |
| 376 | + * | |
| 377 | + * This method processes fields that have sub-fields (like address field having | |
| 378 | + * address_line_1, city, state, etc.) and prepares them for shortcode generation. | |
| 379 | + * | |
| 380 | + * @return $this | |
| 381 | + */ | |
| 382 | + protected function handleCustomField() | |
| 383 | + { | |
| 384 | + // If this field is a custom field we'll assume it has it's child fields | |
| 385 | + // under the `fields` key. Then we are gonna modify those child fields' | |
| 386 | + // attribute `name`, `label` & `conditional_logics` properties using | |
| 387 | + // the parent field. The current implementation will modify those | |
| 388 | + // properties in a way so that we can use dot notation to access. | |
| 389 | + $customFields = Arr::get($this->field, 'fields'); | |
| 390 | + | |
| 391 | + if ($customFields) { | |
| 392 | + $parentAttribute = Arr::get($this->field, 'attributes.name'); | |
| 393 | + | |
| 394 | + $parentConditionalLogics = Arr::get($this->field, 'settings.conditional_logics', []); | |
| 395 | + | |
| 396 | + $isAddressOrNameField = in_array(Arr::get($this->field, 'element'), ['address', 'input_name']); | |
| 397 | + | |
| 398 | + $isRepeatField = Arr::get($this->field, 'element') === 'input_repeat' || Arr::get($this->field, 'element') == 'repeater_field'; | |
| 399 | + | |
| 400 | + // Allow plugins to modify custom fields before processing | |
| 401 | + $customFields = apply_filters('fluentform/extractor_parser_custom_fields', $customFields, $this->field); | |
| 402 | + | |
| 403 | + foreach ($customFields as $index => $customField) { | |
| 404 | + // If the current field is in fact `address` || `name` field | |
| 405 | + // then we have to only keep the enabled child fields | |
| 406 | + // by the user from the form editor settings. | |
| 407 | + if ($isAddressOrNameField) { | |
| 408 | + $subFieldName = Arr::get($customField, 'attributes.name'); | |
| 409 | + if (!Arr::get($customField, 'settings.visible', false) && !in_array($subFieldName, ['latitude', 'longitude'])) { | |
| 410 | + unset($customFields[$index]); | |
| 411 | + continue; | |
| 412 | + } | |
| 413 | + } | |
| 414 | + | |
| 415 | + // Depending on whether the parent field is a repeat field or not | |
| 416 | + // the modified attribute name of the child field will vary. | |
| 417 | + if ($isRepeatField) { | |
| 418 | + $modifiedAttribute = $parentAttribute.'['.$index.'].*'; | |
| 419 | + } else { | |
| 420 | + $modifiedAttribute = $parentAttribute.'['.Arr::get($customField, 'attributes.name').']'; | |
| 421 | + } | |
| 422 | + | |
| 423 | + $modifiedLabel = $parentAttribute.'['.Arr::get($customField, 'settings.label').']'; | |
| 424 | + | |
| 425 | + $customField['attributes']['name'] = $modifiedAttribute; | |
| 426 | + | |
| 427 | + $customField['settings']['label'] = $modifiedLabel; | |
| 428 | + | |
| 429 | + // Now, we'll replace the `conditional_logics` property | |
| 430 | + $customField['settings']['conditional_logics'] = $parentConditionalLogics; | |
| 431 | + | |
| 432 | + // Now that this field's properties are handled we can pass | |
| 433 | + // it to the extract field method to extract it's data. | |
| 434 | + $this->extractField($customField); | |
| 435 | + } | |
| 436 | + } | |
| 437 | + | |
| 438 | + return $this; | |
| 439 | + } | |
| 440 | + | |
| 355 | 441 | /** |
| 356 | 442 | * Set the raw field of the form field. |
| 357 | 443 | * |
| 358 | 444 | * @return $this |
| @@ -361,8 +447,8 @@ | ||
| 361 | 447 | { |
| 362 | 448 | if (in_array('raw', $this->with)) { |
| 363 | 449 | $this->result[$this->attribute]['raw'] = $this->field; |
| 364 | 450 | } |
| 365 | - | |
| 451 | + | |
| 366 | 452 | return $this; |
| 367 | 453 | } |
| 368 | 454 | } |