graphql_core_schema-1.0.x-dev/modules/graphql_form_schema/src/Plugin/GraphQL/DataProducer/FormResult.php

modules/graphql_form_schema/src/Plugin/GraphQL/DataProducer/FormResult.php
<?php

namespace Drupal\graphql_form_schema\Plugin\GraphQL\DataProducer;

use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\FormValidatorInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Return the form result.
 *
 * @DataProducer(
 *   id = "form_result",
 *   name = @Translation("Form result."),
 *   description = @Translation("Build an array of form elements."),
 *   produces = @ContextDefinition("any",
 *     label = @Translation("The form result.")
 *   ),
 *   consumes = {
 *     "form" = @ContextDefinition("any",
 *       label = @Translation("The form object."),
 *     ),
 *     "formState" = @ContextDefinition("any",
 *       label = @Translation("The FormState object."),
 *     ),
 *     "input" = @ContextDefinition("any",
 *       label = @Translation("The input object."),
 *       required = FALSE,
 *     ),
 *     "excludeTypes" = @ContextDefinition("boolean",
 *       label = @Translation("Element types to exclude."),
 *       required = FALSE,
 *     ),
 *   }
 * )
 */
class FormResult extends DataProducerPluginBase implements ContainerFactoryPluginInterface {

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected FormBuilderInterface $formBuilder;

  /**
   * The form validator.
   *
   * @var \Drupal\Core\Form\FormValidator
   */
  protected FormValidatorInterface $formValidator;

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\Messenger
   */
  protected MessengerInterface $messenger;

  /**
   * The renderer service.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected RendererInterface $renderer;

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $pluginId,
    $pluginDefinition,
  ) {
    return new static(
      $configuration,
      $pluginId,
      $pluginDefinition,
      $container->get('form_builder'),
      $container->get('form_validator'),
      $container->get('messenger'),
      $container->get('renderer'),
    );
  }

  /**
   * The constructor.
   *
   * @param array $configuration
   *   The plugin configuration array.
   * @param string $pluginId
   *   The plugin id.
   * @param mixed $pluginDefinition
   *   The plugin definition.
   * @param \Drupal\Core\Form\FormBuilderInterface $formBuilder
   *   The form builder.
   * @param \Drupal\Core\Form\FormValidatorInterface $formValidator
   *   The form validator.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer service.
   */
  public function __construct(
    array $configuration,
    $pluginId,
    $pluginDefinition,
    FormBuilderInterface $formBuilder,
    FormValidatorInterface $formValidator,
    MessengerInterface $messenger,
    RendererInterface $renderer,
  ) {
    parent::__construct($configuration, $pluginId, $pluginDefinition);
    $this->formBuilder = $formBuilder;
    $this->formValidator = $formValidator;
    $this->messenger = $messenger;
    $this->renderer = $renderer;
  }

  /**
   * Handles form validation and submitting.
   *
   * Resolves the form elements and errors.
   *
   * @param \Drupal\Core\Form\FormInterface $formObject
   *   The form object.
   * @param \Drupal\Core\Form\FormState $formState
   *   The form state object.
   * @param array $input
   *   The input object for this form.
   * @param array $excludeTypes
   *   Array of form element types to exclude, e.g. "textfield".
   * @param \Drupal\graphql\GraphQL\Execution\FieldContext $field
   *   The field context.
   *
   * @return array
   *   The form elements.
   */
  public function resolve(
    FormInterface $formObject,
    FormStateInterface $formState,
    $input,
    $excludeTypes,
    FieldContext $field,
  ) {
    $context = new RenderContext();
    $formState->setProgrammedBypassAccessCheck(FALSE);

    $result = $this->renderer->executeInRenderContext($context, function () use ($formObject, $formState, $input, $excludeTypes) {
      $validate = $input['validate'] ?? FALSE;
      $submit = $input['submit'] ?? FALSE;
      $values = $input['values'] ?? [];

      // Build form initially without values.
      $form = $this->formBuilder->buildForm($formObject, $formState);

      // If an input is provided and validation is enabled, set the values on
      // FormState.
      if (!empty($input) && ($validate || $submit)) {
        // This is crucial and makes sure we can validate and submit
        // programmatically.
        $formState->setProgrammed();

        // Convert the input array of element items to an array of form values.
        $formValues = $this->buildUserInput($values);

        // Set values.
        $formState->setValues($formValues);

        // Make sure the user input reflects the provided values.
        $formState->setUserInput($formState->getValues());
      }

      // Submit form if requested.
      if ($submit) {
        // SubmitHandlers are normally add in builder process that happens before submission.
        $formState->setSubmitHandlers(['::submitForm', '::save']);
        $this->formBuilder->submitForm($formObject, $formState);
      }

      // Rebuild the form to reflect the provided input values.
      $form = $this->formBuilder->buildForm($formObject, $formState);

      // Collect form elements.
      $elements = [];
      $this->getElements($form, $elements, $excludeTypes);

      // Collect errors belonging to form elements.
      $elementErrors = [];

      foreach ($elements as $key => $element) {
        // Check if this element has an error.
        $elementError = $formState->getError($element);
        if ($elementError) {
          // Get the element error as a string.
          $elementError = (string) $elementError;
          // Custom array key which is used by the field resolver to resolve this
          // error.
          $elements[$key]['#collected_error'] = $elementError;
          $elementErrors[] = $elementError;
        }
      }

      // Get all form errors and convert them to strings.
      $errors = array_map('strval', $formState->getErrors());

      // Collect all errors that don't belong to a form element.
      $remainingErrors = [];
      foreach ($errors as $error) {
        if (!in_array($error, $elementErrors)) {
          $remainingErrors[] = $error;
        }
      }

      // Purge all messages generated during form handling.
      $this->messenger->deleteAll();

      return [
        'items' => $elements,
        // @todo errors not attached to an element will not be returned in GraphQL,
        // but may prevent the entity from being saved.
        // 'errors' => $remainingErrors,
        'errors' => $errors,
        'isSubmitted' => $formState->isSubmitted(),
        'entity' => $this->getEntity($formObject),
      ];
    });

    $field->addCacheableDependency($context);
    return $result;
  }

  /**
   * Get the entity relating to the form.
   *
   * @param \Drupal\Core\Form\FormInterface $formObject
   *   The form object.
   *
   * @return \Drupal\Core\Entity\EntityInterface|null
   *   The resulting entity.
   */
  private function getEntity(FormInterface $formObject): ?EntityInterface {
    if ($formObject instanceof EntityFormInterface) {
      $entity = $formObject->getEntity();
      if ($entity && !$entity->isNew()) {
        return $entity;
      }
    }

    return NULL;
  }

  /**
   * Convert the incoming array of CommerceFormInput objects to a structured array of form values.
   *
   * @param array $inputElements
   *   The array of CommerceFormInput items.
   *
   * @return array
   *   The resulting form values.
   */
  private function buildUserInput(array $inputElements) {
    // Collect all query values.
    $parts = [];
    foreach ($inputElements as $item) {
      $value = $item['value'] ?? NULL;
      if (is_array($value)) {
        foreach ($value as $itemValue) {
          $parts[] = $item['name'] . '=' . $this->mapUserInputValue($itemValue);
        }
      }
      else {
        $parts[] = $item['name'] . '=' . $this->mapUserInputValue($value);
      }
    }

    // Create the query string.
    $str = implode('&', $parts);

    // Parse the query string to an array.
    $values = [];
    parse_str($str, $values);

    foreach ($values as $key => $value) {
      // Replace empty strings with NULL.
      if ($values[$key] === '') {
        $values[$key] = NULL;
      }
      elseif (isset($values[$key]['value'])) {
        if ($values[$key]['value'] === '') {
          $values[$key]['value'] = NULL;
        }
      }
    }

    return $values;
  }

  /**
   * Encode the value for building the user input.
   *
   * @param mixed $value
   *   The value.
   *
   * @return string|null
   *   The mapped value.
   */
  private function mapUserInputValue($value): ?string {
    if ($value === FALSE || $value === NULL) {
      return $value;
    }
    elseif ($value === TRUE) {
      return '1';
    }

    $result = urlencode($value);
    return $result;
  }

  /**
   * Collect all form elements relevant to data input.
   *
   * @param array $form
   *   The form render array.
   * @param array $elements
   *   Array to collect all elements.
   * @param array $excludeTypes
   *   Array of form element types to exclude.
   */
  protected function getElements(array $form, array &$elements, array $excludeTypes) {
    $children = Element::children($form);
    $ignoreTypes = [
      'container',
      'field_multiple_value_form',
    ];

    foreach ($children as $child) {
      $element = $form[$child];
      $name = $element['#name'] ?? NULL;
      $type = $element['#type'] ?? NULL;
      if ($name && $type !== 'submit' && !in_array($type, $excludeTypes) && !in_array($type, $ignoreTypes)) {
        $elements[] = $element;
      }
      $this->getElements($element, $elements, $excludeTypes);
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc