You are here

public function DateRecurModularAlphaWidget::formElement in Recurring Date Field Modular Widgets 8

Same name and namespace in other branches
  1. 3.x src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php \Drupal\date_recur_modular\Plugin\Field\FieldWidget\DateRecurModularAlphaWidget::formElement()
  2. 2.x src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php \Drupal\date_recur_modular\Plugin\Field\FieldWidget\DateRecurModularAlphaWidget::formElement()

Returns the form for a single field widget.

Field widget form elements should be based on the passed-in $element, which contains the base form element properties derived from the field configuration.

The BaseWidget methods will set the weight, field name and delta values for each form element. If there are multiple values for this field, the formElement() method will be called as many times as needed.

Other modules may alter the form element provided by this function using hook_field_widget_form_alter() or hook_field_widget_WIDGET_TYPE_form_alter().

The FAPI element callbacks (such as #process, #element_validate, #value_callback, etc.) used by the widget do not have access to the original $field_definition passed to the widget's constructor. Therefore, if any information is needed from that definition by those callbacks, the widget implementing this method, or a hook_field_widget[_WIDGET_TYPE]_form_alter() implementation, must extract the needed properties from the field definition and set them as ad-hoc $element['#custom'] properties, for later use by its element callbacks.

Parameters

\Drupal\Core\Field\FieldItemListInterface $items: Array of default values for this field.

int $delta: The order of this item in the array of sub-elements (0, 1, 2, etc.).

array $element: A form element array containing basic properties for the widget:

  • #field_parents: The 'parents' space for the field in the form. Most widgets can simply overlook this property. This identifies the location where the field values are placed within $form_state->getValues(), and is used to access processing information for the field through the getWidgetState() and setWidgetState() methods.
  • #title: The sanitized element label for the field, ready for output.
  • #description: The sanitized element description for the field, ready for output.
  • #required: A Boolean indicating whether the element value is required; for required multiple value fields, only the first widget's values are required.
  • #delta: The order of this item in the array of sub-elements; see $delta above.

array $form: The form structure where widgets are being attached to. This might be a full form structure, or a sub-element of a larger form.

\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.

Return value

array The form elements for a single widget for this field.

Overrides WidgetInterface::formElement

See also

hook_field_widget_form_alter()

hook_field_widget_WIDGET_TYPE_form_alter()

File

src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php, line 124

Class

DateRecurModularAlphaWidget
Date recur alpha widget.

Namespace

Drupal\date_recur_modular\Plugin\Field\FieldWidget

Code

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) : array {

  /** @var \Drupal\date_recur\Plugin\Field\FieldType\DateRecurFieldItemList|\Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem[] $items */
  $elementParents = array_merge($element['#field_parents'], [
    $this->fieldDefinition
      ->getName(),
    $delta,
  ]);
  $element['#element_validate'][] = [
    static::class,
    'validateModularWidget',
  ];
  $element['#after_build'][] = [
    static::class,
    'afterBuildModularWidget',
  ];
  $element['#theme'] = 'date_recur_modular_alpha_widget';
  $element['#theme_wrappers'][] = 'form_element';
  $item = $items[$delta];
  $grid = $items
    ->getPartGrid();
  $rule = $this
    ->getRule($item);
  $parts = $rule ? $rule
    ->getParts() : [];
  $count = $parts['COUNT'] ?? NULL;
  $timeZone = $this
    ->getDefaultTimeZone($item);
  $endsDate = NULL;
  try {
    $until = $parts['UNTIL'] ?? NULL;
    if (is_string($until)) {
      $endsDate = new \DateTime($until);
    }
    elseif ($until instanceof \DateTimeInterface) {
      $endsDate = $until;
    }
    if ($endsDate) {

      // UNTIL is _usually_ in UTC, adjust it to the field time zone.
      $endsDate
        ->setTimezone(new \DateTimeZone($timeZone));
    }
  } catch (\Exception $e) {
  }
  $fieldModes = $this
    ->getFieldModes($grid);
  $element['start'] = [
    '#type' => 'datetime',
    '#title' => $this
      ->t('Starts on'),
    '#title_display' => 'invisible',
    '#default_value' => $item->start_date,
    // \Drupal\Core\Datetime\Element\Datetime::valueCallback tries to change
    // the time zone to current users timezone if not set, Set the timezone
    // here so the value doesn't change.
    '#date_timezone' => $timeZone,
  ];
  $element['end'] = [
    '#title' => $this
      ->t('Ends on'),
    '#title_display' => 'invisible',
    '#type' => 'datetime',
    '#default_value' => $item->end_date,
    '#date_timezone' => $timeZone,
  ];
  $element['mode'] = $this
    ->getFieldMode($item);
  $element['mode']['#title_display'] = 'invisible';
  $element['daily_count'] = [
    '#type' => 'number',
    '#title' => $this
      ->t('Days'),
    '#title_display' => 'invisible',
    '#field_suffix' => $this
      ->t('days'),
    '#default_value' => $count ?? 1,
    '#min' => 1,
    // Some part elements also need to check access, as when states are
    // applied if there are no conditions then the field is always visible.
    '#access' => count($fieldModes['daily_count'] ?? []) > 0,
  ];
  $element['daily_count']['#states'] = $this
    ->getVisibilityStates($element, $fieldModes['daily_count'] ?? []);
  $element['weekdays'] = $this
    ->getFieldByDay($rule);
  $element['weekdays']['#states'] = $this
    ->getVisibilityStates($element, $fieldModes['weekdays'] ?? []);
  $element['weekdays']['#title_display'] = 'invisible';
  $element['ordinals'] = $this
    ->getFieldMonthlyByDayOrdinals($element, $rule);
  $element['ordinals']['#states'] = $this
    ->getVisibilityStates($element, $fieldModes['ordinals'] ?? []);
  $element['ordinals']['#title_display'] = 'invisible';
  $element['time_zone'] = $this
    ->getFieldTimeZone($timeZone);
  $endsModeDefault = $endsDate ? DateRecurModularWidgetOptions::ENDS_MODE_ON_DATE : ($count > 0 ? DateRecurModularWidgetOptions::ENDS_MODE_OCCURRENCES : DateRecurModularWidgetOptions::ENDS_MODE_INFINITE);
  $element['ends_mode'] = $this
    ->getFieldEndsMode();
  $element['ends_mode']['#states'] = $this
    ->getVisibilityStates($element, $fieldModes['ends_mode'] ?? []);
  $element['ends_mode']['#title_display'] = 'before';
  $element['ends_mode']['#default_value'] = $endsModeDefault;

  // Hide or show 'On date' / 'number of occurrences' checkboxes depending on
  // selected mode.
  $element['ends_mode'][DateRecurModularWidgetOptions::ENDS_MODE_OCCURRENCES]['#states'] = $this
    ->getVisibilityStates($element, $fieldModes['ends_count'] ?? []);
  $element['ends_mode'][DateRecurModularWidgetOptions::ENDS_MODE_ON_DATE]['#states'] = $this
    ->getVisibilityStates($element, $fieldModes['ends_date'] ?? []);
  $element['ends_count'] = [
    '#type' => 'number',
    '#title' => $this
      ->t('End after number of occurrences'),
    '#title_display' => 'invisible',
    '#field_prefix' => $this
      ->t('after'),
    '#field_suffix' => $this
      ->t('occurrences'),
    '#default_value' => $count ?? 1,
    '#min' => 1,
    '#access' => count($fieldModes['ends_count'] ?? []) > 0,
  ];
  $nameMode = $this
    ->getName($element, [
    'mode',
  ]);
  $nameEndsMode = $this
    ->getName($element, [
    'ends_mode',
  ]);
  $element['ends_count']['#states']['visible'] = [];
  foreach ($fieldModes['ends_count'] ?? [] as $mode) {
    $element['ends_count']['#states']['visible'][] = [
      ':input[name="' . $nameMode . '"]' => [
        'value' => $mode,
      ],
      ':input[name="' . $nameEndsMode . '"]' => [
        'value' => DateRecurModularWidgetOptions::ENDS_MODE_OCCURRENCES,
      ],
    ];
  }

  // States dont yet work on date time so put it in a container.
  // @see https://www.drupal.org/project/drupal/issues/2419131
  $element['ends_date'] = [
    '#type' => 'container',
  ];
  $element['ends_date']['ends_date'] = [
    '#type' => 'datetime',
    '#title' => $this
      ->t('End before this date'),
    '#title_display' => 'invisible',
    '#description' => $this
      ->t('No occurrences can begin after this date.'),
    '#default_value' => $endsDate ? DrupalDateTime::createFromDateTime($endsDate) : NULL,
    // Fix values tree thanks to state+container hack.
    '#parents' => array_merge($elementParents, [
      'ends_date',
    ]),
    // \Drupal\Core\Datetime\Element\Datetime::valueCallback tries to change
    // the time zone to current users timezone if not set, Set the timezone
    // here so the value doesn't change.
    '#date_timezone' => $timeZone,
  ];
  $element['ends_date']['#states']['visible'] = [];
  foreach ($fieldModes['ends_date'] ?? [] as $mode) {
    $element['ends_date']['#states']['visible'][] = [
      ':input[name="' . $nameMode . '"]' => [
        'value' => $mode,
      ],
      ':input[name="' . $nameEndsMode . '"]' => [
        'value' => DateRecurModularWidgetOptions::ENDS_MODE_ON_DATE,
      ],
    ];
  }
  return $element;
}