I've updated this issue to describe a larger issue I discovered while seeking support on a smaller issue. Basically, the gist is the commerce registration module calls a few functions around submission of the checkout pane that work outside of the Field API's expected process, which results in messed up entity structures and a bad $form_state.

Immediate form state issues on submission

commerce_registration_information_checkout_form() mishandles entity forms in the way it adds them to the checkout pane. By disregarding the $form_state when calling regisration_form() and by trying to adjust the fields' parents with a foreach loop after calling field_attach_form(), the $form_state array gets mangled and field elements are rendered with duplicate instance ID's.

This breaks AJAX callbacks and breaks field types that require a properly constructed $form_state array. I originally found this when the Inline Form Entity module was choking on missing data from the $form_state array.

There is a patch below to address part of this issue (#5)

Malformed entity after submission callbacks

Then, the submit handler calls commerce_registration_entity_update_fields(), which takes $form_state values and puts them directly into the entity object for saving as-is. Many field modules will alter the structure of the data in field_attach_submit(), but these alterations are lost when commerce_registration calls commerce_registration_entity_update_fields().

Original issue:

When I am using an Entity reference field on the Registration entity with the Inline Entity Form widget, the Inline Entity Form widget fails because values are missing in the $form_state, even though they are clearly present in the $form that is rendered onto the page.

Requirements of Inline Entity Form field widget

When the Inline Entity Form field widget prints on the page, it uses a value typed form element to pass information to the form about the "bundle" that needs to be created inline. (It also uses a 'select' type if the widget is configured to handle a variety of bundles, but the problem is the same in either case)

The Inline Entity Form uses a button to submit this information to an ajax callback.

Missing values in IEF callback

The problem is that for some reason the $form_state['values'] array is missing the "bundle" value. It's NULL.
I've tried changing the element to be a "hidden" value, a "textfield" and a few others, but no matter what I do, the value is missing from the $form_state['values'] array in the submit callback for the IEF Form.

The result of the missing value is this error in the AJAX callback:
EntityMalformedException: Missing bundle property on entity of type node. in entity_extract_ids()

Source of the problem

I've tried using this exact field on several different entity types and have had no issues. I used Devel to dpm() the form and I can see that the "bundle" form element is definitely in the form, and definitely has the right value in it.

The problem is in the commerce_registration_information_checkout_form(). The element "bundle" value is lost somehow in the way that form is built. I can't tell if it's in the validation, the submit handler or the way simply the form is built, but something about that form prevents the value from being passed to the $form_state on submit, even though it is in the $form.

Other related issues

#1787046: Setting #field_parents for the conditional fields module
#1831562: Unlimited file fields on registration entities cause registration_save errors
#1778464: SQL error when saving registration with multi-value entity reference or field collection field
#1803568: Multiple calls to field_attach_form() causes conflicts
#1835852: Multiple same-entity add/edit forms on one page results in duplicate DOM Id's

Comments

tmsimont’s picture

Issue summary: View changes

removing unnecessary header

tmsimont’s picture

Issue summary: View changes

clarification about "dropping the value" vs. "not allowing it into the form_state array"

tmsimont’s picture

I updated the issue to be more clear about an important distinction:
The IEF form values are correctly rendered into the $form array on page load. They are just missing from the $form_state['values'] array on submit callback. It's almost like the $form_state is somehow built before the IEF form gets added to the $form.

tmsimont’s picture

I ran a few tests and found that this has to be related to the commerce_registration_information_checkout_form() function, because the registration form works great on standard registration entities on ER's default /registration/%registration/edit path (which uses registration_form() directly)

tmsimont’s picture

Title: The CR checkout pane form misuses the Form API when handling entity fields, causing numerous issues » Inline Entity Form field widget conflict with Registration form
Version: 7.x-2.x-dev » 7.x-2.0-beta3
Category: bug » support
Status: Needs review » Active

Around line 86 of commerce_registration.checkout_pane.inc there is some odd use of the registration_form() function that seems to be the source of this problem:

  $tmp_form_state = array();
  $register_form = registration_form($form, $tmp_form_state, $entity);

The rest of the function then "glues" together parts of the registration form with the checkout pane form. If i drop out all of the glue and pass the real $form_state into registration_form(), then my $form_state passes the right data through to the IEF callbacks and my error goes away.

Can anyone explain why there is a need to add the registration form is such a strange way (with use of a phony $tmp_form_state and odd corrections of $form['#parents'], etc?

tmsimont’s picture

Title: Inline Entity Form field widget conflict with Registration form » The CR checkout pane form misuses the Form API when handling entity fields, causing numerous issues
Category: support » bug
StatusFileSize
new2.38 KB

I'm changing the title to more accurately describe this issue.

The problem arises with IEF because IEF needs the form #parents to be constructed properly in order to function. The #parents array is glued to together in commerce_registration_information_checkout_form() in a way that breaks the $form_state structure.

This reconstruction of the parents hack can easily be avoided with a small alteration to the code described above.

Rather than going back into the form after calling field_attach_form() and trying to alter the parents configured there, the parents can be set on the $form[$prodkey][$prodkey . '-reg-' . $i] element before calling field_attach_form().

This is how the documentation on field_attach_form() says how to alter the parents in such situations:

By default, submitted field values appear at the top-level of $form_state['values']. A different location within $form_state['values'] can be specified by setting the '#parents' property on the incoming $form parameter. Because of name clashes, two instances of the same field cannot appear within the same $form element, or within the same '#parents' space.

Unfortunately there are some other issues with the way the registration entity form is attached. The instance ID's on individual entity fields are not unique. This causes failures in AJAX callbacks and in the Conditional Fields module when there is more than 1 registration form on the pane.

Attached is a patch to illustrate the correct use of field_attach_form() and the $form['#parents'] param, but I think this needs more work... more patches to come..

tmsimont’s picture

Issue summary: View changes

more clarification and formatting

tmsimont’s picture

Issue summary: View changes

updating to cover broader problem, rather than specific symptom

tmsimont’s picture

Status: Active » Needs review
StatusFileSize
new2.38 KB

Reattaching last patch for review. After some testing on my end I can see that this does not noticeably break a basic commerce registration process. Conditional fields and IEF fields, however, are still not fixed.

More work on the way..

Status: Needs review » Needs work

The last submitted patch, commerce_registration-form_api_usage-1833924-4.patch, failed testing.

jpontani’s picture

Version: 7.x-2.0-beta3 » 7.x-2.x-dev
Status: Needs work » Needs review

Applied the patch to dev, ce8fe72.

tmsimont’s picture

Title: Inline Entity Form field widget conflict with Registration form » The CR checkout pane form misuses the Form API when handling entity fields, causing numerous issues
Version: 7.x-2.0-beta3 » 7.x-2.x-dev
Category: support » bug
Status: Active » Needs review

Thanks jpontani

I found another issue related to this process that was causing more problems for me with use of IEF:

commerce_registration_entity_update_fields() will take form state values and assume they are ready for direct insertion to the entity object:


  foreach ($values as $key => $value) {
    $entity->$key = $value;
  }

The problem is that some fields, like IEF, will execute code in field_attach_submit() that will manipulate the form_state, modify the entity object and prepare the field for saving.

The problem is that after this might happen, commerce_registration_entity_update_fields() bulldozes the construction of the entity's field by overwriting it with the exact structure of the $form_state array.

I have to call it a day today, but I will pick it up here and patch tomorrow.

tmsimont’s picture

Issue summary: View changes

added related issue to summary

tmsimont’s picture

Issue summary: View changes

found another related issue

tmsimont’s picture

Issue summary: View changes

edited to be more about found deeper issues, less about original issue

tmsimont’s picture

Title: The CR checkout pane form misuses the Form API when handling entity fields, causing numerous issues » The CR checkout pane form misuses the Field API when handling entity fields, causing numerous issues
tmsimont’s picture

Issue summary: View changes

found another related issue

tmsimont’s picture

ok, so simply removing commerce_registration_entity_update_fields() does the trick just fine.

I've tested this fields with unlimited values (text, file upload and entity reference) and it works great.

I imagine commerce_registration_entity_update_fields() used to be necessary before the last patch I put up that prevents $form_state from coming through all messed up. As far as I can tell the "update fields" function is longer necessary. I need to test results on multiple registrations at once -- i was seeing "Instance ID" problems on individual fields earlier.

Attached is an updated patch.

Status: Needs review » Needs work
tmsimont’s picture

Status: Needs work » Needs review

crap there are some issues here i don't think this patch aligns with the most recent 7.x-2.x version -- re-rolling...

tmsimont’s picture

re-rolled, tested with unlimited values on individual text, file upload and entity reference fields. Still need to fix possible issues with multiple registrations per page.

tmsimont’s picture

so far so good on multiple registrations with a variety unlimited field types. IEF is acting strange on the AJAX callback, but that might actually turn out to be an issue with IEF

tmsimont’s picture

I'm still having issues on multiple registrations at once: both the conditional fields module and the IEF module don't do well with the second registration form on one page. The IEF module uses the field instance ID on to identify an area for JS to manipulate. I think conditional fields does the same thing. The problem is that neither module expects to find the entity add/edit form loaded twice onto one page. I wonder which would make more sense:

  • refactor this module to use mulitple checkout pages, one for each registration
  • patch IEF and conditional fields to allow multiple entity forms on one page

@jpontani, any thoughts?

tmsimont’s picture

nevermind!

I think the issues I'm still seeing are in fact IEF/Conditional Fields issue. If #13 is committed I think this issue can be closed.

IEF/Conditional Fields issues:
#1803568: Multiple calls to field_attach_form() causes conflicts
#1740074: Fix IEF to work when IEF is nested into another IEF
#1835852: Multiple same-entity add/edit forms on one page results in duplicate DOM Id's

tmsimont’s picture

Issue summary: View changes

patch referenced addresses part of the issue, not the whole issue

tmsimont’s picture

Status: Needs review » Needs work

crap this breaks the default values of "This registration is for:" when using the "Go Back" button

tmsimont’s picture

Status: Needs work » Needs review

nevermind -- that's an unrelated issue. The current 7.x-2.x branch does not work when using "Go Back" even without the mod above. I still stand by #13!

tmsimont’s picture

@jpontani -- did #13 get committed? I'm trying to re-roll this to the most recent version of 7.x-2.x and it looks like it did.

however, there is still a documentation reference to the removed function -- updated patch addresses this

jpontani’s picture

Status: Needs review » Fixed

Yea, sorry I thought I had posted saying that. About to do another commit with the removed documentation reference. Going to mark this issue as fixed as the appropriate fixes to CR have been applied, just need to wait on the patch to Conditional Fields/IEF.

Status: Fixed » Closed (fixed)

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

Anonymous’s picture

Issue summary: View changes

more related issues