datafield-1.x-dev/src/Plugin/Field/FieldType/DataFieldItem.php

src/Plugin/Field/FieldType/DataFieldItem.php
<?php

namespace Drupal\datafield\Plugin\Field\FieldType;

use Drupal\Core\Field\Attribute\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Plugin implementation of the 'data_field' field type.
 */
#[FieldType(
  id: "data_field",
  label: new TranslatableMarkup("Data Field"),
  description: new TranslatableMarkup("Data Field."),
  default_widget: "data_field_table_widget",
  default_formatter: "data_field_table_formatter",
)]
class DataFieldItem extends FieldItemBase {

  public const DATETIME_STORAGE_TIMEZONE = 'UTC';

  public const DATETIME_DATETIME_STORAGE_FORMAT = 'Y-m-d\TH:i:s';

  public const DATETIME_DATE_STORAGE_FORMAT = 'Y-m-d';

  /**
   * {@inheritdoc}
   */
  public static function defaultStorageSettings(): array {
    // Need to have at least one item by default because the table is created
    // before the user gets a chance to customize and will throw an Exception
    // if there isn't at least one column defined.
    return [
      'columns' => [
        'value' => [
          'name' => 'value',
          'max_length' => 255,
          'size' => 'normal',
          'type' => 'string',
          'unsigned' => FALSE,
          'precision' => 10,
          'scale' => 2,
          'datetime_type' => 'date',
        ],
      ],
    ] + parent::defaultStorageSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data): array {
    $settings = $this->getSettings();
    $fieldDefinition = $this->getFieldDefinition();
    $isNew = $fieldDefinition->isNew();
    $request_stack = \Drupal::service('request_stack');
    $post = $request_stack->getCurrentRequest()->request->all();
    if (!empty($post["field_storage"]["subform"]["settings"])) {
      $settings["columns"] = $post["field_storage"]["subform"]["settings"]["storage"];
    }
    if ($isNew) {
      $form["cardinality_container"]["cardinality"]["#default_value"] = -1;
      $settings["columns"] = array_values($settings["columns"]);
    }
    // Add a new item if there aren't any, or we're rebuilding.
    if ($form_state->get('add') || count($settings['columns']) == 0) {
      $firstCol = current($settings['columns']);
      if (!empty($firstCol['name'])) {
        $firstCol['name'] .= count($settings['columns']);
      }
      $settings['columns'][] = $firstCol;
      $form_state->set('add', NULL);
    }
    $element = [
      '#tree' => TRUE,
      'columns' => [
        '#type' => 'value',
        '#value' => $settings['columns'],
      ],
      'storage' => [
        '#type' => 'table',
        '#header' => [
          $this->t('Machine-readable name'),
          $this->t('Type'),
          $this->t('Maximum length'),
          $this->t('Unsigned'),
          $this->t('Size'),
          $this->t('Precision'),
          $this->t('Scale'),
          $this->t('Date type'),
          '',
        ],
      ],
      'actions' => [
        '#type' => 'actions',
      ],
    ];

    $this->buildStorageTable($element, $settings, $form_state, $has_data);

    if (!$has_data) {
      $element['actions']['add'] = [
        '#type' => 'submit',
        '#value' => $this->t('Add another'),
        '#submit' => [get_class($this) . '::addSubmit'],
        // @todo copy setting.
        '#states' => [
          'visible' => [
            'select[data-id="datafield-settings-clone"]' => ['value' => ''],
          ],
        ],
      ];
    }

    $form_state->setCached(FALSE);
    return $element;
  }

  /**
   * {@inheritDoc}
   */
  protected function buildStorageTable(array &$element, array $settings, FormStateInterface $form_state, bool $has_data) {
    $default_settings = self::defaultStorageSettings()['columns']['value'];
    $isNew = $this->getFieldDefinition()->isNew();
    $i = 0;
    foreach ($settings['columns'] as $item) {
      $name = $item['name'] ?? 'value';
      $storageKeys = array_keys($settings["storage"] ?? $settings["columns"]);
      if (!empty($storageKeys[$i]) && !empty($settings["storage"][$storageKeys[$i]])) {
        $name = $settings["storage"][$storageKeys[$i]]["name"];
      }
      if ($i === $form_state->get('remove')) {
        $form_state->set('remove', NULL);
        // @todo it must remove columns in database.
        if (!empty($settings["columns"])) {
          if (isset($settings["columns"][$name])) {
            unset($settings["columns"][$name]);
            $this->getFieldDefinition()->setSetting('columns', $settings["columns"]);
          }
          else {
            foreach ($settings["columns"] as $index => $col) {
              if ($col['name'] == $name) {
                unset($settings["columns"][$index]);
                break;
              }
            }
          }
        }
        if (!empty($settings["field_settings"][$name])) {
          unset($settings["field_settings"][$name]);
          $this->getFieldDefinition()->setSetting('field_settings', $settings["field_settings"]);
        }
        if (!$isNew) {
          $this->getFieldDefinition()->save();
        }
        if (!empty($element["columns"]["#value"][$name])) {
          unset($element["columns"]["#value"][$name]);
        }
        if (!empty($element["columns"]["#value"][$name])) {
          unset($element["columns"]["#value"][$name]);
        }
        if (!empty($element["columns"]["#value"][$i])) {
          unset($element["columns"]["#value"][$i]);
        }
        if (!empty($element["storage"][$i])) {
          unset($element["storage"][$i]);
        }
        continue;
      }
      $parents = ['field_storage', 'subform', 'settings', 'storage', $name];
      $this->buildStorageRow($element['storage'][$i], $parents, $item, $default_settings, $has_data);
      $element['storage'][$i]['remove'] = [
        '#type' => 'submit',
        '#parents' => [...$parents, 'remove'],
        '#value' => $this->t('Remove'),
        '#submit' => [get_class($this) . '::removeSubmit'],
        '#name' => 'remove:' . $i,
        '#delta' => $i,
        '#disabled' => $has_data,
      ];
      $i++;
    }
    if ($i == 1) {
      $element['storage'][0]['remove']['#access'] = FALSE;
    }
  }

  /**
   * {@inheritDoc}
   */
  protected function buildStorageRow(array|null &$row, array $parents, mixed $item, mixed $default_settings, bool $has_data) {
    $fieldName = $this->getFieldDefinition()->getName();
    $lengthField = strlen($fieldName);
    $name = end($parents);
    $row['name'] = [
      '#type' => 'machine_name',
      '#description' => $this->t('A unique machine-readable name containing only letters, numbers, or underscores. This will be used in the column name on the field table in the database.'),
      '#default_value' => $name,
      '#parents' => [...$parents, 'name'],
      '#disabled' => $has_data,
      '#maxlength' => 63 - $lengthField,
      '#required' => TRUE,
      '#title_display' => 'invisible',
      '#attributes' => [
        'pattern' => '[a-z_0-9]+',
      ],
      '#machine_name' => [
        'exists' => [$this, 'machineNameExists'],
        'standalone' => TRUE,
      ],
    ];

    $row['type'] = [
      '#type' => 'select',
      '#parents' => [...$parents, 'type'],
      '#default_value' => $item['type'] ?? 'string',
      '#disabled' => $has_data,
      '#required' => TRUE,
      '#options' => $this->subfieldTypes(),
    ];

    $row['max_length'] = [
      '#type' => 'number',
      '#parents' => [...$parents, 'max_length'],
      '#description' => $this->t('The maximum length of the subfield in characters.'),
      '#default_value' => $item['max_length'] ?? $default_settings["max_length"],
      '#disabled' => $has_data,
      '#min' => 1,
      '#states' => [
        'visible' => [
          ":input[name*='[storage][$name][type]']" => [
            ['value' => 'string'],
            ['value' => 'char'],
            ['value' => 'varchar'],
          ],
        ],
      ],
    ];

    $row['unsigned'] = [
      '#type' => 'checkbox',
      '#parents' => [...$parents, 'unsigned'],
      '#title' => $this->t('Unsigned'),
      '#default_value' => $item['unsigned'] ?? $default_settings['unsigned'],
      '#disabled' => $has_data,
      '#states' => [
        'visible' => [
          ":input[name*='[storage][$name][type]']" => [
            ['value' => 'integer'],
            ['value' => 'float'],
            ['value' => 'numeric'],
          ],
        ],
      ],
    ];

    $row['size'] = [
      '#type' => 'select',
      '#parents' => [...$parents, 'size'],
      '#default_value' => $item['size'] ?? $default_settings['size'],
      '#disabled' => $has_data,
      '#options' => [
        'normal' => $this->t('Normal'),
        'big' => $this->t('Big'),
        'medium' => $this->t('Medium'),
        'small' => $this->t('Small'),
        'tiny' => $this->t('Tiny'),
      ],
      '#states' => [
        'visible' => [
          ":input[name*='[storage][$name][type]']" => [
            ['value' => 'integer'],
            ['value' => 'serial'],
            ['value' => 'float'],
            ['value' => 'text'],
            ['value' => 'blob'],
          ],
        ],
      ],
    ];

    $row['precision'] = [
      '#type' => 'number',
      '#parents' => [...$parents, 'precision'],
      '#description' => $this->t('The total number of digits to store in the database, including those to the right of the decimal.'),
      '#default_value' => $item['precision'] ?? $default_settings['precision'],
      '#disabled' => $has_data,
      '#min' => 10,
      '#max' => 32,
      '#states' => [
        'visible' => [
          ":input[name*='[storage][$name][type]']" => [
            ['value' => 'numeric'],
            ['value' => 'float'],
          ],
        ],
      ],
    ];

    $row['scale'] = [
      '#type' => 'number',
      '#parents' => [...$parents, 'scale'],
      '#description' => $this->t('The number of digits to the right of the decimal.'),
      '#default_value' => $item['scale'] ?? $default_settings['scale'],
      '#disabled' => $has_data,
      '#min' => 0,
      '#max' => 10,
      '#states' => [
        'visible' => [":input[name*='[storage][$name][type]']" => ['value' => 'numeric']],
      ],
    ];

    $row['datetime_type'] = [
      '#type' => 'radios',
      '#parents' => [...$parents, 'datetime_type'],
      '#description' => $this->t('Choose the type of date to create.'),
      '#default_value' => $item['datetime_type'] ?? $default_settings['datetime_type'],
      '#disabled' => $has_data,
      '#options' => [
        'datetime' => $this->t('Date and time'),
        'date' => $this->t('Date'),
        'time' => $this->t('Time'),
        'year' => $this->t('Year'),
        'month' => $this->t('Month'),
        'week' => $this->t('Week'),
        'timestamp' => $this->t('Timestamp'),
      ],
      '#states' => [
        'visible' => [
          ":input[name*='[storage][$name][type]']" => [
            ['value' => 'datetime_iso8601'],
            ['value' => 'date'],
          ],
        ],
      ],
    ];
  }

  /**
   * Submit handler for the StorageConfigEditForm.
   *
   * This handler is added in data field. Module since it has to be placed
   * directly on the submit button (which we don't have access to in our
   * ::storageSettingsForm() method above).
   */
  public static function submitStorageConfigEditForm(array &$form, FormStateInterface $form_state) {
    // Re-key our column settings and overwrite the values in form_state so that
    // we have clean settings saved to the db.
    $columns = [];
    $parents = ['field_storage', 'subform', 'settings', 'storage'];
    $items = $form_state->getValue($parents) ?? [];
    foreach ($items as $item) {
      $columns[$item['name']] = $item;
      unset($columns[$item['name']]['remove']);
    }
    self::cleanSettings($columns);
    $form_state->setValue(['field_storage', 'subform', 'settings', 'columns'], $columns);
    $form_state->setValue(['field_storage', 'subform', 'settings', 'storage'], NULL);

    // Reset the field storage config property - it will be recalculated
    // when accessed via the property definition getter.
    // @see Drupal\field\Entity\FieldStorageConfig::getPropertyDefinitions()
    // If we don't do this, an exception is thrown during the table update that
    // is very difficult to recover from since the original field tables have
    // already been removed at that point.
    $field_storage_config = $form_state->getBuildInfo()['callback_object']->getEntity();
    $field_storage_config->set('propertyDefinitions', NULL);
  }

  /**
   * {@inheritdoc}
   */
  public static function cleanSettings(&$settings) {
    foreach ($settings as &$setting) {
      switch ($setting['type']) {
        case 'date':
        case 'datetime_iso8601':
          unset($setting['size']);
          unset($setting['scale']);
          unset($setting['unsigned']);
          unset($setting['precision']);
          unset($setting['max_length']);
          break;

        case 'float':
        case 'numeric':
          unset($setting['datetime_type']);
          break;

        case 'file':
        case 'boolean':
        case 'integer':
        case 'entity_reference':
          unset($setting['scale']);
          unset($setting['precision']);
          unset($setting['datetime_type']);
          break;

        case 'uri':
        case 'blob':
        case 'json':
        case 'text':
        case 'email':
        case 'serial':
        case 'string':
        case 'telephone':
          unset($setting['scale']);
          unset($setting['unsigned']);
          unset($setting['precision']);
          unset($setting['datetime_type']);
          break;
      }
    }
  }

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

  /**
   * {@inheritdoc}
   */
  public function fieldSettingsForm(array $form, FormStateInterface $form_state): array {
    $settings = $this->getSettings();
    if ($form_state->isRebuilding()) {
      $formSettings = $form_state->getValue('settings') ?? $form_state->getUserInput()['settings'];
      $field_settings = $formSettings['field_settings'] ?? [];
      $settings['field_settings'] = $field_settings;
    }
    else {
      $field_settings = $this->getSetting('field_settings');
    }

    // Remove unnecessary data from $settings.
    unset($settings["field_settings"]);

    $types = static::subfieldTypes();
    $element = [
      '#type' => 'fieldset',
      '#title' => $this->t('Sub-field settings'),
    ];
    $pluginService = \Drupal::service('plugin.manager.data_field.type');
    $fieldName = $this->getFieldDefinition()->getName();
    $entityType = $this->getEntity()->getEntityTypeId();
    $subfields = $settings["storage"] ?? $settings["columns"];
    $request_stack = \Drupal::service('request_stack');
    $post = $request_stack->getCurrentRequest()->request->all();
    if (!empty($post["field_storage"]["subform"]["settings"])) {
      $subfields = [];
      foreach ($post["field_storage"]["subform"]["settings"]["storage"] as $item) {
        if (!empty($item['name'])) {
          $subfields[$item['name']] = $item;
        }
      }
    }

    foreach ($subfields as $subfield => $item) {
      $type = $item['type'];
      $plugin = $pluginService->createInstance($type);
      $title = $subfield;

      // Update title with custom label, if available.
      $title = !empty($field_settings[$subfield]['label']) ? $field_settings[$subfield]['label'] : $title;
      $title = ucfirst(str_replace('_', ' ', $title));
      $title .= ' - ' . $types[$type];

      if (empty($field_settings[$subfield])) {
        $field_settings[$subfield] = [];
      }
      // Update field settings with common properties.
      $field_settings[$subfield] += [
        'machine_name' => $subfield,
        'field_name' => $fieldName,
        'entity_type' => $entityType,
      ];

      $element['field_settings'][$subfield] = [
        '#type' => 'details',
        '#title' => $title,
        '#open' => FALSE,
        '#tree' => TRUE,
      ] + $plugin->fieldSettingsForm($field_settings[$subfield], $item);
    }

    return $element;
  }

  /**
   * {@inheritdoc}
   *
   * @todo Find a way to disable constraints for default field values.
   */
  public function getConstraints(): array {
    $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
    $constraints = parent::getConstraints();
    $settings = $this->getSettings();
    $subConstraints = [];
    $pluginService = \Drupal::service('plugin.manager.data_field.type');
    foreach ($this->getSetting('columns') as $subfield => $column) {
      $subfield_type = $column['type'];
      $field_settings = [];
      if (!empty($settings['field_settings'][$subfield])) {
        $field_settings += $settings['field_settings'][$subfield];
      }
      $field_settings += $column;
      $plugin = $pluginService->createInstance($subfield_type);
      $subConstraints[$subfield] = $plugin->getConstraints($field_settings);
    }
    $constraints[] = $constraint_manager->create('ComplexData', $subConstraints);
    return $constraints;
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition): array {
    $pluginService = \Drupal::service('plugin.manager.data_field.type');
    // Determine the source of subfields data.
    $settings = $field_definition->getSettings();
    $subfields = $field_definition->getSetting('columns');
    $request_stack = \Drupal::service('request_stack');
    $post = $request_stack->getCurrentRequest()->request->all();
    if (!empty($post["field_storage"]["subform"]["settings"]["storage"])) {
      $subfields = [];
      foreach ($post["field_storage"]["subform"]["settings"]["storage"] as $item) {
        if (!empty($item['name'])) {
          $subfields[$item['name']] = $item;
        }
      }
    }
    // Process subfields to generate schema.
    $columns = [];
    foreach ($subfields as $subfield => $item) {
      // Use the provided name or the default subfield name.
      $subfield = !empty($item['name']) ? $item['name'] : $subfield;
      // Check and Add new colum if it is not exists.
      if (!empty($settings["columns"]) && empty($settings['columns'][$subfield])) {
        $settings['columns'][$subfield] = $item;
        $field_definition->setSettings($settings);
      }
      // Create an instance of the data field plugin.
      $type = $item['type'];
      $plugin = $pluginService->createInstance($type);
      // Generate schema for the subfield.
      $schema = $plugin->schema($item);
      // Add a description to the subfield value column.
      $schema['columns']['value']['description'] = ucfirst($subfield) . ' subfield value.';
      // Add the subfield to the columns array.
      $columns[$subfield] = $schema['columns']['value'];
    }

    return ['columns' => $columns];
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties = [];
    $pluginService = \Drupal::service('plugin.manager.data_field.type');
    $originColumns = $columns = $field_definition->getSetting('columns');
    $request_stack = \Drupal::service('request_stack');
    $post = $request_stack->getCurrentRequest()->request->all();
    // Beware [storage] Only for datafield.
    if (!empty($post["field_storage"]["subform"]["settings"]["storage"]) && !empty($post["default_value_input"])) {
      $fieldName = array_key_first($post["default_value_input"]);
      $field_name = $field_definition->getName();
      if ($field_name == $fieldName) {
        $columns = $post["field_storage"]["subform"]["settings"]["storage"];
      }
    }
    $updateSchemas = FALSE;
    foreach ($columns as $colName => &$subfield) {
      $type = $subfield['type'] ?? 'string';
      $plugin = $pluginService->createInstance($type);
      $name = $subfield['name'] ?? '';
      if (!empty($name)) {
        $properties[$name] = $plugin->propertyDefinitions(
          $subfield +
          [
            'field_definition' => $field_definition,
          ]
        );
      }
      $subfield = $plugin->schema($subfield)['columns']['value'];
      $subfield['name'] = $name;
      if ($colName != $name || !isset($originColumns[$name])) {
        $updateSchemas = TRUE;
      }
    }
    // Update schema if column names have changed.
    if ($updateSchemas) {
      self::updateSchema($field_definition, $columns, $originColumns);
    }
    return $properties;
  }

  /**
   * Drupal 10.2 check field schema when we update a subfield.
   *
   * It must have a column added to the schema before checking.
   *
   * {@inheritdoc}
   */
  public static function updateSchema(FieldStorageDefinitionInterface $field_definition, $columns, $originColumns) {
    if (!empty($columns)) {
      $entity_type_id = $field_definition->getTargetEntityTypeId();
      $field_name = $field_definition->getName();
      $schema = \Drupal::database()->schema();
      $entityType = \Drupal::entityTypeManager()->getStorage($entity_type_id);
      if (!method_exists($entityType, 'getTableMapping')) {
        return;
      }
      $table_mapping = $entityType->getTableMapping([$field_name => $field_definition]);
      $table_names = $table_mapping->getDedicatedTableNames();
      foreach ($columns as $colName => $spec) {
        // Update schema name colum.
        $name = $spec['name'] ?? '';
        if (empty($name)) {
          continue;
        }
        $column_name = $field_name . '_' . $name;
        $column_name_origin = $field_name . '_' . $colName;
        foreach ($table_names as $table_name) {
          if ($schema->tableExists($table_name) && $spec) {
            if ($colName != $name && $schema->fieldExists($table_name, $column_name_origin)) {
              $schema->changeField($table_name, $column_name_origin, $column_name, $spec);
            }
            elseif (!$schema->fieldExists($table_name, $column_name)) {
              $schema->addField($table_name, $column_name, $spec);
            }
          }
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty(): bool {
    $settings = $this->getSettings();
    if (empty($settings["columns"])) {
      return FALSE;
    }
    $getValue = $this->getValue();
    foreach ($settings["columns"] as $subfield => $item) {
      $value = $getValue[$subfield] ?? NULL;
      $type = $item['type'] ?? NULL;
      if ($type === 'boolean') {
        if ($value == 1) {
          return FALSE;
        }
        continue;
      }
      if (!empty($getValue[$subfield]) && $item["type"] == 'uri' && is_array($getValue[$subfield])) {
        $value = $getValue[$subfield]['url'] ?? NULL;
      }
      if (in_array($item['type'], ['entity_reference', 'file'])) {
        if (is_array($value) || is_object($value)) {
          if (!empty($value)) {
            return FALSE;
          }
          continue;
        }
        if (is_numeric($value)) {
          return FALSE;
        }
        continue;
      }
      if ($value !== NULL && $value !== '') {
        return FALSE;
      }
    }
    return TRUE;
  }

  /**
   * Returns available subfield storage types.
   */
  public static function subfieldTypes(): array {
    $types = \Drupal::service('plugin.manager.data_field.type');
    $plugin_definitions = $types->getDefinitions();
    $result = [];
    array_walk($plugin_definitions, function ($object) use (&$result) {
      $result[$object["id"]] = $object["label"];
    });
    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public static function fieldSettingsToConfigData(array $settings): array {
    foreach ($settings['field_settings'] as $subfield => $item) {
      if (empty($item['allowed_values'])) {
        continue;
      }
      $structured_values = [];
      if (is_string($item['allowed_values'])) {
        $explode = explode("\r", str_replace("\n", '', $item['allowed_values']));
        $item['allowed_values'] = $explode;
        if (count($explode) > 1) {
          $item['allowed_values'] = [];
          foreach ($explode as $val) {
            $extract = explode('|', $val);
            $value = $extract[0];
            $label = !empty($extract[1]) ? $extract[1] : $extract[0];
            $item['allowed_values'][$value] = $label;
          }
        }
      }
      foreach ($item['allowed_values'] as $value => $label) {
        $structured_values[] = [
          'value' => $value,
          'label' => str_replace('_', ' ', $label),
        ];
      }
      $settings['field_settings'][$subfield]['allowed_values'] = $structured_values;
    }
    return $settings;
  }

  /**
   * {@inheritdoc}
   */
  public static function fieldSettingsFromConfigData(array $settings): array {
    if (empty($settings['field_settings'])) {
      return $settings;
    }
    foreach ($settings['field_settings'] as $field => $subfield) {
      if (empty($subfield['allowed_values'])) {
        continue;
      }
      $structured_values = [];
      foreach ($subfield['allowed_values'] as $item) {
        $structured_values[$item['value']] = $item['label'];
      }
      $settings["field_settings"][$field]["allowed_values"] = $structured_values;
    }
    return $settings;
  }

  /**
   * Checks if list option is allowed for a given sub-field type.
   */
  public static function isListAllowed(string $subfield_type): bool {
    $list_types = array_keys(self::subfieldTypes());
    $notAllowed = [
      'boolean', 'text', 'datetime_iso8601', 'date', 'json', 'blob', 'file', 'entity_reference',
    ];
    return in_array($subfield_type, $list_types) && !in_array($subfield_type, $notAllowed);
  }

  /**
   * {@inheritdoc}
   */
  public static function generateSampleValue(FieldDefinitionInterface $field_definition): array {
    $settings = $field_definition->getSettings();

    $data = [];
    $pluginService = \Drupal::service('plugin.manager.data_field.type');
    foreach ($settings['columns'] as $subfield => $item) {

      // If allowed values are limited pick one of them from field settings.
      if (!empty($settings['field_settings'][$subfield]['list'])) {
        $data[$subfield] = array_rand($settings['field_settings'][$subfield]['allowed_values']);
        continue;
      }
      $type = $item['type'] ?? 'string';
      $plugin = $pluginService->createInstance($type);
      $data[$subfield] = $plugin->generateSampleValue($item);
    }

    return $data;
  }

  /**
   * Check for duplicate names on our columns settings.
   */
  public function machineNameExists($value, array $form, FormStateInterface $form_state) {
    $count = 0;
    $parents = ['field_storage', 'subform', 'settings', 'storage'];
    $settings = $form_state->getValue($parents) ?? [];
    foreach ($settings as $item) {
      if ($item['name'] == $value) {
        $count++;
      }
    }
    return $count > 1;
  }

  /**
   * Callback for both ajax-enabled buttons.
   *
   * Selects and returns the fieldset with the names in it.
   */
  public function actionCallback(array &$form, FormStateInterface $form_state) {
    return $form['settings']['storage'] ?? [];
  }

  /**
   * Submit handler for the "add-one-more" button.
   *
   * Increments the max counter and causes a rebuild.
   */
  public static function addSubmit(array &$form, FormStateInterface $form_state) {
    $form_state->set('add', TRUE);
    // @todo drupal 11.2 replace by ajax command on #field_combined.
    $request = \Drupal::request();
    $request->query->count() == 0 ? $form_state->setRebuild() : $form_state->setRedirect(\Drupal::routeMatch()
      ->getRouteName(), \Drupal::routeMatch()->getRawParameters()->all());
  }

  /**
   * Submit handler for the "remove one" button.
   *
   * Decrements the max counter and causes a form rebuild.
   */
  public static function removeSubmit(array &$form, FormStateInterface $form_state) {
    $form_state->set('remove', $form_state->getTriggeringElement()['#delta']);
    $form_state->setRebuild();
  }

  /**
   * {@inheritdoc}
   */
  public static function mainPropertyName() {
    return NULL;
  }

}

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

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