visualn-8.x-1.x-dev/modules/visualn_drawing/src/Plugin/Field/FieldWidget/VisualNFetcherWidget.php

modules/visualn_drawing/src/Plugin/Field/FieldWidget/VisualNFetcherWidget.php
<?php

namespace Drupal\visualn_drawing\Plugin\Field\FieldWidget;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Render\Element;

use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;

/**
 * Plugin implementation of the 'visualn_fetcher' widget.
 *
 * @FieldWidget(
 *   id = "visualn_fetcher",
 *   label = @Translation("VisualN fetcher"),
 *   field_types = {
 *     "visualn_fetcher"
 *   }
 * )
 */
class VisualNFetcherWidget extends WidgetBase {

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return [
      //'fetcher_id' => '',
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $elements = [];

    // Actually default drawing fetcher is already set at field default value configuration level.
    /*$elements['fetcher_id'] = [
      '#type' => 'select',
      '#title' => t('Fetcher plugin id'),
      '#options' => $options,
      '#description' => t('Default drawing fetcher plugin.'),
    ];*/

    return $elements;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    // @todo: add settings summary
    $summary = [];

    /*if (!empty($this->getSetting('fetcher_id'))) {
      // @todo: get label for the fetcher plugin
      $summary[] = t('Drawing fetcher: @fetcher_plugin_label', ['@fetcher_plugin_label' => $this->getSetting('fetcher_id')]);
    }*/

    return $summary;
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    // @todo: review the code, see VisualNResourceProviderWidget class

    $element['#type'] = 'fieldset';
    $element['#description'] = t('Select and configure drawing fetcher plugin to create a drawing. Each fetcher may have its specific configuration options set and drawing building logic implemented.');

    $item = $items[$delta];
    $fetcher_config = !empty($item->fetcher_config) ? unserialize($item->fetcher_config) : [];
    // @todo: use #field_parents key

    $fetchers_list = ['' => t('- Select drawing fetcher -')];

    // Get drawing fetchers plugins list
    // @todo: instantiate at class creation
    $definitions = \Drupal::service('plugin.manager.visualn.drawing_fetcher')->getDefinitions();
    foreach ($definitions as $definition) {
      // @todo: maybe check for required contexts
      $fetchers_list[$definition['id']] = $definition['label'];
    }


    // @todo: is this ok to get parents this way?
    //    if used in #process though, #parents key is already set
    $field_name = $this->fieldDefinition->getName();
    $parents = array_merge($element['#field_parents'], [$field_name, $delta]);
    $fetcher_id = $form_state->getValue(array_merge($parents, ['fetcher_id']));

    // @todo: how to check if the form is fresh
    // is null basically means that the form is fresh (maybe check the whole $form_state->getValues() to be sure?)
    // $fetcher_id can be empty string (in case of default choice) or NULL in case of fresh form
    if (is_null($fetcher_id)) {
      $fetcher_id = $item->fetcher_id ?: '';
    }

    $ajax_wrapper_id = $field_name . '-' . $delta . '-fetcher-config-ajax-wrapper';

    // select drawing fetcher plugin
    $element['fetcher_id'] = [
      '#type' => 'select',
      '#title' => t('Drawer fetcher plugin'),
      '#options' => $fetchers_list,
      '#default_value' => $fetcher_id,
      '#ajax' => [
        //'callback' => [get_called_class(), 'ajaxCallback'],
        'callback' => [$this, 'ajaxCallback'],
        'wrapper' => $ajax_wrapper_id,
      ],
      '#empty_value' => '',
    ];
    $element['fetcher_container'] = [
      '#prefix' => '<div id="' . $ajax_wrapper_id . '">',
      '#suffix' => '</div>',
      '#type' => 'container',
    ];

    $element['fetcher_container']['fetcher_config'] = ['#process' => [[$this, 'processFetcherConfigurationSubform']]];
    // @todo: $item is needed in the #process callback to access fetcher_config from field configuration,
    //    maybe there is a better way
    $element['fetcher_container']['fetcher_config']['#item'] = $item;

    // @todo: Set entity type and bundle for the fetcher_plugin since it may need the list of all its fields.

    // @todo: We can't pass the current reference to the entity because it doesn't always exist,
    //    e.g. when setting default value for the field in field settings.
    //    Actually it does (see the note below) but can it be used by plugins or should additional
    //    checks be done? Maybe kind of empty entity?
    // @todo: maybe pass entityType config entity

/*
    $field_definition = $this->fieldDefinition;
    if ($field_definition instanceof Drupal\Core\Field\BaseFieldDefinition) {
    }
*/

    // @note: It also works for configuring field form (see FieldItemList::getEntity())
    //   though entity values are all empty since there basically can't be any real entity
    //   it is a nice feature
    $entity = $items->getEntity();
    $entity_type = $entity->getEntityTypeId();
    $bundle = $entity->bundle();

    // @note: $field_definition::getTargetBundle() is empty for base entity fields since
    // they are connected to the entity, not bundle
    // though $field_definition::getTargetEntityTypeId() works
    //$entity_type = $field_definition->getTargetEntityTypeId();
    //$bundle = $field_definition->getTargetBundle();


    // @todo: these are not working with 'Default fetcher' field which is a base field
    //   defined in-code in entity definition.
    //$entity_type = $this->fieldDefinition->get('entity_type');
    //$bundle = $this->fieldDefinition->get('bundle');

    // @todo: maybe we can get this data in the #process callback directly from the $item object
    $element['fetcher_container']['fetcher_config']['#entity_type'] = $entity_type;
    $element['fetcher_container']['fetcher_config']['#bundle'] = $bundle;

    return $element;
  }


  // @todo: The code below should be almost the same as for VisualN block configuration form
  public function processFetcherConfigurationSubform(array $element, FormStateInterface $form_state, $form) {
    $item = $element['#item'];
    $entity_type = $element['#entity_type'];
    $bundle = $element['#bundle'];

    $configuration = [
      'fetcher_id' => $item->fetcher_id,
      'fetcher_config' => !empty($item->fetcher_config) ? unserialize($item->fetcher_config) : [],
    ];

    $fetcher_element_parents = array_slice($element['#parents'], 0, -2);
    $fetcher_id = $form_state->getValue(array_merge($fetcher_element_parents, ['fetcher_id']));
    // Whether fetcher_id is an empty string (which means changed to the Default option) or NULL (which means
    // that the form is fresh) there is nothing to attach for fetcher_config subform.
    if (!$fetcher_id) {
      return $element;
    }

    if ($fetcher_id == $configuration['fetcher_id']) {
      // @note: plugins are instantiated with default configuration to know about it
      //    but at configuration form rendering always the form_state values are (should be) used
      $fetcher_config = $configuration['fetcher_config'];
    }
    else {
      $fetcher_config = [];
    }

    // Basically this check is not needed
    if ($fetcher_id) {
      // fetcher plugin buildConfigurationForm() needs Subform:createForSubform() form_state
      $subform_state = SubformState::createForSubform($element, $form, $form_state);

      // instantiate fetcher plugin
      $visualNDrawingFetcherManager = \Drupal::service('plugin.manager.visualn.drawing_fetcher');
      //$fetcher_plugin = $this->visualNDrawingFetcherManager->createInstance($fetcher_id, $fetcher_config);
      $fetcher_plugin = $visualNDrawingFetcherManager->createInstance($fetcher_id, $fetcher_config);


      // Set "entity_type" and "bundle" contexts
      $context_entity_type = new Context(new ContextDefinition('string', NULL, TRUE), $entity_type);
      $fetcher_plugin->setContext('entity_type', $context_entity_type);

      $context_bundle = new Context(new ContextDefinition('string', NULL, TRUE), $bundle);
      $fetcher_plugin->setContext('bundle', $context_bundle);
      // @todo: see the note regarding setting context in VisualNResourceProviderItem class

      // attach fetcher configuration form
      // @todo: also fetcher_config_key may be added here as it is done for ResourceGenericDraweringFethcher
      //    and drawer_container_key.
      $element = $fetcher_plugin->buildConfigurationForm($element, $subform_state);

      // @todo: fetcher subform should be also validated (check other widgets/places as well)

      // change fetcher configuration form container to fieldset if not empty
      if (Element::children($element)) {
        $element['#type'] = 'fieldset';
        $element['#title'] = t('Fetcher settings');
      }

      // @todo: a $fetcher_container_key could be used here to avoid the case when two fetcher plugins
      //    have configuration forms with the same keys and one overrides another on changing selected
      //    fetcher in the fetcher select box. See ResourceGenericDrawerFetcher for how it is done
      //    for visualn_style_id and drawer config form select.
/*
      $element['fetcher_config'] = [];
      $element['fetcher_config'] += [
        '#parents' => array_merge($element['#parents'], ['fetcher_config']),
        '#array_parents' => array_merge($element['#array_parents'], ['fetcher_config']),
      ];
*/
/*
      $element[$drawer_container_key]['drawer_config'] = [];
      $element[$drawer_container_key]['drawer_config'] += [
        '#parents' => array_merge($element['#parents'], [$drawer_container_key, 'drawer_config']),
        '#array_parents' => array_merge($element['#array_parents'], [$drawer_container_key, 'drawer_config']),
      ];
*/
    }

    return $element;
  }


  /**
   * {@inheritdoc}
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    // @todo: serialize fetcher_config
    foreach ($values as &$value) {
      $fetcher_config = [];
      if (!empty($value['fetcher_container']['fetcher_config'])) {
        foreach ($value['fetcher_container']['fetcher_config'] as $fetcher_config_key => $fetcher_config_item) {
          $fetcher_config[$fetcher_config_key] = $fetcher_config_item;
        }
        // @todo: unset()
      }
      $value['fetcher_config'] = serialize($fetcher_config);
    }
    return $values;
  }


  /**
   * {@inheritdoc}
   *
   * @todo: Add into an interface or add description
   *
   * return drawerConfigForm via ajax at style change
   */
  public static function ajaxCallback(array $form, FormStateInterface $form_state, Request $request) {
    $triggering_element = $form_state->getTriggeringElement();
    $triggering_element_parents = array_slice($triggering_element['#array_parents'], 0, -1);
    $element = NestedArray::getValue($form, $triggering_element_parents);

    return $element['fetcher_container'];
  }

}

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

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