You are here

fapi_validation.module in Form API Validation 8

Form API validation module

File

fapi_validation.module
View source
<?php

/**
 * @file Form API validation module
 */
use Drupal\Core\Form\FormStateInterface;

// Load filters and validtors
module_load_include('inc', 'fapi_validation', 'src/fapi_filters/fapi_filters');
module_load_include('inc', 'fapi_validation', 'src/fapi_validators/fapi_validators');

/**
 * Implements hook_element_info_alter().
 *
 * PURPOSE: Add fapi validation element processing to all form elements.
 */
function fapi_validation_element_info_alter(array &$info) {

  // Loop through all form element types and only hit input variants.
  foreach ($info as $type_name => $type_info) {
    if (!empty($type_info['#input'])) {

      // Processes the form element on form creation.
      $info[$type_name]['#process'][] = 'fapi_validation_element_process';
    }
  }
}

/**
 * Process element validators and filters.
 *
 * Allows both #validators and #filters values. Run on form rendering. Only adds filters and validators on form submission
 * if the values have been provided. Saves us from appending a check to every single item on submission.
 */
function fapi_validation_element_process($element, FormStateInterface $form_state) {
  if (!empty($element['#filters'])) {

    // @TODO: Determine how nescessary this step is. Seems unnescessarily complex given the If below doesn't have it.
    // Check if element validate is already empty, and if so make variable for merging in values an empty array.
    $element_validate = empty($element['#element_validate']) ? array() : $element['#element_validate'];
    $element['#element_validate'] = array_merge(array(
      'fapi_validate_element_filter',
    ), $element_validate);
  }
  if (!empty($element['#validators'])) {
    $element['#element_validate'][] = 'fapi_validate_element_validate';
  }
  return $element;
}

/**
 * Run element filter callbacks.
 */
function fapi_validate_element_filter(&$element, FormStateInterface $form_state) {
  fapi_validation_filters_execute($element, $form_state);
}

/**
 * Run element validation callbacks.
 */
function fapi_validate_element_validate(&$element, FormStateInterface $form_state) {
  fapi_validation_validators_execute($element, $form_state);
}

/**
 *  Function for executing all filters
 */
function fapi_validation_filters_execute(&$element, FormStateInterface $form_state) {
  $available_filters = _fapi_validation_data('filters');

  // If the element has no value, there is nothing to filter.
  if (!isset($element['#value'])) {
    return;
  }

  // Loop through all passed in filters from form element.
  foreach ($element['#filters'] as $element_filter) {

    // Make sure filter passed is available as a recognized filter by FAPI validation.
    if ($element_filter && !empty($available_filters[$element_filter])) {

      // Adjust element value based on calling the appropriate data filter callback.
      $element['#value'] = $available_filters[$element_filter]['callback']($element['#value']);
      $form_state
        ->setValue($element['#name'], $element['#value']);
    }
    else {
      drupal_set_message(t('Form item filter array with wrong structure on %field.', array(
        '%field' => $element['#name'],
      )), 'error');
    }
  }
}

/**
 *  Function for executing all validators
 */
function fapi_validation_validators_execute(&$element, FormStateInterface $form_state) {

  // If element is empty and not required, by pass rule validation
  if (!$element['#required'] && empty($element['#value'])) {
    return;
  }

  //establish errors array
  $errors = array();

  // Load Validators.
  $validators = _fapi_validation_data('validators');

  // Loop through all validators on the field
  foreach ($element['#validators'] as $validator) {

    // Set error message and error callback all to NULL.
    $error_message = $error_callback = NULL;

    // Some Validators can be an array.
    if (is_array($validator)) {
      if (!isset($validators['validator'])) {
        drupal_set_message(t('Validator array with wrong structure on %field.', array(
          '%field' => $element['#name'],
        )), 'error');
        continue;
      }
      if (isset($validator['error'])) {
        $error_message = $validator['error'];
      }
      if (isset($validator['error_callback'])) {
        $error_callback = $validator['error_callback'];
      }
      $validator = $validator['validator'];
    }

    // Break rule into various aspects, incase it's all passed as one string?
    preg_match('/^(.*?)(\\[(.*)\\])?$/', $validator, $rs);
    $validator = $rs[1];

    // Check if rule exists
    if (!isset($validators[$validator])) {
      drupal_set_message(t('Validator %validator not found!', array(
        '%validator' => $validator,
      )), 'error');
      continue;
    }

    //Get element value
    $params = array(
      $element['#value'],
    );

    // Parsing parameters from validator input
    // @TODO: Review that this is the best way to do this... I'd prefer this was passed as an array value.
    if (isset($rs[3])) {
      if ($validator == 'regexp') {
        $params[] = array(
          $rs[3],
        );
      }
      else {
        $params[] = preg_split('/ *, */', $rs[3]);
      }
    }

    // Call the validator function, pass it the $element and $form_state values.
    $validator_success = call_user_func_array($validators[$validator]['callback'], array(
      $element['#value'],
      $form_state,
    ));
    if (!$validator_success) {
      if (!is_null($error_callback)) {
        $error_params = array(
          $validator,
          $params,
          $element,
          $form_state,
        );
        $errors[] = call_user_func_array($error_callback, $error_params);
      }
      else {
        $error = is_null($error_message) ? $validators[$validator]['error_msg'] : $error_message;
        $error = str_replace('%field', $element['#title'], $error
          ->render());
        $errors[] = t($error, array(
          '%field' => $element['#title'],
        ));
      }
    }
  }
  if (!empty($errors)) {
    $form_state
      ->setError($element, implode(' ', $errors));
  }
}

/**
 * Helper function that finds all invocations of hooks.
 *
 * @param string $validation_type
 * @return mixed
 */
function _fapi_validation_data($validation_type) {
  static $data = array();
  $fapi_validation_type_hook_implementations = Drupal::moduleHandler()
    ->invokeAll('fapi_validation_' . $validation_type);
  if (isset($fapi_validation_type_hook_implementations) && is_array($fapi_validation_type_hook_implementations)) {
    foreach ($fapi_validation_type_hook_implementations as $validation_name => $validation_callback) {
      $data[$validation_type][$validation_name] = $validation_callback;
    }
  }
  return $data[$validation_type];
}

Functions

Namesort descending Description
fapi_validate_element_filter Run element filter callbacks.
fapi_validate_element_validate Run element validation callbacks.
fapi_validation_element_info_alter Implements hook_element_info_alter().
fapi_validation_element_process Process element validators and filters.
fapi_validation_filters_execute Function for executing all filters
fapi_validation_validators_execute Function for executing all validators
_fapi_validation_data Helper function that finds all invocations of hooks.