[ 'comparators' => [ 'equals' => [ 'expected_type' => 'string', ], 'does_not_equal' => [ 'expected_type' => 'string', ], 'contains' => [ 'expected_type' => 'string', ], 'does_not_contain' => [ 'expected_type' => 'string', ], 'starts_with' => [ 'expected_type' => 'string', ], 'ends_with' => [ 'expected_type' => 'string', ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'content_length_equals' => [ 'expected_type' => 'number', ], 'content_length_does_not_equal' => [ 'expected_type' => 'number', ], 'content_length_greater_than' => [ 'expected_type' => 'number', ], 'content_length_greater_than_or_equal_to' => [ 'expected_type' => 'number', ], 'content_length_less_than' => [ 'expected_type' => 'number', ], 'content_length_less_than_or_equal_to' => [ 'expected_type' => 'number', ], ], ], 'url' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'string', ], 'does_not_equal' => [ 'expected_type' => 'string', ], 'contains' => [ 'expected_type' => 'string', ], 'does_not_contain' => [ 'expected_type' => 'string', ], 'starts_with' => [ 'expected_type' => 'string', ], 'ends_with' => [ 'expected_type' => 'string', ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'content_length_equals' => [ 'expected_type' => 'number', ], 'content_length_does_not_equal' => [ 'expected_type' => 'number', ], 'content_length_greater_than' => [ 'expected_type' => 'number', ], 'content_length_greater_than_or_equal_to' => [ 'expected_type' => 'number', ], 'content_length_less_than' => [ 'expected_type' => 'number', ], 'content_length_less_than_or_equal_to' => [ 'expected_type' => 'number', ], ], ], 'email' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'string', ], 'does_not_equal' => [ 'expected_type' => 'string', ], 'contains' => [ 'expected_type' => 'string', ], 'does_not_contain' => [ 'expected_type' => 'string', ], 'starts_with' => [ 'expected_type' => 'string', ], 'ends_with' => [ 'expected_type' => 'string', ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'content_length_equals' => [ 'expected_type' => 'number', ], 'content_length_does_not_equal' => [ 'expected_type' => 'number', ], 'content_length_greater_than' => [ 'expected_type' => 'number', ], 'content_length_greater_than_or_equal_to' => [ 'expected_type' => 'number', ], 'content_length_less_than' => [ 'expected_type' => 'number', ], 'content_length_less_than_or_equal_to' => [ 'expected_type' => 'number', ], ], ], 'phone_number' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'string', ], 'does_not_equal' => [ 'expected_type' => 'string', ], 'contains' => [ 'expected_type' => 'string', ], 'does_not_contain' => [ 'expected_type' => 'string', ], 'starts_with' => [ 'expected_type' => 'string', ], 'ends_with' => [ 'expected_type' => 'string', ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'content_length_equals' => [ 'expected_type' => 'number', ], 'content_length_does_not_equal' => [ 'expected_type' => 'number', ], 'content_length_greater_than' => [ 'expected_type' => 'number', ], 'content_length_greater_than_or_equal_to' => [ 'expected_type' => 'number', ], 'content_length_less_than' => [ 'expected_type' => 'number', ], 'content_length_less_than_or_equal_to' => [ 'expected_type' => 'number', ], ], ], 'number' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'number', ], 'does_not_equal' => [ 'expected_type' => 'number', ], 'greater_than' => [ 'expected_type' => 'number', ], 'less_than' => [ 'expected_type' => 'number', ], 'greater_than_or_equal_to' => [ 'expected_type' => 'number', ], 'less_than_or_equal_to' => [ 'expected_type' => 'number', ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'content_length_equals' => [ 'expected_type' => 'number', ], 'content_length_does_not_equal' => [ 'expected_type' => 'number', ], 'content_length_greater_than' => [ 'expected_type' => 'number', ], 'content_length_greater_than_or_equal_to' => [ 'expected_type' => 'number', ], 'content_length_less_than' => [ 'expected_type' => 'number', ], 'content_length_less_than_or_equal_to' => [ 'expected_type' => 'number', ], ], ], 'checkbox' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'does_not_equal' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], ], ], 'select' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'string', ], 'does_not_equal' => [ 'expected_type' => 'string', ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], ], ], 'multi_select' => [ 'comparators' => [ 'contains' => [ 'expected_type' => ['object', 'string'], 'format' => [ 'type' => 'uuid', ], ], 'does_not_contain' => [ 'expected_type' => ['object', 'string'], 'format' => [ 'type' => 'uuid', ], ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], ], ], 'date' => [ 'comparators' => [ 'equals' => [ 'expected_type' => 'string', 'format' => [ 'type' => 'date', ], ], 'before' => [ 'expected_type' => 'string', 'format' => [ 'type' => 'date', ], ], 'after' => [ 'expected_type' => 'string', 'format' => [ 'type' => 'date', ], ], 'on_or_before' => [ 'expected_type' => 'string', 'format' => [ 'type' => 'date', ], ], 'on_or_after' => [ 'expected_type' => 'string', 'format' => [ 'type' => 'date', ], ], 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'past_week' => [ 'expected_type' => 'object', 'format' => [ 'type' => 'empty', 'values' => '{}', ], ], 'past_month' => [ 'expected_type' => 'object', 'format' => [ 'type' => 'empty', 'values' => '{}', ], ], 'past_year' => [ 'expected_type' => 'object', 'format' => [ 'type' => 'empty', 'values' => '{}', ], ], 'next_week' => [ 'expected_type' => 'object', 'format' => [ 'type' => 'empty', 'values' => '{}', ], ], 'next_month' => [ 'expected_type' => 'object', 'format' => [ 'type' => 'empty', 'values' => '{}', ], ], 'next_year' => [ 'expected_type' => 'object', 'format' => [ 'type' => 'empty', 'values' => '{}', ], ], ], ], 'files' => [ 'comparators' => [ 'is_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], 'is_not_empty' => [ 'expected_type' => 'boolean', 'format' => [ 'type' => 'enum', 'values' => [true], ], ], ], ], ]; private $isConditionCorrect = true; private $isActionCorrect = true; private $conditionErrors = []; private $field = []; private $data = []; private function checkBaseCondition($condition) { if (! isset($condition['value'])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'missing condition body'; return; } if (! isset($condition['value']['property_meta'])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'missing condition property'; return; } if (! isset($condition['value']['property_meta']['type'])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'missing condition property type'; return; } if (! isset($condition['value']['operator'])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'missing condition operator'; return; } if (! isset($condition['value']['value'])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'missing condition value'; return; } $typeField = $condition['value']['property_meta']['type']; $operator = $condition['value']['operator']; $value = $condition['value']['value']; if (! isset(self::CONDITION_MAPPING[$typeField])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'configuration not found for condition type'; return; } if (! isset(self::CONDITION_MAPPING[$typeField]['comparators'][$operator])) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'configuration not found for condition operator'; return; } $type = self::CONDITION_MAPPING[$typeField]['comparators'][$operator]['expected_type']; if (is_array($type)) { $foundCorrectType = false; foreach ($type as $subtype) { if ($this->valueHasCorrectType($subtype, $value)) { $foundCorrectType = true; } } if (! $foundCorrectType) { $this->isConditionCorrect = false; } } else { if (! $this->valueHasCorrectType($type, $value)) { $this->isConditionCorrect = false; $this->conditionErrors[] = 'wrong type of condition value'; } } } private function valueHasCorrectType($type, $value) { if ( ($type === 'string' && gettype($value) !== 'string') || ($type === 'boolean' && ! is_bool($value)) || ($type === 'number' && ! is_numeric($value)) || ($type === 'object' && ! is_array($value)) ) { return false; } return true; } private function checkConditions($conditions) { if (array_key_exists('operatorIdentifier', $conditions)) { if (($conditions['operatorIdentifier'] !== 'and') && ($conditions['operatorIdentifier'] !== 'or')) { $this->conditionErrors[] = 'missing operator'; $this->isConditionCorrect = false; return; } if (isset($conditions['operatorIdentifier']['children'])) { $this->conditionErrors[] = 'extra condition'; $this->isConditionCorrect = false; return; } if (! is_array($conditions['children'])) { $this->conditionErrors[] = 'wrong sub-condition type'; $this->isConditionCorrect = false; return; } foreach ($conditions['children'] as &$child) { $this->checkConditions($child); } } elseif (isset($conditions['identifier'])) { $this->checkBaseCondition($conditions); } } private function checkActions($actions) { if (is_array($actions) && count($actions) > 0) { foreach ($actions as $val) { if (! in_array($val, static::ACTIONS_VALUES) || (in_array($this->field['type'], ['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image']) && ! in_array($val, ['hide-block', 'show-block'])) || (isset($this->field['hidden']) && $this->field['hidden'] && ! in_array($val, ['show-block', 'require-answer'])) || (isset($this->field['required']) && $this->field['required'] && ! in_array($val, ['make-it-optional', 'hide-block', 'disable-block'])) || (isset($this->field['disabled']) && $this->field['disabled'] && ! in_array($val, ['enable-block', 'require-answer', 'make-it-optional'])) ) { $this->isActionCorrect = false; break; } } } else { $this->isActionCorrect = false; } } /** * Determine if the validation rule passes. * * @param string $attribute * @param mixed $value * @return bool */ public function passes($attribute, $value) { $this->setProperty($attribute); if (isset($value['conditions'])) { $this->checkConditions($value['conditions']); $this->checkActions($value['actions'] ?? null); } return $this->isConditionCorrect && $this->isActionCorrect; } /** * Get the validation error message. */ public function message() { $message = null; if (! $this->isConditionCorrect) { $message = 'The logic conditions for '.$this->field['name'].' are not complete.'; } elseif (! $this->isActionCorrect) { $message = 'The logic actions for '.$this->field['name'].' are not valid.'; } if (count($this->conditionErrors) > 0) { return $message.' Error detail(s): '.implode(', ', $this->conditionErrors); } return $message; } /** * Set the data under validation. * * @param array $data * @return $this */ public function setData($data) { $this->data = $data; $this->isConditionCorrect = true; $this->isActionCorrect = true; $this->conditionErrors = []; return $this; } private function setProperty(string $attributeKey) { $attributeKey = Str::of($attributeKey)->replace('.logic', '')->toString(); $this->field = \Arr::get($this->data, $attributeKey); } }