You are here

public function InlineParagraphsWidget::formMultipleElements in Paragraphs Sets 8

Special handling to create form elements for multiple values.

Handles generic features for multiple fields:

  • number of widgets
  • AHAH-'add more' button
  • table display and drag-n-drop value reordering

Overrides InlineParagraphsWidget::formMultipleElements

File

src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php, line 69

Class

InlineParagraphsWidget
Plugin definition of the 'entity_reference paragraphs sets' widget.

Namespace

Drupal\paragraphs_sets\Plugin\Field\FieldWidget

Code

public function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
  $host = $items
    ->getEntity();
  $field_name = $this->fieldDefinition
    ->getName();
  $cardinality = $this->fieldDefinition
    ->getFieldStorageDefinition()
    ->getCardinality();
  $this->fieldParents = $form['#parents'];
  $field_state = static::getWidgetState($this->fieldParents, $field_name, $form_state);
  $user_input =& $form_state
    ->getUserInput();
  $max = $field_state['items_count'];
  $entity_type_manager = \Drupal::entityTypeManager();
  $sets = static::getSets();
  $set = isset($field_state['selected_set']) ? $field_state['selected_set'] : NULL;

  // Consider adding a default paragraph for new host entities.
  if ($max == 0 && $items
    ->getEntity()
    ->isNew() && empty($set)) {
    $set = $this
      ->getDefaultParagraphTypeMachineName();
  }
  if ($set) {
    if (isset($field_state['button_type']) && 'set_selection_button' === $field_state['button_type']) {

      // Clear all items.
      $items
        ->filter(function () {
        return FALSE;
      });

      // Clear field state.
      $field_state['paragraphs'] = [];

      // Clear user input.
      foreach ($user_input[$field_name] as $key => $value) {
        if (!is_numeric($key) || empty($value['subform'])) {
          continue;
        }
        unset($user_input[$field_name][$key]);
      }
      $max = 0;
    }
    $target_type = $this
      ->getFieldSetting('target_type');
    $context = [
      'set' => $set,
      'field' => $this->fieldDefinition,
      'form' => $form,
      'form_state' => $form_state,
      'entity' => $host,
    ];
    foreach ($sets[$set]['paragraphs'] as $key => $info) {
      $alter_hooks = [
        'paragraphs_set_data',
        'paragraphs_set_' . $set . '_data',
        'paragraphs_set_' . $set . '_' . $field_name . '_data',
      ];
      $context['key'] = $key;
      $context['paragraphs_bundle'] = $info['type'];
      $data = $info['data'];
      \Drupal::moduleHandler()
        ->alter($alter_hooks, $data, $context);
      $item_values = [
        'type' => $info['type'],
      ] + $data;
      $max++;
      $paragraphs_entity = $entity_type_manager
        ->getStorage($target_type)
        ->create($item_values);
      $display = EntityFormDisplay::collectRenderDisplay($paragraphs_entity, $this
        ->getSetting('form_display_mode'));
      $field_state['paragraphs'][$max - 1] = [
        'entity' => $paragraphs_entity,
        'display' => $display,
        'mode' => 'edit',
        'original_delta' => $max,
      ];
    }
    $field_state['items_count'] = $max;
    $field_state['selected_set'] = NULL;
  }
  $this->realItemCount = $max;
  $is_multiple = $this->fieldDefinition
    ->getFieldStorageDefinition()
    ->isMultiple();
  $title = $this->fieldDefinition
    ->getLabel();
  $description = FieldFilteredMarkup::create(\Drupal::token()
    ->replace($this->fieldDefinition
    ->getDescription()));
  $elements = [];
  $this->fieldIdPrefix = implode('-', array_merge($this->fieldParents, [
    $field_name,
  ]));
  $this->fieldWrapperId = Html::getUniqueId($this->fieldIdPrefix . '-add-more-wrapper');
  $elements['#prefix'] = '<div id="' . $this->fieldWrapperId . '">';
  $elements['#suffix'] = '</div>';
  $field_state['ajax_wrapper_id'] = $this->fieldWrapperId;

  // Persist the widget state so formElement() can access it.
  static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
  if ($max > 0) {
    for ($delta = 0; $delta < $max; $delta++) {

      // Add a new empty item if it doesn't exist yet at this delta.
      if (!isset($items[$delta])) {
        $items
          ->appendItem();
      }

      // For multiple fields, title and description are handled by the
      // wrapping table.
      $element = [
        '#title' => $is_multiple ? '' : $title,
        '#description' => $is_multiple ? '' : $description,
        '#paragraphs_bundle' => '',
      ];
      $element = $this
        ->formSingleElement($items, $delta, $element, $form, $form_state);
      if ($element) {
        $widget_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
        $element['#paragraphs_bundle'] = $widget_state['paragraphs'][$delta]['entity']
          ->bundle();

        // Input field for the delta (drag-n-drop reordering).
        if ($is_multiple) {

          // We name the element '_weight' to avoid clashing with elements
          // defined by widget.
          $element['_weight'] = [
            '#type' => 'weight',
            '#title' => $this
              ->t('Weight for row @number', [
              '@number' => $delta + 1,
            ]),
            '#title_display' => 'invisible',
            // This 'delta' is the FAPI #type 'weight' element's property.
            '#delta' => $max,
            '#default_value' => $items[$delta]->_weight ?: $delta,
            '#weight' => 100,
          ];
        }

        // Access for the top element is set to FALSE only when the paragraph
        // was removed. A paragraphs that a user can not edit has access on
        // lower level.
        if (isset($element['#access']) && !$element['#access']) {
          $this->realItemCount--;
        }
        else {
          $elements[$delta] = $element;
        }
      }
    }
  }
  $field_state = static::getWidgetState($this->fieldParents, $field_name, $form_state);
  $field_state['real_item_count'] = $this->realItemCount;
  $field_state['add_mode'] = $this
    ->getSetting('add_mode');
  $field_state['selected_set'] = NULL;
  static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
  $elements += [
    '#element_validate' => [
      [
        $this,
        'multipleElementValidate',
      ],
    ],
    '#required' => $this->fieldDefinition
      ->isRequired(),
    '#field_name' => $field_name,
    '#cardinality' => $cardinality,
    '#max_delta' => $max - 1,
  ];
  if ($this->realItemCount > 0) {
    $elements += [
      '#theme' => 'field_multiple_value_form__paragraphs_sets',
      '#cardinality_multiple' => $is_multiple,
      '#title' => $title,
      '#description' => $description,
    ];
  }
  else {
    $classes = $this->fieldDefinition
      ->isRequired() ? [
      'form-required',
    ] : [];
    $elements += [
      '#type' => 'container',
      '#theme_wrappers' => [
        'container',
      ],
      '#cardinality_multiple' => TRUE,
      'title' => [
        '#type' => 'html_tag',
        '#tag' => 'strong',
        '#value' => $title,
        '#attributes' => [
          'class' => $classes,
        ],
      ],
      'text' => [
        '#type' => 'container',
        'value' => [
          '#markup' => $this
            ->t('No @title added yet.', [
            '@title' => $this
              ->getSetting('title'),
          ]),
          '#prefix' => '<em>',
          '#suffix' => '</em>',
        ],
      ],
    ];
    if ($this->fieldDefinition
      ->isRequired()) {
      $elements['title']['#attributes']['class'][] = 'form-required';
    }
    if ($description) {
      $elements['description'] = [
        '#type' => 'container',
        'value' => [
          '#markup' => $description,
        ],
        '#attributes' => [
          'class' => [
            'description',
          ],
        ],
      ];
    }
  }
  $this
    ->initIsTranslating($form_state, $host);
  $elements['set_selection'] = $this
    ->buildSelectSetSelection($form_state, $set);
  if (($this->realItemCount < $cardinality || $cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) && !$form_state
    ->isProgrammed() && !$this->isTranslating) {
    $elements['add_more'] = $this
      ->buildAddActions();
  }
  $elements['#attached']['library'][] = 'paragraphs/drupal.paragraphs.admin';
  $elements['#attached']['library'][] = 'paragraphs_sets/drupal.paragraphs_sets.admin';
  return $elements;
}