Repository URL to install this package:
|
Version:
1.3.7 ▾
|
<?php
namespace Drupal\custom_forms\Controller;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\custom_forms\Entity\CustomFormSubmission;
use NumberFormatter;
use Symfony\Component\DependencyInjection\ContainerInterface;
class SubmissionValidationController extends ControllerBase {
protected $logger;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('logger.factory')
);
}
/**
* {@inheritdoc}
*/
public function __construct(LoggerChannelFactoryInterface $loggerChannelFactory) {
$this->logger = $loggerChannelFactory->get('Custom Forms');
}
/**
* Validate the submitted values based on the specified items.
*
* @param \Drupal\custom_forms\Entity\CustomFormSubmission $submission
* The submission containing the values to validate.
* @param \Drupal\custom_forms\CustomFormItem[] $items
* The array of form items (fields and groups) to validate.
* @param \NumberFormatter $numberFormatter
* The number formatter used for number comparison checks.
* @param array $errors
* An array of errors to allow for continued addition of errors when the
* function is called recursively.
*
* @return bool
* Returns TRUE if the items validate successfully, otherwise FALSE.
*/
public function validateItems(CustomFormSubmission $submission, array $items, NumberFormatter $numberFormatter, array &$errors = []) : bool {
$result = TRUE;
$values = $submission->getData();
if (empty($errors)) {
$errors = $submission->getErrors();
}
/** @var \Drupal\custom_forms\CustomFormItem $item */
foreach ($items as $item) {
// If the field is always hidden, we do not want to validate it.
if ($item->getSetting('visibility') === 'hidden') {
continue;
}
/** @var \Drupal\custom_forms\CustomFormItem[] $children */
$children = $item->getChildren();
$machine_name = $item->getMachineName();
$plugin = $item->getPlugin();
// Add error message if field is fully required.
$is_required = FALSE;
if ($plugin->isEmpty($values, $item) && (bool) $item->getSetting('required') === TRUE) {
$is_required = TRUE;
$errors[$machine_name]['required'] = $this->t('@field-name is required.', ['@field-name' => $item->getSetting('label')]);
}
$states = $item->getStates();
foreach ($states as $state) {
switch ($state['state']) {
case 'required':
// We only need to check for required if it isn't already marked
// as such.
if (!$is_required) {
$is_required = $this->checkConditions($values, $state['condition'], $state['operator'], $numberFormatter);
if ($is_required) {
if (!$item->isEmpty($values)) {
unset($errors[$machine_name]['required']);
}
else {
$errors[$machine_name]['required'] = $this->t('@field-name is required.', ['@field-name' => $item->getSetting('label')]);
}
}
}
break;
case 'optional':
$is_optional = $this->checkConditions($values, $state['condition'], $state['operator'], $numberFormatter);
if (!$is_optional) {
if (!empty($values[$machine_name])) {
unset($errors[$machine_name]['required']);
}
else {
$errors[$machine_name]['required'] = $this->t('@field-name is required.', ['@field-name' => $item->getSetting('label')]);
}
}
break;
case 'visible':
$is_visible = $this->checkConditions($values, $state['condition'], $state['operator'], $numberFormatter);
// If the item is not visible, don't show required validation
// errors.
if (!$is_visible) {
unset($errors[$machine_name]['required']);
}
break;
case 'hidden':
$is_hidden = $this->checkConditions($values, $state['condition'], $state['operator'], $numberFormatter);
// If the item is hidden, don't show required validation errors.
if ($is_hidden) {
unset($errors[$machine_name]['required']);
}
break;
}
}
// If the item has children, validate them as well.
if (!empty($children)) {
$result = $this->validateItems($submission, $children, $numberFormatter, $errors);
}
// If no errors at this point, call the item's own validate functions.
if ($item->getType() === 'field') {
try {
$item->getPlugin()->validate($item, $values, $errors);
}
catch (PluginException $e) {
$error_message = t('PluginException during field validation. Message = %message', [
'%message' => $e->getMessage(),
]);
$this->logger->error($error_message);
return FALSE;
}
}
}
$submission->setErrors($errors);
return $result;
}
/**
* Check if all conditions are met.
*
* @param array $values
* An array containing all submitted values to validate conditions against.
* @param array $conditions
* An array of all the conditions for the state.
* @param string $operator
* The operator that the conditions use.
* @param \NumberFormatter $numberFormatter
* The number formatter used for number comparison checks.
*
* @return bool
* Returns TRUE if all conditions are met.
*/
private function checkConditions(array $values, array $conditions, $operator, NumberFormatter $numberFormatter) : bool {
$valid_conditions = 0;
foreach ($conditions as $condition) {
// We cannot validate expanded or collapsed items, so we skip those
// triggers.
if (in_array($condition['trigger'], ['expanded', 'collapsed'])) {
$valid_conditions++;
continue;
}
switch ($condition['trigger']) {
case 'empty':
if (empty($values[$condition['selector']])) {
$valid_conditions++;
}
break;
case 'filled':
if (!empty($values[$condition['selector']])) {
$valid_conditions++;
}
break;
case 'checked':
if (
isset($values[$condition['selector']]) &&
(bool) $values[$condition['selector']] === TRUE
) {
$valid_conditions++;
}
break;
case 'unchecked':
if (
(
isset($values[$condition['selector']]) &&
(bool) $values[$condition['selector']] === FALSE
) ||
empty($values[$condition['selector']])
) {
$valid_conditions++;
}
break;
case 'value':
if (
isset($values[$condition['selector']]) &&
$values[$condition['selector']] === $condition['value']
) {
$valid_conditions++;
}
break;
case '!value':
if (
!isset($values[$condition['selector']]) ||
(
isset($values[$condition['selector']]) &&
$values[$condition['selector']] !== $condition['value']
)
) {
$valid_conditions++;
}
break;
case 'pattern':
if (isset($values[$condition['selector']])) {
$match = preg_match($condition['value'], $values[$condition['selector']]);
if (!empty($match)) {
$valid_conditions++;
}
}
break;
case '!pattern':
if (isset($values[$condition['selector']])) {
$match = preg_match($condition['value'], $values[$condition['selector']]);
if (empty($match)) {
$valid_conditions++;
}
}
break;
case 'less':
if (isset($values[$condition['selector']])) {
$field_value = $numberFormatter->parse($values[$condition['selector']]);
$condition_value = $numberFormatter->parse($values[$condition['value']]);
if (
$field_value !== FALSE &&
$condition_value !== FALSE &&
$field_value < $condition_value
) {
$valid_conditions++;
}
}
break;
case 'greater':
if (isset($values[$condition['selector']])) {
$field_value = $numberFormatter->parse($values[$condition['selector']]);
$condition_value = $numberFormatter->parse($values[$condition['value']]);
if (
$field_value !== FALSE &&
$condition_value !== FALSE &&
$field_value > $condition_value
) {
$valid_conditions++;
}
}
break;
}
}
// Depending on the operator we need to validate it a bit differently.
switch ($operator) {
case 'or':
// If using the OR operator, we just need more than 1 valid condition.
if ($valid_conditions > 0) {
return TRUE;
}
break;
case 'xor':
// If using the XOR operator, we only accept 1 valid condition,
// no more, no less.
if ($valid_conditions === 1) {
return TRUE;
}
break;
case 'and':
default:
// If using the AND operator, the valid conditions must match the
// total conditions.
if ($valid_conditions === count($conditions)) {
return TRUE;
}
break;
}
// If we get this far, we know the condition check failed and we can return
// FALSE to indicate as such.
return FALSE;
}
}