Problem/Motivation

We've been slowly converting all forms to FormInterface.
There aren't too many left, and once that is done we can remove the support for it once and for all.

Proposed resolution

Remove support for function based forms.

Remaining tasks

Wait for all forms to be FormInterface

User interface changes

N/A

API changes

All forms must be classes implementing FormInterface.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

Status: Active » Needs review
FileSize
8.61 KB

Will postpone after this first test run.

tim.plunkett’s picture

Status: Needs work » Postponed

See you later.

catch’s picture

Issue tags: +beta target
tim.plunkett’s picture

tim.plunkett’s picture

tim.plunkett’s picture

Status: Postponed » Needs review
FileSize
135.92 KB
8.66 KB

Here's all of the above issues combined with this, let's see what else breaks.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
652 bytes
136.56 KB

Whoops!

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
661 bytes
137.2 KB

/me shakes fist at old code

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
140.64 KB
3.46 KB

13363, 5002, 974... 0?

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
1.64 KB
141.27 KB

Status: Needs review » Needs work

The last submitted patch, 16: form-2268761-16-combined.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
141.96 KB
14.72 KB
709 bytes

Woot! This should pass now.

jibran’s picture

It is RTBC. Thank you @tim.plunkett for revamping the form component.

+++ b/core/lib/Drupal/Core/Controller/FormController.php
@@ -77,8 +77,7 @@ public function getContentResult(Request $request) {
-    return $this->formBuilder->buildForm($form_id, $form_state);
+    return $this->formBuilder->buildForm($form_object, $form_state);

Yay!!!!

tim.plunkett’s picture

Removing the final hack from FormBuilder... Yay!

Uploading one last time to see it pass, and then postponing until the 7 issues go in. Then I can just retest it.

tim.plunkett’s picture

Status: Needs review » Postponed

Great.

tim.plunkett’s picture

tim.plunkett’s picture

Status: Postponed » Needs review

THIS DAY HAS FINALLY COME.
From #1913084: Introduce a Form interface for building, validating, and submitting forms in February 2013 until now.

Berdir’s picture

+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -685,40 +675,8 @@ public function prepareForm($form_id, &$form, &$form_state) {
-    }
+    $form['#validate'][] = array($form_state['build_info']['callback_object'], 'validateForm');
+    $form['#submit'][] = array($form_state['build_info']['callback_object'], 'submitForm');
 

Are you sure that we should do this? Doesn't this break forms that don't want to call the default methods by default? I guess it's only on the default submit and usually you have non-standard submits on specific buttons, but still?

Before, the form object versions were still inside the !isset().

I keep forgetting/avoiding it, but I'd still like to see us find a way to avoid serializing the form object and specifying it like this, because what happens is that during submit, we have two different instances, the unserialized one in those form submits and the one that was re-created. To avoid nasty stuff like https://github.com/karanpoddar/monitoring/blob/gsoc-plugin/src/SensorFor... and the corresponding one inside EntityForm.

I think supporting a way to only specify the method name for submit/validate should bring us pretty far, possibly serialize the callback_object object as a string and re-initalize it.

tim.plunkett’s picture

Issue summary: View changes
Issue tags: +API change

#24 we absolutely do want this. #2252165: submitForm() is not called when custom form #submit handler is set was actually going to do that a different way, but this is that without the functional callbacks.
The rest of #24 is unrelated to removing this legacy code.

  1. +++ b/core/lib/Drupal/Core/Form/FormBuilder.php
    @@ -685,40 +675,8 @@ public function prepareForm($form_id, &$form, &$form_state) {
    -    if (!isset($form['#validate'])) {
    ...
    -    if (!isset($form['#submit'])) {
    ...
    +    $form['#validate'][] = array($form_state['build_info']['callback_object'], 'validateForm');
    +    $form['#submit'][] = array($form_state['build_info']['callback_object'], 'submitForm');
    

    Before, these were optional. Now they always run.

  2. +++ b/core/modules/field_ui/src/Form/FieldEditForm.php
    @@ -153,8 +153,6 @@ public function buildForm(array $form, array &$form_state, FieldInstanceConfigIn
    -    $form['#submit'][] = array($this, 'submitForm');
    -    $form['#validate'][] = array($this, 'validateForm');
    

    This can be removed because of that.

  3. +++ b/core/includes/batch.inc
    @@ -379,7 +379,7 @@ function _batch_next_set() {
    -      call_user_func_array($callback, array($batch['form_state']['complete_form'], &$batch['form_state']));
    +      call_user_func_array($callback, array(&$batch['form_state']['complete_form'], &$batch['form_state']));
    

    All normal forms have always been given the form by reference (though it is rarely used), but batch forms were not. Now that submitForm() is always called, it must comply with the interface.

  4. +++ b/core/modules/field/src/Tests/FieldAttachOtherTest.php
    @@ -342,6 +342,7 @@ function testEntityFormDisplayExtractFormValues() {
    +    $form_state['build_info']['callback_object'] = \Drupal::entityManager()->getFormObject($entity_type, 'default');
         \Drupal::formBuilder()->prepareForm('field_test_entity_form', $form, $form_state);
    
    +++ b/core/modules/system/src/Tests/Form/ElementsTableSelectTest.php
    @@ -216,6 +216,7 @@ private function formSubmitHelper($form, $edit) {
    +    $form_state['build_info']['callback_object'] = new StubForm($form_id, $form);
     
         \Drupal::formBuilder()->prepareForm($form_id, $form, $form_state);
    
    +++ b/core/modules/system/src/Tests/Form/FormTest.php
    @@ -109,6 +109,7 @@ function testRequiredFields() {
    +          $form_state['build_info']['callback_object'] = new StubForm($form_id, $form);
    

    These additions are just for tests calling prepareForm() directly, its not a change for real code.

Writing a change record draft now.

dawehner’s picture

Issue summary: View changes
Issue tags: -API change
  1. +++ b/core/lib/Drupal/Core/Controller/FormController.php
    @@ -77,8 +77,7 @@ public function getContentResult(Request $request) {
    -    $form_id = $this->formBuilder->getFormId($form_object, $form_state);
    -    return $this->formBuilder->buildForm($form_id, $form_state);
    +    return $this->formBuilder->buildForm($form_object, $form_state);
    
    +++ b/core/lib/Drupal/Core/Entity/EntityFormBuilder.php
    @@ -52,7 +52,7 @@ public function getForm(EntityInterface $entity, $operation = 'default', array $
     
    -    return $this->formBuilder->buildForm($form_object->getFormId(), $form_state);
    +    return $this->formBuilder->buildForm($form_object, $form_state);
    

    <3 This is much better to use

  2. +++ b/core/lib/Drupal/Core/Form/FormBuilder.php
    @@ -139,18 +140,16 @@ public function getFormId($form_arg, &$form_state) {
    +    if (!is_object($form_arg) || !($form_arg instanceof FormInterface)) {
    +      throw new \Exception(String::format('The form argument @form_arg is not a valid form.', array('@form_arg' => $form_arg)));
         }
    

    I wonder whether we can use a more specific exception, at least InvalidArgument or a custom one for forms?

  3. +++ b/core/lib/Drupal/Core/Form/FormBuilder.php
    @@ -685,40 +675,8 @@ public function prepareForm($form_id, &$form, &$form_state) {
    -    if (!isset($form['#validate'])) {
    ...
    -    if (!isset($form['#submit'])) {
    ...
    +    $form['#validate'][] = array($form_state['build_info']['callback_object'], 'validateForm');
    +    $form['#submit'][] = array($form_state['build_info']['callback_object'], 'submitForm');
    

    This is a small behavior change. Previously you seem to be able to skip the default validate/submit handling? some clarification would be cool

  4. +++ b/core/lib/Drupal/Core/Form/FormBuilder.php
    --- a/core/modules/field/src/Tests/FieldAttachOtherTest.php
    +++ b/core/modules/field/src/Tests/FieldAttachOtherTest.php
    
    +++ b/core/modules/field/src/Tests/FieldAttachOtherTest.php
    @@ -342,6 +342,7 @@ function testEntityFormDisplayExtractFormValues() {
         // Pretend the form has been built.
    +    $form_state['build_info']['callback_object'] = \Drupal::entityManager()->getFormObject($entity_type, 'default');
    

    This is odd ... why was that not needed previously?

  5. +++ b/core/modules/views/includes/ajax.inc
    @@ -14,7 +14,7 @@
    -function views_ajax_form_wrapper($form_id, &$form_state) {
    +function views_ajax_form_wrapper($form_class, &$form_state) {
    

    We need a follow up to add proper documentation for the parameters: #2303761: Move views_ajax_form_wrapper() to ViewsFormBase

tim.plunkett’s picture

FileSize
4.72 KB
20.87 KB

#26.2 Good point, switched the exception class
#26.3 Yes, this was intentional. Adding test coverage for that explicitly.
#26.4 This wasn't needed before because the form isn't actually validated or submitted, just prepared, so the form object wasn't needed. Now it must be present or the adding of the handlers will cause a notice.

Added https://www.drupal.org/node/2303785

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Thank you, this is just perfect.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/includes/batch.inc
    @@ -379,7 +379,7 @@ function _batch_next_set() {
    -      call_user_func_array($callback, array($batch['form_state']['complete_form'], &$batch['form_state']));
    +      call_user_func_array($callback, array(&$batch['form_state']['complete_form'], &$batch['form_state']));
    

    How is this change related?

  2. +++ b/core/lib/Drupal/Core/Controller/FormController.php
    @@ -77,8 +77,7 @@ public function getContentResult(Request $request) {
    -    return $this->formBuilder->buildForm($form_id, $form_state);
    +    return $this->formBuilder->buildForm($form_object, $form_state);
    

    Does the docblock of FormBuilder::buildForm need to change?

tim.plunkett’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
22.32 KB
1.45 KB

#29.1 Batch forms now have submitForm(&$form, &$form_state) executed, and need to pass $form as a reference.
Previously all batch forms provided custom submit handlers that happened to not ask for the form by reference.

#29.2 Fixed.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed fc939d4 and pushed to 8.x. Thanks!

diff --git a/core/modules/system/src/Tests/Form/FormDefaultHandlersTest.php b/core/modules/system/src/Tests/Form/FormDefaultHandlersTest.php
index 812f368..9c980a6 100644
--- a/core/modules/system/src/Tests/Form/FormDefaultHandlersTest.php
+++ b/core/modules/system/src/Tests/Form/FormDefaultHandlersTest.php
@@ -72,7 +72,7 @@ public function submitForm(array &$form, array &$form_state) {
   /**
    * Tests that default handlers are added even if custom are specified.
    */
-  function testLimitValidationErrors() {
+  function testDefaultAndCustomHandlers() {
     $form_state['values'] = array();
     $form_builder = $this->container->get('form_builder');
     $form_builder->submitForm($this, $form_state);

The method name is wrong - fixed on commit.

  • alexpott committed fc939d4 on 8.x
    Issue #2268761 by tim.plunkett: Remove support for function-based forms.
    
tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned

Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.