0

Hello I created a custom field type and field widget with multiple values, one entity reference and two checkboxes.

Everything's was working well, but to improve the usability of the fields, I would like to display the fields as a table, and that is when my problem begins.

I tried changing the field widget to be like a table, that visually worked to me, but now I will have problem because the keys of each field will be different than the defined in the field type, any suggestions in how to handle with this problem? If i roll back and remove the 'multiple_route' key to create the table and set the $elements[''] like i orignally started the fields are saving correctly.

FieldWidgetType.php

class MultipleRouteWidgetType extends WidgetBase {

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {

    $element['multiple_route'] = [
      '#type' => 'table',
      '#header' => [
        t(''),
        t('checkbox 1'),
        t('checkbox 2'),
      ],
    ];

    $route_value = NULL;
    if (isset($items[$delta]->route)) {
      $route_value = \Drupal::entityTypeManager()->getStorage('node')->load(intval($items[$delta]->route));
    }

    $element['multiple_route'][0]['route'] = $element + [
      '#title' => t('Page'),
      '#type' => 'entity_autocomplete',
      '#target_type' => 'node',
      '#required' => TRUE,
      '#empty_value' => NULL,
      '#default_value' => $route_value,
    ];

    $element['multiple_route'][0]['checkbox_1'] = [
      '#title' => t('checkbox 1'),
      '#type' => 'checkbox',
      '#empty_value' => 0,
      '#default_value' => isset($items[$delta]->checkbox_1) ? $items[$delta]->checkbox_1 : 0,
    ];

    $element['multiple_route'][0]['checkbox_2'] = [
      '#title' => t('checkbox_2.'),
      '#type' => 'checkbox',
      '#default_value' => isset($items[$delta]->checkbox_2) ? $items[$delta]->checkbox_2 : 0,
    ];

    return $element;
  }

}

Visually this is my goal and worked: enter image description here

But now the fields will not save because the keys are different than the definied in the schema, resulting in this error:

The website encountered an unexpected error. Please try again later. Error: Cannot create references to/from string offsets in Drupal\Component\Utility\NestedArray::setValue() (line 155 of core/lib/Drupal/Component/Utility/NestedArray.php). Drupal\Component\Utility\NestedArray::setValue(Array, Array, NULL) (Line: 1254)

Field Type.php property definitions and schema:

class RouteFieldType extends FieldItemBase {

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['route'] = DataDefinition::create('integer')
      ->setLabel(new TranslatableMarkup('Page'))
      ->addConstraint('RouteConstraint', ['fieldName' => $field_definition])
      ->setRequired(TRUE);

    $properties['checkbox_1'] = DataDefinition::create('boolean')
      ->setLabel(new TranslatableMarkup('checkbox_1'));

    $properties['checkbox_2'] = DataDefinition::create('boolean')
      ->setLabel(new TranslatableMarkup('checkbox_2'));

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    $schema = [
      'columns' => [
        'route' => [
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => TRUE,
        ],
        'checkbox_1' => [
          'type' => 'int',
          'size' => 'tiny',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'default' => 0,
        ],
        'checkbox_2' => [
          'type' => 'int',
          'size' => 'tiny',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'default' => 0,
        ],
      ],
    ];

    return $schema;
  }

2 Answers 2

1

Your widget needs to massage the form values. Like this core widget which restructures the array by removing one level:

/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php

  /**
   * {@inheritdoc}
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    // Since file upload widget now supports uploads of more than one file at a
    // time it always returns an array of fids. We have to translate this to a
    // single fid, as field expects single value.
    $new_values = [];
    foreach ($values as &$value) {
      foreach ($value['fids'] as $fid) {
        $new_value = $value;
        $new_value['target_id'] = $fid;
        unset($new_value['fids']);
        $new_values[] = $new_value;
      }
    }

    return $new_values;
  }
0

Thank you.

Before that to quick solve the issue and because my task was late, i emulate the form table with a markup on the suffix and prefix on the widget fields and returned the keys to the root of structure of $element:

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $prefix = '<table class="responsive-enabled">
      <thead>
        <tr>
          <th></th>
          <th>' . t('Checbox 1') . '</th>
          <th>' . t('Checbox 2') . '</th>
        </tr>
      </thead>
      <tbody>
        <tr class="odd">
          <td>
    ';

    $route_value = NULL;
    if (isset($items[$delta]->route)) {
      $route_value = \Drupal::entityTypeManager()->getStorage('node')->load(intval($items[$delta]->route));
    }

    $element['route'] = $element + [
      '#prefix' => $prefix,
      '#suffix' => '</td>',
      '#title' => t('Page'),
      '#type' => 'entity_autocomplete',
      '#target_type' => 'node',
      '#required' => TRUE,
      '#empty_value' => NULL,
      '#default_value' => $route_value,
    ];

    $element['checkbox_1'] = [
      '#prefix' => '<td>',
      '#suffix' => '</td>',
      '#title' => t('c'),
      '#title_display' => 'invisible',
      '#type' => 'checkbox',
      '#empty_value' => 0,
      '#default_value' => isset($items[$delta]->checkbox_1) ? $items[$delta]->checkbox_1 : 0,
    ];

    $element['checkbox_2'] = [
      '#prefix' => '<td>',
      '#suffix' => '</td></tr></tbody></table>',
      '#title' => t('c.'),
      '#title_display' => 'invisible',
      '#type' => 'checkbox',
      '#empty_value' => 0,
      '#default_value' => isset($items[$delta]->checkbox_2) ? $items[$delta]->checkbox_2 : 0,
    ];

    return $element;
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.