0

I'm creating a custom field for use with a bundle. I'm mentioning 'bundle' explicitly because the details of creating the field for use with an entity are slightly different.

  • The module enables cleanly.
  • The field table contains the property as defined in the field schema.
  • The field name appears in the Add Field dialog.
  • The field is added to a bundle cleanly, and its settings form contains the property in the default settings pane, properly.
  • When content is created, the field and its property appear in the node edit form correctly.
  • The node saves without throwing any errors.

When the node is saved and then re-edited, the field property is empty. In inspecting the field in the database, the instance meta such as bundle, and entity_id are populated, but the property column is NULL.

If the fieldset is removed, the value is saved as expected.

Here is the field code: web/modules/custom/test_video_widget/src/Plugin/Field/FieldType/TestVideoWidgetField.php

<?php
namespace Drupal\test_video_widget\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;

/**
 * @FieldType(
 *   id = "test_video_widget",
 *   module = "test_video_widget",
 *   label = @Translation("Test Video Widget"),
 *   description = @Translation("This field stores video meta information."),
 *   default_widget = "TestVideoWidgetW",
 *   default_formatter = "TestVideoWidgetFormatter",
 * )
 */


class TestVideoWidgetField extends FieldItemBase {
    /**
     * {@inheritdoc}
     */
    public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {

        $properties = [];

        $properties['video_title'] = DataDefinition::create('string')
            ->setLabel(t('Video Title'))
            ->setDescription(t('Optional title to be displayed instead of the node title.'));

        return $properties;
    }

    /**
     * {@inheritdoc}
     */
    public static function schema(FieldStorageDefinitionInterface $field_definition) {
        $columns = array(
            'video_title' => array(
                'description' => 'Optional title to be displayed instead of the node title.',
                'type' => 'varchar',
                'length' => 255,
            ),
        );

        $schema = array(
            'columns' => $columns,
            'indexes' => [],
            //'foreign keys' => array(),
        );

        return $schema;
    }

Here is the field widget code that does NOT work: web/modules/custom/test_video_widget/src/Plugin/Field/FieldWidget/TestVideoWidgetW.php

<?php
namespace Drupal\test_video_widget\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;

/**
 *
 * @FieldWidget(
 *   id = "TestVideoWidgetW",
 *   label = @Translation("Test Video Widget - Widget"),
 *   description = @Translation("Test Video Widget - Widget"),
 *   field_types = {
 *     "test_video_widget",
 *   },
 * )
 */

class TestVideoWidgetW extends WidgetBase {
  /**
   * {@inheritdoc}
   */
    public function formElement(
        FieldItemListInterface $items,
        $delta,
        array $element,
        array &$form,
        FormStateInterface $form_state
    ) {
        $element['video_meta'] = [
            '#type' => 'fieldset',
            '#title' => t('Video Meta'),
        ];

        $element['video_meta']['video_title'] = [
            '#type' => 'textfield',
            '#title' => t('Video Title'),
            '#default_value' => isset($items[$delta]->video_title) ? $items[$delta]->video_title : NULL,
            '#placeholder' => t(''),
            '#size' => 60,
        ];

        return $element;
    }
}

and here is the same method without the fieldset, which DOES work:

    public function formElement(
        FieldItemListInterface $items,
        $delta,
        array $element,
        array &$form,
        FormStateInterface $form_state
    ) {
        $element['video_title'] = [
            '#type' => 'textfield',
            '#title' => t('Video Title'),
            '#default_value' => isset($items[$delta]->video_title) ? $items[$delta]->video_title : NULL,
            '#placeholder' => t(''),
            '#size' => 60,
        ];

        return $element;
    }

also, the resulting field table for the field added to the bundle:

node__field_svideo;
+--------------------------------+------------------+------+-----+---------+-------+
| Field                          | Type             | Null | Key | Default | Extra |
+--------------------------------+------------------+------+-----+---------+-------+
| bundle                         | varchar(128)     | NO   | MUL |         |       |
| deleted                        | tinyint(4)       | NO   | PRI | 0       |       |
| entity_id                      | int(10) unsigned | NO   | PRI | NULL    |       |
| revision_id                    | int(10) unsigned | NO   | MUL | NULL    |       |
| langcode                       | varchar(32)      | NO   | PRI |         |       |
| delta                          | int(10) unsigned | NO   | PRI | NULL    |       |
| field_svideo_video_title       | varchar(255)     | YES  |     | NULL    |       |
+--------------------------------+------------------+------+-----+---------+-------+
  • Using xdebug, I notice that at the point that the field widget is processed after submitting the node form, form state contains the values in the input array, and evaluating $form_state->getUserInput() returns the same, but values are nowhere to be found in the item or element during WidgetBase::formElement() – JAyenGreen May 14 '18 at 17:01
  • I also note that when I manually add a value to one of the properties in mysql (update node__field_svideo set field_svideo_video_title = 'Test' where entity_id = 1) the edited node still displays the property as empty. – JAyenGreen May 14 '18 at 19:33
  • I suspect if you go up a level in the form you'll find '#tree' => 'true'. Maybe try turning it off in your fieldgroup. If that doesn't work, override whatever the submit method is in WidgetBase to get the value and set it on the entity as desired. – Alfred Armstrong May 16 '18 at 16:22
  • Interestingly, the fieldset didn't have a #tree property. – JAyenGreen May 21 '18 at 16:29
1
+50

This is best resolved in the Widget Plugin as opposed to the FieldType so other widgets aren't adversely affect. Overwriting the messageFormValues method in your TestVideoWidgetW class will allow you to output the value the FieldType expects.

  /**
   * @inheritdoc
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    foreach ($values as &$value) {
      if ($value && array_key_exists('video_meta', $value)) {
        $value['video_title'] = $value['video_meta']['video_title'];
      }
    }
    return parent::massageFormValues($values, $form, $form_state);
  }
  • 1
    That worked, but I massaged it a bit to this: public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { foreach ($values as $array) { if (is_array($array)) { foreach ($array as $key => $value) { $values[$key] = $value; } } } – JAyenGreen May 22 '18 at 0:29
0

This added to the FieldType code resolves the issue:

public function preSave() {
    foreach ($this->values['video_meta'] as $key => $value) {
        $this->values[$key] = $value;
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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