Download & Extend

Move $form['#field'] meta information into $form_state

Project:Drupal core
Version:7.x-dev
Component:field system
Category:task
Priority:critical
Assigned:yched
Status:closed (fixed)
Issue tags:API change, D7 Form API challenge

Issue Summary

Field API currently stores meta information about the fields being edited in $form['#fields']. For each field, $form['#fields'][$field_name] has:
- 'field', 'instance': the full $field and $instance definitions.
This is the information that all widget processing callbacks (FAPI process, validate, value callbacks) are supposed to use if they need some info about the field or instance - NOT field_info_field() or field_info_instance(). This lets us build form elements for 'variants' of a field, different from the officially stored definition - for instance in views exposed filters, or in Field UI admin screens.
- 'form_path': path to the field form element within the $form structure (what Form API actually calls #array_parents).
This is used in the AJAX 'add more' handler, and in the assignment of field validation errors down to actual form elements.

This information clutters the $form array, and makes a print_r($form) unreadable. This really belongs in $form_state.
We can do that now that #634440: Remove auto-rebuilding magic for $form_state['storage'] is in.

What the patch does:

  1. removes $form['#fields'] and introduces $form_state['storage']['field_info'] - for each field:
    $form_state['storage']['field_info'][$field_name][$language] = array(
      'field' => $field definition,
      'instance' => $instance definition,
      'array_parents' => (previoulsy 'form_path') FAPI #array_parents : path to the field elements in $form
      'parents' => = FAPI #parents : path to the field values in $form_state['values']
      'errors' => the array of field-level errors reported by hook_field_validate()
    );
  2. fixes widgets that were incorrectly relying on field_info_[field|instance]() instead of $form['#fields'], and also clearly documents that practice in hook_field_widget() (actually that's still a @todo in this version of the patch)
  3. makes field_add_more_js() and field_default_form_errors() use the $form_state 'array_parents' info (prevsiously used the info from $form['#fields']).
  4. makes field_default_extract_form_values() use the $form_state 'parents' info instead of assuming values are in $form_state['values'][$field_name][$language]
  5. automatically populates 'parents' and 'array_parents' at #after_build time.
  6. adds $form and $form_state args to hook_field_widget_error(). That's a widget hook, those arguments belong here, even if most implementations probably won't need them (current ones in core do not). API addition rather that API change, since nothing breaks if a given hook overlooks those additional args.

Worthy gains, aside from uncluttering $form:
- This ensures that Field API form features (add more, error reporting, saving field values) do not break when some hook_form_alter() changes the form structure, or when some field widget is stuck at a random place in a random form. For instance, this should allow an 'add more' button in the 'default value' widget in Field UI.
- $form_state['storage']['field_info'] being keyed by langcode, this is also an important step to allow forms with multiple languages ('side by side translate' in contrib...)

Patch still has a few documentation @todos, posting it here for peer and bot review.

AttachmentSizeStatusTest resultOperations
field_form_state.patch31.82 KBIdleFailed on MySQL 5.0 ISAM, with: 15,256 pass(es), 22 fail(s), and 144 exception(es).View details

Comments

#1

Status:active» needs review

Minor FAPI bug I found out along the way: #641544: FAPI #type 'container' ids generated twice

+ forgot to set to CNR

#2

uh oh. Using #after_build to shift #parents and #array_parents into $form_state...? That, we need to think about.

#3

Also linking back to the dupe #627730: Make field forms resilient to form_alter() structure changes, which identified the #parents issue in fieldable entity forms in the first place.

#4

Status:needs review» needs work

The last submitted patch failed testing.

#5

Status:needs work» needs review

Argh @ translation tests.

Well, for now, reroll after #619230: Update 'default field value' UI code for D7 Field API and fix for the 'Field attach' failure.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-5.patch34.46 KBIdleFailed on MySQL 5.0 ISAM, with: 15,306 pass(es), 21 fail(s), and 144 exception(es).View details

#6

Status:needs review» needs work

The last submitted patch failed testing.

#7

Status:needs work» needs review

Attached patch should fix the TF failures.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-7.patch34.82 KBIdleFAILED: [[SimpleTest]]: [MySQL] Unable to apply patch field_form_state-641670-7.patch.View details

#8

Priority:normal» critical

OK, reducing the scope a little.

Attached patch removes the #parents part - automatic extraction of #parents at after_build time, + using the 'parents' in field_default_extract_form_values().
I still think it's a good approach, but:
- this can be debated in #627730: Make field forms resilient to form_alter() structure changes,
- there are other places in core that currently rely on a hardcoded location in $form_state['values'][$field_name][$langcode]: file_field_widget(), locale_field_node_form_update_field_language(), and changing those would take the patch too far off,
- I don't want this to stop the $form['#fields'] -> $form_state['storage']['field_info'] change from getting in.

So, updated patch does as described in the OP without item 4 and the 'parents' component in $form_state['storage']['field_info'].

+ Marking as critical, since with the current code, a contrib module form_altering the structure of the $form without updating $form['#fields']'s 'form_path' accordingly causes field-level validation errors not to be reported on the form.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-8.patch34.83 KBIdlePassed on all environments.View details

#9

Status:needs review» needs work

The last submitted patch failed testing.

#10

Status:needs work» needs review

yched requested that failed test be re-tested.

#11

Rerolled after #639428: Remove #process pattern from text field (no more changes needed in text.module)

AttachmentSizeStatusTest resultOperations
field_form_state-641670-11.patch32.26 KBIdlePassed on all environments.View details

#12

Rerolled after #635094: Unify "language neutral" language codes

AttachmentSizeStatusTest resultOperations
field_form_state-641670-12.patch32.15 KBIdlePassed on all environments.View details

#13

Assigned to:Anonymous» yched

easier tracking

#14

Rerolled.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-14.patch32.17 KBIdlePassed on all environments.View details

#15

Rerolled.

Patch summary, updated from the OP:

Field API currently stores meta information about the fields being edited in $form['#fields']. For each field, $form['#fields'][$field_name] has:
- 'field', 'instance': the full $field and $instance definitions.
This is the information that all widget processing callbacks (FAPI process, validate, value callbacks) are supposed to use if they need some info about the field or instance - NOT field_info_field() or field_info_instance(). This lets us build form elements for 'variants' of a field, different from the officially stored definition - for instance in views exposed filters, or in Field UI admin screens.
- 'form_path': path to the field form element within the $form structure (what Form API actually calls #array_parents).
This is used in the AJAX 'add more' handler, and in the assignment of field validation errors down to actual form elements.

This information bloats $form and makes a print_r($form) tedious. This belongs in $form_state, but couldn't live there before $form_state persistence and auto-rebuild behavior got cleaned.
Also, not all FAPI callbacks receive $form (some receive an $element), but all receive $form_state.

1. removes $form['#fields'] and introduces $form_state['storage']['field_info'] - for each field:

      $form_state['storage']['field_info'][$field_name][$language] = array(
        'field' => $field definition,
        'instance' => $instance definition,
        'array_parents' => (previously 'form_path') FAPI #array_parents : path to the field elements in $form
        'errors' => the array of field-level errors reported by hook_field_validate()
      );

2. fixes widgets that were incorrectly relying on field_info_[field|instance]() instead of $form['#fields'], and also clearly documents that practice in hook_field_widget() (actually that's still a @todo in this version of the patch)

3. makes field_add_more_js() and field_default_form_errors() use the $form_state 'array_parents' info (prevsiously used the info from $form['#fields']).

4. automatically populates 'array_parents' at #after_build time.

5. adds $form and $form_state args to hook_field_widget_error(). That's a widget hook, those arguments belong here, even if most implementations probably won't need them (current ones in core do not). API addition rather that API change, since nothing breaks if a given hook overlooks those additional args.

'crtical' because with the current code, a contrib module form_altering the structure of the $form without updating $form['#fields']'s 'form_path' accordingly causes field-level validation errors not to be reported on the form and thus bypassed.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-15.patch31.53 KBIdlePassed on all environments.View details

#16

Overall, I like all of the changes in this patch. I was worried about #after_build before, but then again, that's probably the right point to do this, and after all, the originally intended purpose of #after_build.

+++ modules/field/field.form.inc 4 Jan 2010 19:41:48 -0000
@@ -17,16 +17,16 @@ function field_default_form($obj_type, $
+  // Store field information in $form_state['storage'].
+  $form_state['storage']['field_info'][$field_name][$langcode] = array(

Since we removed the limitation of $form_state['storage'], we should now store this information by module name, i.e.

$form_state['field']

Powered by Dreditor.

#17

So let's see what the bot thinks of this (in-patch-rename).

AttachmentSizeStatusTest resultOperations
drupal.field-form-state.17.patch31.24 KBIdlePassed on all environments.View details

#18

Also, tagging.

#19

I agree with the change.

#20

All in all, it's probably still sun who can RTBC ;-).

#21

Issue tags:+API change

We still need to fill out those @todos though.

#22

Ah true, I left dome docs @todos pending for an initial approval of the approach :-/
Will try to add those asap.

#23

Fixed the doc @todos.

Patch summary is in #15.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-23.patch34.57 KBIdlePassed on all environments.View details

#24

Awesome!

+++ modules/field_ui/field_ui.admin.inc 15 Jan 2010 20:31:48 -0000
@@ -1196,6 +1196,7 @@
+    '#parents' => array(),

uhm, err, why that? :)

+++ modules/field/field.attach.inc 15 Jan 2010 20:31:41 -0000
@@ -400,6 +400,79 @@
+ *     '#field_name' => the name of the field

Missing trailing commma.

+++ modules/field/field.attach.inc 15 Jan 2010 20:31:41 -0000
@@ -400,6 +400,79 @@
+ *        // Only for 'single' widgets:
+ *       '#theme' => 'field_multiple_value_form',

Wrong indentation for inline comment.

+++ modules/field/field.attach.inc 15 Jan 2010 20:31:41 -0000
@@ -400,6 +400,79 @@
+ *   'field_foo' => array (
+ *     $langcode => array (

Spaces before opening parenthesis.

+++ modules/field/field.attach.inc 15 Jan 2010 20:31:41 -0000
@@ -414,73 +487,8 @@
+ *   parameter. Processing information is added by reference in
+ *   $form_state['field'].

+++ modules/field/field.form.inc 15 Jan 2010 20:31:41 -0000
@@ -17,16 +17,16 @@
+  $form_state['field'][$field_name][$langcode] = array(
     'field' => $field,
     'instance' => $instance,
+    // This entry will be populated at form build time.
+    'array_parents' => array(),
+    // This entry will be populated at form validation time.
+    'errors' => array(),
   );

The docs state that the values are added by reference, but this doesn't seem to be the case?

+++ modules/field/field.api.php 15 Jan 2010 20:31:39 -0000
@@ -609,15 +609,25 @@
+ * (regular object edit form, 'default vale' input in the field settings form,

Typo in 'vale' here.

+++ modules/field/field.api.php 15 Jan 2010 20:31:39 -0000
@@ -609,15 +609,25 @@
+ * - $form_state['field'][$field_name][$langcode]['field'],
+ * - $form_state['field'][$field_name][$langcode]['instance'].

We can remove the trailing punctuation here.

Powered by Dreditor.

#25

+++ modules/field_ui/field_ui.admin.inc 15 Jan 2010 20:31:48 -0000
@@ -1196,6 +1196,7 @@
+    '#parents' => array(),

That's for the widget element used as 'default value' input in the field settings form. That change goes along with those in the _validate and _submit form handlers:
+++ modules/field_ui/field_ui.admin.inc 15 Jan 2010 20:31:48 -0000
@@ -1217,25 +1214,22 @@
-    // Extract field values.
-    $items = array();
-    $form_state_copy = array('values' => $instance['default_value_widget']);
-    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state_copy);
(...)
+  $items = array();
+  field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);

It's a fine change, except I guess it doesn't really belong in this patch now (the initial patch in the thread ventured into 'parents' and $form_state ['values'] land).

I removed those hunks from the patch.

The docs state that the values are added by reference, but this doesn't seem to be the case?

Hm, the 'array_parents' and 'errors' entries are initialized as empty arrays by ref in f_attach_form() / f_default_form(). The doc also says that they are actually populated respectively at form build time and form validation time.

Other remarks fixed.

Powered by Dreditor.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-25.patch31.54 KBIdlePassed on all environments.View details

#26

Spin off for the '#parents' => array() hunks in field_ui 'edit default value' : #686226: Nicer code in 'default value' input handling

#27

Status:needs review» reviewed & tested by the community

Somehow I only thought I'd have this marked as RTBC already. ;) Good job, yched!

#28

Status:reviewed & tested by the community» needs work

Yikes. This is a huge patch, but seems to mostly just be making the same basic changes over and over.

I was going to commit this, but it no longer applies. Can I have a re-roll please?

#29

Status:needs work» reviewed & tested by the community

Rerolled - was a hunk for a trailing whitespace fix that got committed meanwhile.

AttachmentSizeStatusTest resultOperations
field_form_state-641670-29.patch31.04 KBIdlePASSED: [[SimpleTest]]: [MySQL] 17,606 pass(es).View details

#30

Status:reviewed & tested by the community» fixed

Thanks! Committed to HEAD.

#32

Status:fixed» needs review

#7: field_form_state-641670-7.patch queued for re-testing.

#33

Status:needs review» fixed

Not sure why this was reopened ?

#34

Status:fixed» closed (fixed)

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

nobody click here