I have a module that overrides the alter_hook form. The form created is a form created by the core form tool in Drupal 8. When I try to modify one of the fields, I cannot submit the form, but get an error:

Fatal error: Cannot unset string offsets in .../core/lib/Drupal/Core/Field/WidgetBase.php on line 356

Comments

Jaypan’s picture

Probably something you did wrong in your code.

Eagle11’s picture

That's a safe assumption, I guess. It's a very simple .module file in a custom module, overwriting a single field in the form. Below is the entire code.


<?php

/**
 * Implements hook_form_alter().
 */
function custom_form_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {

    if ($form_id == 'contact_message_contact_form') {
        $form['#attributes']['class'] = 'data-entry-form';

        $form['field_first_name'] = [
            '#placeholder' => 'First name*',
            '#prefix' => '<div class="row"><div class="col-md-6"><div class="data-entry-form__item">',
            '#required' => true,
            '#suffix' => '</div></div>',
            '#type' => 'textfield',
        ];

    }
}
Jaypan’s picture

I think that this:

$form['#attributes']['class'] = 'data-entry-form';

Should be this:

$form['#attributes']['class'][] = 'data-entry-form';
Eagle11’s picture

I just tried your fix, refreshed the caches, and still no.

After experimenting quite a lot, I found that if I remove this part of the array,

'#type' => 'textfield'

I can submit the form successfully. But if I add that in, it fails. That is the only key value pair that causes the form to not submit. Of course, the field doesn't actually render on the page without that pair though.

Jaypan’s picture

What does this dump to the page?

function custom_form_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {

    if ($form_id == 'contact_message_contact_form') {
        $form['#attributes']['class'] = 'data-entry-form';
die('&lt;pre>' . print_r($form['field_first_name'], TRUE) . '&lt;/pre>');
  }
}
Eagle11’s picture

This is the result:

<pre>
Array
(
    [#type] => container
    [#parents] => Array
        (
            [0] => field_first_name_wrapper
        )

    [#attributes] => Array
        (
            [class] => Array
                (
                    [0] => field--type-string
                    [1] => field--name-field-first-name
                    [2] => field--widget-string-textfield
                )

        )

    [widget] => Array
        (
            [0] => Array
                (
                    [#title] => First Name
                    [#title_display] => before
                    [#description] => 
                    [#field_parents] => Array
                        (
                        )

                    [#required] => 
                    [#delta] => 0
                    [#weight] => 0
                    [value] => Array
                        (
                            [#title] => First Name
                            [#title_display] => before
                            [#description] => 
                            [#field_parents] => Array
                                (
                                )

                            [#required] => 
                            [#delta] => 0
                            [#weight] => 0
                            [#type] => textfield
                            [#default_value] => 
                            [#size] => 60
                            [#placeholder] => 
                            [#maxlength] => 255
                            [#attributes] => Array
                                (
                                    [class] => Array
                                        (
                                            [0] => js-text-full
                                            [1] => text-full
                                        )

                                )

                        )

                )

            [#theme] => field_multiple_value_form
            [#field_name] => field_first_name
            [#cardinality] => 1
            [#cardinality_multiple] => 
            [#required] => 
            [#title] => First Name
            [#description] => 
            [#max_delta] => 0
            [#after_build] => Array
                (
                    [0] => Array
                        (
                            [0] => Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextfieldWidget
                            [1] => afterBuild
                        )

                )

            [#field_parents] => Array
                (
                )

            [#parents] => Array
                (
                    [0] => field_first_name
                )

            [#tree] => 1
        )

    [#access] => 1
    [#weight] => 0
    [#cache] => Array
        (
            [contexts] => Array
                (
                )

            [tags] => Array
                (
                    [0] => config:field.field.contact_message.contact.field_first_name
                    [1] => config:field.storage.contact_message.field_first_name
                )

            [max-age] => -1
        )

)</pre>
Jaypan’s picture

As you can see, the element is [#type] => container, and you are changing it to a textfield. This is confusing the submit handler that is expecting some child of the container that you haven't set.

Rather than re-declaring the field altogether, you should be altering the values you want to change. Examine that dump that you just posted, and change the values you want to be different.

Eagle11’s picture

I tried that. At least I tried it the only way I know how, which may be wrong. So if I do this instead:

        $form['field_first_name'] = [
            '#placeholder' => 'First name*',
            '#prefix' => '<div class="row"><div class="col-md-6"><div class="data-entry-form__item">',
            '#suffix' => '</div></div>',
        ];

Which leaves off the textfield and the required, the field does not render. Am I correct in how I'm overriding this field?

Jaypan’s picture

You are re-declaring the field (which entirely overrides the existing field). You need to alter the elements that you want to change.

What specifically are you trying to do with this code?

Eagle11’s picture

My main objective is to add the placeholder text and the prefix and suffix.

Jaypan’s picture

Well it looks like the actual element you want to alter is $form['field_first_name']['widget'][0], so you'll want to make your changes to this.

Ex:

$form['field_first_name']['widget'][0]['#prefix'] = 'Prefix goes here';
$form['field_first_name']['widget'][0]['#suffix'] = 'Suffix goes here';
etc...
Eagle11’s picture

That was it! Not very intuitive to me, but hey, it works! Thanks so much for sticking with me!

Jaypan’s picture

No problem :)

Here's a little more explanation. Imagine we have this array:

$element = array
(
  '#title' => 'This is the title',
  '#type =>'textfield',
);

Let's imagine you want to change the title. If you do this:

$element = array
(
  '#title' => 'This is the new title',
);

Now your array dump will be this:

Array
(
    [#title] => This is the new title
)

As you can see, there is only one element in the array now, because the array was re-declared (overwritten).

To change the title, you don't change $element, you change $element['#title']:

$element['#title'] = 'This is the new title';

Now the dump will be:

Array
(
    [#title] => This is the new title
    [#type] => textfield
)

As you can see, by only overwriting $element['#title'], it leaves the other field(s) intact, instead of wiping it out.

In hook_form_alter(), you should almost always be altering elements of array, not re-declaring the array.

Eagle11’s picture

Yes, that makes sense now. You make a good teacher. :)

learnbydrop’s picture

Error Message::
Fatal error: Cannot unset string offsets in .../core/lib/Drupal/Core/Field/WidgetBase.php on line 356

Contact form subject field is altered with 'select list' in custom module.
When I am submitting the form, the same error is generating.

Code::

if($form_id=="contact_message_contact_us_form"){

$form['subject'] = [
'#type' => 'select',
'#title' => 'Subject',
'#options' => [
'0' => 'yes',
'1' => 'No'
],
];

}