455 lines
13 KiB
PHP
455 lines
13 KiB
PHP
<?php
|
|
|
|
namespace FluentForm\App\Services\Parser;
|
|
|
|
use FluentForm\App\Helpers\Helper;
|
|
use FluentForm\App\Services\ConditionAssesor;
|
|
use FluentForm\Framework\Helpers\ArrayHelper as Arr;
|
|
|
|
class Extractor
|
|
{
|
|
/**
|
|
* The form field settings.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $fields;
|
|
|
|
/**
|
|
* The properties that need to be extracted.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $with;
|
|
|
|
/**
|
|
* The supported form input types defined for Fluent Forms.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $inputTypes;
|
|
|
|
/**
|
|
* The extracted result.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $result = [];
|
|
|
|
/**
|
|
* The current form field that's being set when we loop
|
|
* through all the form fields in the looper method.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $field = [];
|
|
|
|
/**
|
|
* The current field attribute that's being set when we loop
|
|
* through all the form fields in the looper method.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $attribute;
|
|
|
|
/**
|
|
* Extractor constructor.
|
|
*
|
|
* @param array $fields
|
|
* @param array $with
|
|
* @param array $inputTypes
|
|
*/
|
|
public function __construct($fields, $with, $inputTypes)
|
|
{
|
|
$this->fields = $fields;
|
|
$this->with = $with;
|
|
$this->inputTypes = $inputTypes;
|
|
}
|
|
|
|
/**
|
|
* The extractor initializer for getting the extracted data.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function extract()
|
|
{
|
|
$this->looper($this->fields);
|
|
|
|
return $this->result;
|
|
}
|
|
|
|
/**
|
|
* The recursive looper method to loop each
|
|
* of the fields and extract it's data.
|
|
*
|
|
* @param array $fields
|
|
*/
|
|
protected function looper($fields = [])
|
|
{
|
|
foreach ($fields as $field) {
|
|
// If the field is a Container (collection of other fields)
|
|
// then we will recursively call this function to resolve.
|
|
if (Arr::get($field, 'element') === 'container') {
|
|
$columns = Arr::get($field, 'columns', []);
|
|
foreach ($columns as $item) {
|
|
$itemFields = Arr::get($item, 'fields', []);
|
|
$this->looper($itemFields);
|
|
}
|
|
}
|
|
|
|
// Now the field is supposed to be a flat field.
|
|
// We can extract the desired keys as we want.
|
|
else {
|
|
$element = Arr::get($field, 'element');
|
|
if ($element && in_array($element, $this->inputTypes)) {
|
|
$this->extractField($field);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The extractor initializer for getting the extracted data.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function extractEssentials($formData)
|
|
{
|
|
$this->looperEssential($formData, $this->fields);
|
|
|
|
return $this->result;
|
|
}
|
|
|
|
/**
|
|
* The recursive looper method to loop each
|
|
* of the fields and extract it's data.
|
|
*
|
|
* @param array $fields
|
|
*/
|
|
protected function looperEssential($formData, $fields = [])
|
|
{
|
|
foreach ($fields as $field) {
|
|
$field['conditionals'] = Arr::get($field, 'settings.conditional_logics', []);
|
|
|
|
$matched = ConditionAssesor::evaluate($field, $formData);
|
|
|
|
if (!$matched) {
|
|
continue;
|
|
}
|
|
|
|
// If the field is a Container (collection of other fields)
|
|
// then we will recursively call this function to resolve.
|
|
if (Arr::get($field, 'element') === 'container') {
|
|
$columns = Arr::get($field, 'columns', []);
|
|
foreach ($columns as $item) {
|
|
$itemFields = Arr::get($item, 'fields', []);
|
|
$this->looperEssential($formData, $itemFields);
|
|
}
|
|
}
|
|
|
|
// Now the field is supposed to be a flat field.
|
|
// We can extract the desired keys as we want.
|
|
else {
|
|
$element = Arr::get($field, 'element');
|
|
if ($element && in_array($element, $this->inputTypes)) {
|
|
$this->extractField($field);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract the form field.
|
|
*
|
|
* @param array $field
|
|
* @return $this
|
|
*/
|
|
protected function extractField($field)
|
|
{
|
|
// Before starting the extraction we'll set the current
|
|
// field and it's attribute name at first. And then we
|
|
// will proceed to extract the field settings that
|
|
// the developer demanded using $with initially.
|
|
$this->prepareIteration($field, Arr::get($field, 'attributes.name'))
|
|
->setElement()
|
|
->setAdminLabel()
|
|
->setLabel()
|
|
->setOptions()
|
|
->setAdvancedOptions()
|
|
->setSettings()
|
|
->setRaw()
|
|
->setAttributes()
|
|
->setValidations()
|
|
->handleCustomField();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the field and attribute of the current iteration when
|
|
* we loop through the form fields using the looper method.
|
|
*
|
|
* @param array $field
|
|
* @param string $attribute
|
|
* @return $this
|
|
*/
|
|
protected function prepareIteration($field, $attribute)
|
|
{
|
|
$this->field = $field;
|
|
|
|
$this->attribute = $attribute;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the element of the form field.
|
|
*
|
|
* @param array $field
|
|
* @param string $attributeName
|
|
* @return $this
|
|
*/
|
|
protected function setElement()
|
|
{
|
|
$this->result[$this->attribute]['element'] = Arr::get($this->field, 'element', '');
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the label of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setLabel()
|
|
{
|
|
if (in_array('label', $this->with)) {
|
|
$this->result[$this->attribute]['label'] = Arr::get($this->field, 'settings.label', '');
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the admin label of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setAdminLabel()
|
|
{
|
|
if (in_array('admin_label', $this->with)) {
|
|
$adminLabel = Arr::get($this->field, 'settings.admin_field_label') ?:
|
|
Arr::get($this->field, 'settings.label') ?:
|
|
Arr::get($this->field, 'element');
|
|
|
|
$this->result[$this->attribute]['admin_label'] = $adminLabel;
|
|
}
|
|
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the options of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setOptions()
|
|
{
|
|
if (in_array('options', $this->with)) {
|
|
$options = Arr::get($this->field, 'options', []);
|
|
|
|
if(!$options) {
|
|
$newOptions = \FluentForm\App\Helpers\Helper::flattenAdvancedOptions(
|
|
Arr::get($this->field, 'settings.advanced_options', [])
|
|
);
|
|
|
|
if(
|
|
!$newOptions
|
|
&& Arr::get($this->field,'element') == 'multi_payment_component'
|
|
&& Arr::get($this->field,'attributes.type') != 'single'
|
|
) {
|
|
$pricingOptions = Arr::get($this->field, 'settings.pricing_options', []);
|
|
foreach ($pricingOptions as $pricingOption) {
|
|
$newOptions[] = [
|
|
'value' => $pricingOption['label'],
|
|
'label' => $pricingOption['label']
|
|
];
|
|
}
|
|
}
|
|
|
|
$options = [];
|
|
if($newOptions) {
|
|
foreach ($newOptions as $option) {
|
|
$value = sanitize_text_field($option['value']);
|
|
$options[$value] = sanitize_text_field($option['label']);
|
|
}
|
|
}
|
|
}
|
|
$this->result[$this->attribute]['options'] = $options;
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the advanced options of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setAdvancedOptions()
|
|
{
|
|
if (in_array('advanced_options', $this->with)) {
|
|
$this->result[$this->attribute]['advanced_options'] = Arr::get($this->field, 'settings.advanced_options', []);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
protected function setSettings()
|
|
{
|
|
if (in_array('settings', $this->with)) {
|
|
$this->result[$this->attribute]['settings'] = Arr::get($this->field, 'settings', []);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the attributes of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setAttributes()
|
|
{
|
|
if (in_array('attributes', $this->with)) {
|
|
$this->result[$this->attribute]['attributes'] = Arr::get($this->field, 'attributes');
|
|
}
|
|
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the validation rules and conditions of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setValidations()
|
|
{
|
|
if (in_array('rules', $this->with)) {
|
|
$this->result[$this->attribute]['rules'] = Arr::get(
|
|
$this->field,
|
|
'settings.validation_rules'
|
|
);
|
|
|
|
$this->handleMaxLengthValidation();
|
|
|
|
$this->result[$this->attribute]['conditionals'] = Arr::get(
|
|
$this->field,
|
|
'settings.conditional_logics'
|
|
);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
protected function handleMaxLengthValidation()
|
|
{
|
|
$maxLength = Arr::get($this->field, 'attributes.maxlength');
|
|
$fieldHasMaxValidation = Arr::get($this->field, 'settings.validation_rules.max');
|
|
$shouldSetMaxValidation = $maxLength && !$fieldHasMaxValidation;
|
|
|
|
if ($shouldSetMaxValidation) {
|
|
$message = Helper::getGlobalDefaultMessage('max');
|
|
if (!$message) {
|
|
$message = __('Validation fails for maximum value', 'fluentform');
|
|
}
|
|
$this->result[$this->attribute]['rules']['max'] = [
|
|
'value' => $maxLength,
|
|
'message' => $message,
|
|
];
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Handle the child fields of complex form fields (address, name, repeater).
|
|
*
|
|
* This method processes fields that have sub-fields (like address field having
|
|
* address_line_1, city, state, etc.) and prepares them for shortcode generation.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function handleCustomField()
|
|
{
|
|
// If this field is a custom field we'll assume it has it's child fields
|
|
// under the `fields` key. Then we are gonna modify those child fields'
|
|
// attribute `name`, `label` & `conditional_logics` properties using
|
|
// the parent field. The current implementation will modify those
|
|
// properties in a way so that we can use dot notation to access.
|
|
$customFields = Arr::get($this->field, 'fields');
|
|
|
|
if ($customFields) {
|
|
$parentAttribute = Arr::get($this->field, 'attributes.name');
|
|
|
|
$parentConditionalLogics = Arr::get($this->field, 'settings.conditional_logics', []);
|
|
|
|
$isAddressOrNameField = in_array(Arr::get($this->field, 'element'), ['address', 'input_name']);
|
|
|
|
$isRepeatField = Arr::get($this->field, 'element') === 'input_repeat' || Arr::get($this->field, 'element') == 'repeater_field';
|
|
|
|
// Allow plugins to modify custom fields before processing
|
|
$customFields = apply_filters('fluentform/extractor_parser_custom_fields', $customFields, $this->field);
|
|
|
|
foreach ($customFields as $index => $customField) {
|
|
// If the current field is in fact `address` || `name` field
|
|
// then we have to only keep the enabled child fields
|
|
// by the user from the form editor settings.
|
|
if ($isAddressOrNameField) {
|
|
$subFieldName = Arr::get($customField, 'attributes.name');
|
|
if (!Arr::get($customField, 'settings.visible', false) && !in_array($subFieldName, ['latitude', 'longitude'])) {
|
|
unset($customFields[$index]);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Depending on whether the parent field is a repeat field or not
|
|
// the modified attribute name of the child field will vary.
|
|
if ($isRepeatField) {
|
|
$modifiedAttribute = $parentAttribute.'['.$index.'].*';
|
|
} else {
|
|
$modifiedAttribute = $parentAttribute.'['.Arr::get($customField, 'attributes.name').']';
|
|
}
|
|
|
|
$modifiedLabel = $parentAttribute.'['.Arr::get($customField, 'settings.label').']';
|
|
|
|
$customField['attributes']['name'] = $modifiedAttribute;
|
|
|
|
$customField['settings']['label'] = $modifiedLabel;
|
|
|
|
// Now, we'll replace the `conditional_logics` property
|
|
$customField['settings']['conditional_logics'] = $parentConditionalLogics;
|
|
|
|
// Now that this field's properties are handled we can pass
|
|
// it to the extract field method to extract it's data.
|
|
$this->extractField($customField);
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the raw field of the form field.
|
|
*
|
|
* @return $this
|
|
*/
|
|
protected function setRaw()
|
|
{
|
|
if (in_array('raw', $this->with)) {
|
|
$this->result[$this->attribute]['raw'] = $this->field;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
}
|