Form API Table element is being treated as an input element, this can break fields that have a handler defined which are now no longer altering the data in the $form_state array.

Created on 3 November 2021, about 3 years ago
Updated 30 January 2023, almost 2 years ago

Problem/Motivation

I have a custom form in the back-end where I have placed a few fields inside of a FAPI table element. One of the fields is a datetime field. The issue I'm having is whenever I try to submit the form I'm getting a warning thrown and a validation error.

Warning: Undefined array key "object" in Drupal\Core\Datetime\Element\Datetime::validateDatetime() (line 371 of core/lib/Drupal/Core/Datetime/Element/Datetime.php).
Drupal\Core\Datetime\Element\Datetime::validateDatetime(Array, Object, Array)
call_user_func_array(Array, Array) (Line: 282)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object) (Line: 238)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object) (Line: 238)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object) (Line: 238)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object, 'apply_task_strategy_form') (Line: 118)
Drupal\Core\Form\FormValidator->validateForm('apply_task_strategy_form', Array, Object) (Line: 588)
Drupal\Core\Form\FormBuilder->processForm('apply_task_strategy_form', Array, Object) (Line: 320)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 73)
Drupal\Core\Controller\FormController->getContentResult(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 564)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 158)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 80)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 49)
Asm89\Stack\Cors->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 708)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

...

The date is invalid. Please enter a date in the format 2021-11-03 08:51:23.

The date is beging selected by the datepicker so it is for sure valid. When I inspected the \Drupal\Core\Datetime\Element\Datetime::validateDatetime function that is throwing the error and sets the validation error, I noticed that the $form_state object for our datetime field is missing the datetime object, it only contains the date and time as a string. Thus the step where it tries to retrieve the $input['object'] (line 371) fails because there is no object and makes it so that the validations fails.

Steps to reproduce

I created a simple form by extending the FormBase class and provided a route for it. The relevant part of the code where I create the table structure is this:

    ...

    $form['tasks'] = [
      '#type' => 'table',
      '#header' => [
        $this->t('Task type'),
        $this->t('Description'),
        $this->t('Due date'),
      ],
      '#empty' => t('No tasks found.'),
    ];

    // Build the form based on the enabled tasks.
    foreach ($tasks_to_create as $task) {
      $task_term = $tasks_terms[$task];

      $form['tasks'][$task] = [
        'task_type' => [
          '#markup' => $task_term->label(),
        ],
        'description' => [
          '#type' => 'textarea',
          '#rows' => 1,
          '#placeholder' => $this->t('Type a task description here if required.'),
        ],
        'due_date' => [
          '#type' => 'datetime',
          '#default_value' => DrupalDateTime::createFromTimestamp(strtotime('+ 1 day')),
          '#required' => TRUE,
        ],
      ];
    }

   ...

No custom validation code is written. The submit logic I have written is never reached because the core validation fails.

The issue seems to be isolated to the combination of a table field and a datetime field because if I place a test field outside of the table (exactly the same as the field inside of the table) that field doesn't throw the warning and error.

Proposed resolution

Investigate why the DrupalDateTime object is not being created when the datetime element is inside of a FAPI Table element.

Remaining tasks

Create a patch.

User interface changes

None

API changes

None

Data model changes

None

Release notes snippet

None

πŸ› Bug report
Status

Needs work

Version

10.1 ✨

Component
RenderΒ  β†’

Last updated 6 days ago

Created by

πŸ‡§πŸ‡ͺBelgium BramDriesen Belgium πŸ‡§πŸ‡ͺ

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • The Needs Review Queue Bot β†’ tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • First commit to issue fork.
Production build 0.71.5 2024