jsonapi_extras-8.x-3.16/src/Form/JsonapiResourceConfigForm.php

src/Form/JsonapiResourceConfigForm.php
<?php

namespace Drupal\jsonapi_extras\Form;

use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\jsonapi\ResourceType\ResourceType;
use Drupal\jsonapi_extras\Entity\JsonapiResourceConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Base form for jsonapi_resource_config.
 */
class JsonapiResourceConfigForm extends EntityForm {

  /**
   * The bundle information service.
   *
   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
   */
  protected $bundleInfo;

  /**
   * The JSON:API resource type repository.
   *
   * @var \Drupal\jsonapi\ResourceType\ResourceTypeRepository
   */
  protected $resourceTypeRepository;

  /**
   * The field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManager
   */
  protected $fieldManager;

  /**
   * The entity type repository.
   *
   * @var \Drupal\Core\Entity\EntityTypeRepositoryInterface
   */
  protected $entityTypeRepository;

  /**
   * The field enhancer manager.
   *
   * @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager
   */
  protected $enhancerManager;

  /**
   * The JSON:API extras config.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The current route match.
   *
   * @var \Symfony\Component\HttpFoundation\Request
   */
  protected $request;

  /**
   * The typed config manager.
   *
   * @var \Drupal\Core\Config\TypedConfigManagerInterface
   */
  protected $typedConfigManager;

  /**
   * A logger instance.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);

    $instance->bundleInfo = $container->get('entity_type.bundle.info');
    $instance->resourceTypeRepository = $container->get('jsonapi.resource_type.repository');
    $instance->fieldManager = $container->get('entity_field.manager');
    $instance->entityTypeRepository = $container->get('entity_type.repository');
    $instance->enhancerManager = $container->get('plugin.manager.resource_field_enhancer');
    $instance->config = $container->get('config.factory')->get('jsonapi_extras.settings');
    $instance->request = $container->get('request_stack')->getCurrentRequest();
    $instance->typedConfigManager = $container->get('config.typed');
    $instance->logger = $container->get('logger.channel.jsonapi_extras');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);
    // Disable caching on this form.
    $form_state->setCached(FALSE);

    $entity_type_id = $this->request->get('entity_type_id');
    $bundle = $this->request->get('bundle');

    /** @var \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $entity */
    $entity = $this->getEntity();
    $resource_id = $entity->get('id');
    // If we are editing an entity we don't want the Entity Type and Bundle
    // picker, that info is locked.
    if (!$entity_type_id || !$bundle) {
      if (!$resource_id) {
        // We can't build the form without an entity type and bundle.
        throw new \InvalidArgumentException('Unable to load entity type or bundle for the overrides form.');
      }
      [$entity_type_id, $bundle] = explode('--', $resource_id);
      $form['#title'] = $this->t('Edit %label resource config', ['%label' => $resource_id]);
    }

    if ($entity_type_id && $resource_type = $this->resourceTypeRepository->get($entity_type_id, $bundle)) {
      // Get the JSON:API resource type.
      $resource_config_id = sprintf('%s--%s', $entity_type_id, $bundle);
      $existing_entity = $this->entityTypeManager
        ->getStorage('jsonapi_resource_config')->load($resource_config_id);
      if ($existing_entity && $entity->isNew()) {
        $this->messenger()->addStatus($this->t('This override already exists, please edit it instead.'));
        return $form;
      }
      try {
        $fields_wrapper = $this->buildOverridesForm($resource_type, $entity);
        $form['bundle_wrapper']['fields_wrapper'] = $fields_wrapper;
      }
      catch (PluginNotFoundException $exception) {
        $this->logger->error($exception);
      }
      $form['id'] = ['#type' => 'hidden', '#value' => $resource_config_id];
    }

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    if (!method_exists($this->typedConfigManager, 'createFromNameAndData')) {
      // Versions of Drupal before 8.4 have poor support for constraints. In
      // those scenarios we don't validate the form submission.
      return;
    }
    $typed_config = $this->typedConfigManager
      ->createFromNameAndData($this->entity->id(), $this->entity->toArray());
    $constraints = $typed_config->validate();
    /** @var \Symfony\Component\Validator\ConstraintViolation $violation */
    foreach ($constraints as $violation) {
      $form_path = str_replace('.', '][', $violation->getPropertyPath());
      $form_state->setErrorByName($form_path, $violation->getMessage());
    }
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $resource_config = $this->entity;
    $status = $resource_config->save();
    $type = $resource_config->get('resourceType') ?? '';

    switch ($status) {
      case SAVED_NEW:
        $this->messenger()->addStatus($this->t('Created the %type JSON:API Resource overwrites.', [
          '%type' => $type,
        ]));
        break;

      default:
        $this->messenger()->addStatus($this->t('Saved the %type JSON:API Resource overwrites.', [
          '%type' => $type,
        ]));
    }
    $form_state->setRedirectUrl($resource_config->toUrl('collection'));
  }

  /**
   * Builds the part of the form that contains the overrides.
   *
   * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
   *   The resource type being overridden.
   * @param \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $entity
   *   The configuration entity backing this form.
   *
   * @return array
   *   The partial form.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function buildOverridesForm(ResourceType $resource_type, JsonapiResourceConfig $entity) {
    $entity_type_id = $resource_type->getEntityTypeId();
    /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type */
    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
    $bundle = $resource_type->getBundle();
    $field_names = $this->getAllFieldNames($entity_type, $bundle);

    $overrides_form['overrides']['entity'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Resource'),
      '#description' => $this->t('Override configuration for the resource entity.'),
      '#open' => !$entity->get('resourceType') || !$entity->get('path'),
      '#weight' => 0,
    ];

    $overrides_form['overrides']['entity']['disabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Disabled'),
      '#description' => $this->t('Check this if you want to disable this resource. Disabling a resource can have unexpected results when following relationships belonging to that resource.'),
      '#default_value' => $entity->get('disabled'),
    ];

    $resource_type_name = $entity->get('resourceType');
    if (!$resource_type_name) {
      $resource_type_name = sprintf('%s--%s', $entity_type_id, $bundle);
    }
    $overrides_form['overrides']['entity']['resourceType'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Resource Type'),
      '#description' => $this->t('Overrides the type of the resource. Example: Change "node--article" to "articles".'),
      '#default_value' => $resource_type_name,
      '#states' => [
        'visible' => [
          ':input[name="disabled"]' => ['checked' => FALSE],
        ],
      ],
    ];

    $path = $entity->get('path');
    if (!$path) {
      $path = sprintf('%s/%s', $entity_type_id, $bundle);
    }

    $prefix = $this->config->get('path_prefix');
    $overrides_form['overrides']['entity']['path'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Resource Path'),
      '#field_prefix' => sprintf('/%s/', $prefix),
      '#description' => $this->t('Overrides the path of the resource. Example: Use "articles" to change "/@prefix/node/article" to "/@prefix/articles".', [
        '@prefix' => $prefix,
      ]),
      '#default_value' => $path,
      '#required' => TRUE,
      '#states' => [
        'visible' => [
          ':input[name="disabled"]' => ['checked' => FALSE],
        ],
      ],
    ];

    $overrides_form['overrides']['fields'] = [
      '#type' => 'details',
      '#title' => $this->t('Fields'),
      '#open' => TRUE,
      '#weight' => 1,
    ];

    $markup = '';
    $markup .= '<dl>';
    $markup .= '<dt>' . $this->t('Disabled') . '</dt>';
    $markup .= '<dd>' . $this->t('Check this if you want to disable this field completely. Disabling required fields will cause problems when writing to the resource.') . '</dd>';
    $markup .= '<dt>' . $this->t('Alias') . '</dt>';
    $markup .= '<dd>' . $this->t('Overrides the field name with a custom name. Example: Change "field_tags" to "tags".') . '</dd>';
    $markup .= '<dt>' . $this->t('Enhancer') . '</dt>';
    $markup .= '<dd>' . $this->t('Select an enhancer to manipulate the public output coming in and out.') . '</dd>';
    $markup .= '</dl>';
    $overrides_form['overrides']['fields']['info'] = [
      '#markup' => $markup,
    ];

    $overrides_form['overrides']['fields']['resourceFields'] = [
      '#type' => 'table',
      '#theme' => 'expandable_rows_table',
      '#header' => [
        'disabled' => $this->t('Disabled'),
        'fieldName' => $this->t('Field name'),
        'publicName' => $this->t('Alias'),
        'advancedOptions' => '',
      ],
      '#empty' => $this->t('No fields available.'),
      '#states' => [
        'visible' => [
          ':input[name="disabled"]' => ['checked' => FALSE],
        ],
      ],
      '#attached' => [
        'library' => [
          'jsonapi_extras/expandable_rows_table',
        ],
      ],
    ];

    foreach ($field_names as $field_name) {
      try {
        $overrides = $this->buildOverridesField($field_name, $entity);
      }
      catch (PluginException $exception) {
        // Log exception and continue.
        $this->logger->error($exception);
        continue;
      }
      NestedArray::setValue(
        $overrides_form,
        ['overrides', 'fields', 'resourceFields', $field_name],
        $overrides
      );
    }

    return $overrides_form;
  }

  /**
   * {@inheritdoc}
   */
  public function buildEntity(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $entity */
    $entity = parent::buildEntity($form, $form_state);

    // Trim slashes from path.
    $path = trim($form_state->getValue('path'), '/');
    if (strlen($path) > 0) {
      $entity->set('path', $path);
    }

    return $entity;
  }

  /**
   * Builds the part of the form that overrides the field.
   *
   * @param string $field_name
   *   The field name of the field being overridden.
   * @param \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $entity
   *   The config entity backed by this form.
   *
   * @return array
   *   The partial form.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  protected function buildOverridesField($field_name, JsonapiResourceConfig $entity) {
    $rfs = $entity->get('resourceFields') ?: [];
    $resource_fields = array_filter($rfs, function (array $resource_field) use ($field_name) {
      return $resource_field['fieldName'] == $field_name;
    });
    $resource_field = array_shift($resource_fields);
    $overrides_form = [];
    $overrides_form['disabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Disabled'),
      '#title_display' => 'invisible',
      '#default_value' => empty($resource_field['disabled']) ? NULL : $resource_field['disabled'],
    ];
    $overrides_form['fieldName'] = [
      '#type' => 'hidden',
      '#value' => $field_name,
      '#prefix' => $field_name,
    ];
    $overrides_form['publicName'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Override Public Name'),
      '#title_display' => 'hidden',
      '#default_value' => empty($resource_field['publicName']) ? $field_name : $resource_field['publicName'],
      '#states' => [
        'visible' => [
          ':input[name="resourceFields[' . $field_name . '][disabled]"]' => [
            'checked' => FALSE,
          ],
        ],
      ],
    ];
    $overrides_form['advancedOptions'] = [
      '#markup' => t('Advanced'),
    ];

    $overrides_form['advancedOptionsIcon'] = [
      // Here we are just printing an arrow.
      '#markup' => '&#x21B3;',
    ];

    $overrides_form['enhancer_label'] = [
      '#markup' => $this->t('Enhancer for: %name', ['%name' => $field_name]),
    ];

    // Build the select field for the list of enhancers.
    $overrides_form['enhancer'] = [
      '#wrapper_attributes' => ['colspan' => 2],
      '#type' => 'fieldgroup',
      '#states' => [
        'visible' => [
          ':input[name="resourceFields[' . $field_name . '][disabled]"]' => [
            'checked' => FALSE,
          ],
        ],
      ],
    ];
    $options = array_reduce(
      $this->enhancerManager->getDefinitions(),
      function (array $carry, array $definition) {
        $carry[$definition['id']] = $definition['label'];
        return $carry;
      },
      ['' => $this->t('- None -')]
    );
    $id = empty($resource_field['enhancer']['id'])
      ? ''
      : $resource_field['enhancer']['id'];
    $overrides_form['enhancer']['id'] = [
      '#type' => 'select',
      '#options' => $options,
      '#ajax' => [
        'callback' => '::getEnhancerSettings',
        'wrapper' => $field_name . '-settings-wrapper',
      ],
      '#default_value' => $id,
    ];
    $overrides_form['enhancer']['settings'] = [
      '#type' => 'container',
      '#attributes' => ['id' => $field_name . '-settings-wrapper'],
    ];
    if (!empty($resource_field['enhancer']['id'])) {
      /** @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerInterface $enhancer */
      $enhancer = $this->enhancerManager
        ->createInstance($resource_field['enhancer']['id'], []);
      $overrides_form['enhancer']['settings'] += $enhancer
        ->getSettingsForm($resource_field);
    }
    return $overrides_form;
  }

  /**
   * AJAX callback to get the form settings for the enhancer for a field.
   *
   * @param array $form
   *   The reference to the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return mixed
   *   The specific form sub-tree in the form.
   */
  public static function getEnhancerSettings(array &$form, FormStateInterface $form_state) {
    // Find what is the field name that triggered the AJAX request.
    $user_input = $form_state->getUserInput();
    $parts = explode('[', $user_input['_triggering_element_name']);
    $field_name = rtrim($parts[1], ']');
    // Now return the sub-tree for the settings on the enhancer plugin.
    return $form['bundle_wrapper']['fields_wrapper']['overrides']['fields']['resourceFields'][$field_name]['enhancer']['settings'];
  }

  /**
   * {@inheritdoc}
   */
  protected function actionsElement(array $form, FormStateInterface $form_state) {
    // We want to display "Revert" instead of "Delete" on the Resource Config
    // Form.
    $element = parent::actionsElement($form, $form_state);
    if (isset($element['delete'])) {
      $element['delete']['#title'] = $this->t('Revert');
    }
    return $element;
  }

  /**
   * Gets all field names for a given entity type and bundle.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type for which to get all field names.
   * @param string $bundle
   *   The bundle for which to get all field names.
   *
   * @todo This is a copy of ResourceTypeRepository::getAllFieldNames. We can't
   * reuse that code because it's protected.
   *
   * @return string[]
   *   All field names.
   */
  protected function getAllFieldNames(EntityTypeInterface $entity_type, $bundle) {
    if (is_a($entity_type->getClass(), FieldableEntityInterface::class, TRUE)) {
      $field_definitions = $this->fieldManager->getFieldDefinitions(
        $entity_type->id(),
        $bundle
      );
      return array_keys($field_definitions);
    }
    elseif (is_a($entity_type->getClass(), ConfigEntityInterface::class, TRUE)) {
      // @todo Uncomment the first line, remove everything else once https://www.drupal.org/project/drupal/issues/2483407 lands.
      // return array_keys($entity_type->getPropertiesToExport());
      $export_properties = $entity_type->getPropertiesToExport();
      if ($export_properties !== NULL) {
        return array_keys($export_properties);
      }
      else {
        return ['id', 'type', 'uuid', '_core'];
      }
    }

    return [];
  }

}

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

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