diff --git a/core/lib/Drupal/Core/Entity/ContentEntityFormController.php b/core/lib/Drupal/Core/Entity/ContentEntityFormController.php index d0383d6..b258508 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityFormController.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityFormController.php @@ -149,7 +149,7 @@ public function buildEntity(array $form, array &$form_state) { $entity = clone $this->entity; // First, extract values from widgets. - $extracted = $this->EntityFormHelper->extractWidgetsValue($entity, $form_state['form_display'], $form, $form_state); + $extracted = $this->EntityFormHelper->extractWidgetsValues($entity, $form_state['form_display'], $form, $form_state); // Then extract the values of fields that are not rendered through widgets, // by simply copying from top-level form values. This leaves the fields diff --git a/core/lib/Drupal/Core/Entity/ContentEntityFormHelper.php b/core/lib/Drupal/Core/Entity/ContentEntityFormHelper.php index de0b5e1..6cc8c5c 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityFormHelper.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityFormHelper.php @@ -1,4 +1,5 @@ The location of field values in $form_state['values'], + * '#entity_type' => The name of the entity type, + * '#bundle' => The name of the bundle, + * // One sub-array per field appearing in the entity, keyed by field name. + * // The structure of the array differs slightly depending on whether the + * // widget is 'single-value' (provides the input for one field value, + * // most common case), and will therefore be repeated as many times as + * // needed, or 'multiple-values' (one single widget allows the input of + * // several values, e.g checkboxes, select box...). + * 'field_foo' => array( + * '#access' => TRUE if the current user has 'edit' grants for the field, + * FALSE if not. + * 'widget' => array( + * '#field_name' => The name of the field, + * '#language' => $langcode, + * '#field_parents' => The 'parents' space for the field in the form, + * equal to the #parents property of the $form parameter received by + * field_attach_form(), + * '#required' => Whether or not the field is required, + * '#title' => The label of the field instance, + * '#description' => The description text for the field instance, + * + * // Only for 'single' widgets: + * '#theme' => 'field_multiple_value_form', + * '#cardinality' => The field cardinality, + * '#cardinality_multiple => TRUE if the field can contain multiple + * items, FALSE otherwise. + * // One sub-array per copy of the widget, keyed by delta. + * 0 => array( + * '#entity_type' => The name of the entity type, + * '#bundle' => The name of the bundle, + * '#field_name' => The name of the field, + * '#field_parents' => The 'parents' space for the field in the form, + * equal to the #parents property of the $form parameter, + * '#title' => The title to be displayed by the widget, + * '#default_value' => The field value for delta 0, + * '#required' => Whether the widget should be marked required, + * '#delta' => 0, + * // The remaining elements in the sub-array depend on the widget. + * '#type' => The type of the widget, + * ... + * ), + * 1 => array( + * ... + * ), + * + * // Only for multiple widgets: + * '#entity_type' => The name of the entity type, + * '#bundle' => $instance['bundle'], + * // The remaining elements in the sub-array depend on the widget. + * '#type' => The type of the widget, + * ... + * ), + * ... + * ), + * ) + * @endcode + * + * Additionally, some processing data is placed in $form_state, and can be + * accessed by field_form_get_state() and field_form_set_state(). + * * @param EntityInterface $entity * The entity. * @param EntityFormDisplayInterface $display * The form display containing the widget settings. * @param array $form - * The form. + * The form structure to fill in. This can be a full form structure, or a + * sub-element of a larger form. The #parents property can be set to + * control the location of submitted field values within + * $form_state['values']. If not specified, $form['#parents'] is set to an + * empty array, which results in field values located at the top-level of + * $form_state['values']. * @param array $form_state * The form state. */ @@ -47,18 +127,22 @@ public function attachWidgets(EntityInterface $entity, EntityFormDisplayInterfac // @todo hook_field_attach_form() ? // We still need a hook to allow alteration of "forms with widgets", because // the regular "entity form" alter hook won't run non "non enity forms"... - // Although, there's already hook_field_widget_form_alter() to alter each widget ? + // Although, there's already hook_field_widget_form_alter() to alter each widget ? } /** * Extracts field values from the submitted widget values into the entity. * + * This accounts for drag-and-drop reordering of field values, and filtering + * of empty values. + * * @param EntityInterface $entity * The entity. * @param EntityFormDisplayInterface $display * The form display containing the widget settings. * @param array $form - * The form. + * The form structure where field elements are attached to. This might be a + * full form structure, or a sub-element of a larger form. * @param array $form_state * The form state. * @@ -68,7 +152,7 @@ public function attachWidgets(EntityInterface $entity, EntityFormDisplayInterfac * any, do not correspond to widgets and should be extracted manually by * the caller if needed. */ - public function extractWidgetsValue(EntityInterface $entity, EntityFormDisplayInterface $display, array &$form, array &$form_state) { + public function extractWidgetsValues(EntityInterface $entity, EntityFormDisplayInterface $display, array &$form, array &$form_state) { $extracted = array(); foreach ($entity as $name => $items) { @@ -85,12 +169,29 @@ public function extractWidgetsValue(EntityInterface $entity, EntityFormDisplayIn /** * Validates submitted widget values and sets the corresponding form errors. * + * There are two levels of validation for fields in forms: widget validation + * and field validation. + * - Widget validation steps are specific to a given widget's own form + * structure and UI metaphors. They are executed during normal form + * validation, usually through Form API's #element_validate property. + * Errors reported at this level are typically those that prevent the + * extraction of proper field values from the submitted form input. + * - If no form / widget errors were reported for the field, field validation + * steps are performed according to the "constraints" specified by the + * field definition. Those are independent of the specific widget being + * used in a given form, and are also performed on REST entity submissions. + * + * This function performs field validation in the context of a form submission. + * It reports field constraint violations as form errors on the correct form + * elements. + * * @param EntityInterface $entity * The entity. * @param EntityFormDisplayInterface $display * The form display containing the widget settings. * @param array $form - * The form. + * The form structure where field elements are attached to. This might be a + * full form structure, or a sub-element of a larger form. * @param array $form_state * The form state. */ diff --git a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php index 7ae8698..944da20 100644 --- a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php +++ b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php @@ -58,6 +58,8 @@ class EditFieldForm extends FormBase { * The module handler. * @param \Drupal\Core\Entity\EntityStorageControllerInterface $node_type_storage * The node type storage. + * @param \Drupal\Core\Entity\ContentEntityFormHelper $entity_form_helper + * The ContentEntity form helper. */ public function __construct(TempStoreFactory $temp_store_factory, ModuleHandlerInterface $module_handler, EntityStorageControllerInterface $node_type_storage, ContentEntityFormHelper $entity_form_helper) { $this->tempStoreFactory = $temp_store_factory; @@ -189,7 +191,7 @@ protected function buildEntity(array $form, array &$form_state) { $entity = clone $form_state['entity']; $field_name = $form_state['field_name']; - $this->entityFormHelper->extractWidgetsValue($entity, $form_state['form_display'], $form, $form_state); + $this->entityFormHelper->extractWidgetsValues($entity, $form_state['form_display'], $form, $form_state); // @todo Refine automated log messages and abstract them to all entity // types: http://drupal.org/node/1678002. diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index 8341c7a..807fe56 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -11,15 +11,16 @@ /** * Exposes "pseudo-field" components on fieldable entities. * - * Field UI's "Manage fields" and "Manage display" pages let users re-order - * fields, but also non-field components. For nodes, these include the title - * and other elements exposed by modules through hook_form_alter(). + * Field UI's "Manage display" and "Manage form display" pages let users + * re-order fields rendered through the regular widget/formatter pipeline, but + * also other components: entity fields that are rendered through custom code, + * or other arbitrary components added through hook_form_alter() or + * hook_entity_view(). * * Fieldable entities or modules that want to have their components supported * should expose them using this hook. The user-defined settings (weight, * visible) are automatically applied on rendered forms and displayed entities - * in a #pre_render callback added by field_attach_form() and - * field_attach_view(). + * by ContentEntityFormController::form() and EntityViewBuilder::viewMultiple(). * * @see hook_field_extra_fields_alter() * @@ -162,8 +163,8 @@ function hook_field_info_alter(&$info) { * * Widgets are @link forms_api_reference.html Form API @endlink * elements with additional processing capabilities. The methods of the - * WidgetInterface object are typically called by the Field Attach API during - * the creation of the field form structure with field_attach_form(). + * WidgetInterface object are typically called by respective methods in the + * ContentEntityFormHelper class. * * @see field * @see field_types diff --git a/core/modules/field/field.deprecated.inc b/core/modules/field/field.deprecated.inc index 1358149..25b9c5e 100644 --- a/core/modules/field/field.deprecated.inc +++ b/core/modules/field/field.deprecated.inc @@ -438,83 +438,6 @@ function field_read_instances($conditions = array(), $include_additional = array /** * Adds form elements for all fields for an entity to a form structure. * - * The form elements for the entity's fields are added by reference as direct - * children in the $form parameter. This parameter can be a full form structure - * (most common case for entity edit forms), or a sub-element of a larger form. - * - * 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. - * - * For each call to field_attach_form(), field values are processed by calling - * field_attach_extract_form_values() on the same $form element. - * - * Sample resulting structure in $form: - * @code - * '#parents' => The location of field values in $form_state['values'], - * '#entity_type' => The name of the entity type, - * '#bundle' => The name of the bundle, - * // One sub-array per field appearing in the entity, keyed by field name. - * // The structure of the array differs slightly depending on whether the - * // widget is 'single-value' (provides the input for one field value, - * // most common case), and will therefore be repeated as many times as - * // needed, or 'multiple-values' (one single widget allows the input of - * // several values, e.g checkboxes, select box...). - * 'field_foo' => array( - * '#access' => TRUE if the current user has 'edit' grants for the field, - * FALSE if not. - * 'widget' => array( - * '#field_name' => The name of the field, - * '#language' => $langcode, - * '#field_parents' => The 'parents' space for the field in the form, - * equal to the #parents property of the $form parameter received by - * field_attach_form(), - * '#required' => Whether or not the field is required, - * '#title' => The label of the field instance, - * '#description' => The description text for the field instance, - * - * // Only for 'single' widgets: - * '#theme' => 'field_multiple_value_form', - * '#cardinality' => The field cardinality, - * '#cardinality_multiple => TRUE if the field can contain multiple items, - * FALSE otherwise. - * // One sub-array per copy of the widget, keyed by delta. - * 0 => array( - * '#entity_type' => The name of the entity type, - * '#bundle' => The name of the bundle, - * '#field_name' => The name of the field, - * '#field_parents' => The 'parents' space for the field in the form, - * equal to the #parents property of the $form parameter received by - * field_attach_form(), - * '#title' => The title to be displayed by the widget, - * '#default_value' => The field value for delta 0, - * '#required' => Whether the widget should be marked required, - * '#delta' => 0, - * // The remaining elements in the sub-array depend on the widget. - * '#type' => The type of the widget, - * ... - * ), - * 1 => array( - * ... - * ), - * - * // Only for multiple widgets: - * '#entity_type' => The name of the entity type, - * '#bundle' => $instance['bundle'], - * // The remaining elements in the sub-array depend on the widget. - * '#type' => The type of the widget, - * ... - * ), - * ... - * ), - * ) - * @endcode - * - * Additionally, some processing data is placed in $form_state, and can be - * accessed by field_form_get_state() and field_form_set_state(). - * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity for which to load form elements, used to initialize * default form values. @@ -545,20 +468,6 @@ function field_attach_form(EntityInterface $entity, &$form, &$form_state, $langc /** * Performs field validation against form-submitted field values. * - * There are two levels of validation for fields in forms: widget validation and - * and field validation. - * - Widget validation steps are specific to a given widget's own form structure - * and UI metaphors. They are executed through FAPI's #element_validate - * property during normal form validation. - * - Field validation steps are common to a given field type, independently of - * the specific widget being used in a given form. They are defined in the - * field type's implementation of hook_field_validate(). - * - * This function performs field validation in the context of a form submission. - * It converts field validation errors into form errors on the correct form - * elements. Fieldable entity types should call this function during their own - * form validation function. - * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity being submitted. The actual field values will be read * from $form_state['values']. @@ -580,9 +489,6 @@ function field_attach_form_validate(ContentEntityInterface $entity, $form, &$for /** * Populates an entity object with values from a form submission. * - * Currently, this accounts for drag-and-drop reordering of field values, and - * filtering of empty values. - * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity being submitted. The actual field values will be read * from $form_state['values']. @@ -598,7 +504,7 @@ function field_attach_form_validate(ContentEntityInterface $entity, $form, &$for * @deprecated as of Drupal 8.0. Use the entity system instead. */ function field_attach_extract_form_values(EntityInterface $entity, $form, &$form_state, array $options = array()) { - \Drupal::service('entity.content.form_helper')->extractWidgetsValue($entity, $form_state['form_display'], $form, $form_state); + \Drupal::service('entity.content.form_helper')->extractWidgetsValues($entity, $form_state['form_display'], $form, $form_state); } /** diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc index d5d058a..60fccbc 100644 --- a/core/modules/field/field.multilingual.inc +++ b/core/modules/field/field.multilingual.inc @@ -36,33 +36,10 @@ * languages, unless it is given a language code suggestion. Based on that * suggestion, _field_language_suggestion() determines the languages to act on. * - * Most field_attach_*() functions act on all available language codes, except - * for the following: - * - field_attach_form() only takes a single language code, specifying which - * language the field values will be submitted in. - * - field_attach_view() requires the language the entity will be displayed in. - * Since it is unknown whether a field translation exists for the requested - * language, the translation handler is responsible for performing one of the - * following actions: - * - Ignore missing translations, i.e. do not show any field values for the - * requested language. For example, see field_field_language_alter(). - * - Provide a value in a different language as fallback. By default, the - * fallback logic is applied separately to each field to ensure that there - * is a value for each field to display. - * The field language fallback logic relies on the global language fallback - * configuration. Therefore, the displayed field values can be in the - * requested language, but may be different if no values for the requested - * language are available. The default language fallback rules inspect all the - * enabled languages ordered by their weight. This behavior can be altered or - * even disabled by modules implementing hook_field_language_alter(), making - * it possible to choose the first approach. The display language for each - * field is returned by field_language(). - * * See @link field Field API @endlink for information about the other parts of * the Field API. */ - /** * Collects the available language codes for the given entity type and field. * diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php index e73b9db..690fbe3 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php @@ -318,22 +318,24 @@ function testFieldAttachCache() { } /** - * Test field_attach_form(). + * Test \Drupal\Core\Entity\ContentEntityFormHelper::attachWidgets(). * * This could be much more thorough, but it does verify that the correct * widgets show up. */ - function testFieldAttachForm() { + function testFieldAttachWidgets() { + $entity_form_helper = \Drupal::service('entity.content.form_helper'); + $this->createFieldWithInstance('_2'); $entity_type = 'entity_test'; $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $this->instance->bundle)); - // When generating form for all fields. + // Test generating widgets for all fields. + $display = entity_get_form_display($entity_type, $this->instance->bundle, 'default'); $form = array(); $form_state = form_state_defaults(); - $form_state['form_display'] = entity_get_form_display($entity_type, $this->instance->bundle, 'default'); - field_attach_form($entity, $form, $form_state); + $entity_form_helper->attachWidgets($entity, $display, $form, $form_state); $this->assertEqual($form[$this->field_name]['widget']['#title'], $this->instance->getLabel(), "First field's form title is {$this->instance->getLabel()}"); $this->assertEqual($form[$this->field_name_2]['widget']['#title'], $this->instance_2->getLabel(), "Second field's form title is {$this->instance_2->getLabel()}"); @@ -346,7 +348,7 @@ function testFieldAttachForm() { $this->assertEqual($form[$this->field_name_2]['widget'][$delta]['value']['#type'], 'textfield', "Second field's form delta $delta widget is textfield"); } - // When generating form for a single field (the second field). + // Test generating widgets for all fields. $display = entity_get_form_display($entity_type, $this->instance->bundle, 'default'); foreach ($display->getComponents() as $name => $options) { if ($name != $this->field_name_2) { @@ -355,8 +357,7 @@ function testFieldAttachForm() { } $form = array(); $form_state = form_state_defaults(); - $form_state['form_display'] = $display; - field_attach_form($entity, $form, $form_state, NULL, $options); + $entity_form_helper->attachWidgets($entity, $display, $form, $form_state); $this->assertFalse(isset($form[$this->field_name]), 'The first field does not exist in the form'); $this->assertEqual($form[$this->field_name_2]['widget']['#title'], $this->instance_2->getLabel(), "Second field's form title is {$this->instance_2->getLabel()}"); @@ -367,19 +368,21 @@ function testFieldAttachForm() { } /** - * Test field_attach_extract_form_values(). + * Test \Drupal\Core\Entity\ContentEntityFormHelper::extractWidgetsValues(). */ function testFieldAttachExtractFormValues() { + $entity_form_helper = \Drupal::service('entity.content.form_helper'); + $this->createFieldWithInstance('_2'); $entity_type = 'entity_test'; $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $this->instance->bundle)); // Build the form for all fields. + $display = entity_get_form_display($entity_type, $this->instance->bundle, 'default'); $form = array(); $form_state = form_state_defaults(); - $form_state['form_display'] = entity_get_form_display($entity_type, $this->instance->bundle, 'default'); - field_attach_form($entity_init, $form, $form_state); + $entity_form_helper->attachWidgets($entity_init, $display, $form, $form_state); // Simulate incoming values. // First field. @@ -417,9 +420,9 @@ function testFieldAttachExtractFormValues() { $form_state['values'][$this->field_name] = $values; $form_state['values'][$this->field_name_2] = $values_2; - // Call field_attach_extract_form_values() for all fields. + // Extract values for all fields. $entity = clone($entity_init); - field_attach_extract_form_values($entity, $form, $form_state); + $entity_form_helper->extractWidgetsValues($entity, $display, $form, $form_state); asort($weights); asort($weights_2); @@ -439,20 +442,20 @@ function testFieldAttachExtractFormValues() { $this->assertIdentical($entity->{$this->field_name_2}->getValue(), $expected_values_2, 'Submit filters empty values'); // Call field_attach_extract_form_values() for a single field (the second field). - foreach ($form_state['form_display']->getComponents() as $name => $options) { + foreach ($display->getComponents() as $name => $options) { if ($name != $this->field_name_2) { - $form_state['form_display']->removeComponent($name); + $display->removeComponent($name); } } $entity = clone($entity_init); - field_attach_extract_form_values($entity, $form, $form_state, $options); + $entity_form_helper->extractWidgetsValues($entity, $display, $form, $form_state); $expected_values_2 = array(); foreach ($weights_2 as $key => $value) { if ($key != 1) { $expected_values_2[] = array('value' => $values_2[$key]['value']); } } - $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'The first field does is empty in the entity object'); + $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'The first field is empty in the entity object'); $this->assertIdentical($entity->{$this->field_name_2}->getValue(), $expected_values_2, 'Submit filters empty values'); } diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php index 81ec2ff..9c0e384 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php @@ -531,10 +531,10 @@ function testFieldFormAccess() { // apart from #access. $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0)); + $display = entity_get_form_display($entity_type, $entity_type, 'default'); $form = array(); $form_state = form_state_defaults(); - $form_state['form_display'] = entity_get_form_display($entity_type, $entity_type, 'default'); - field_attach_form($entity, $form, $form_state); + \Drupal::service('entity.content.form_helper')->attachWidgets($entity, $display, $form, $form_state); $this->assertEqual($form[$field_name_no_access]['widget'][0]['value']['#entity_type'], $entity_type, 'The correct entity type is set in the field structure.'); $this->assertFalse($form[$field_name_no_access]['#access'], 'Field #access is FALSE for the field without edit access.'); diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc index 880636e..e562648 100644 --- a/core/modules/field/tests/modules/field_test/field_test.entity.inc +++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc @@ -37,85 +37,3 @@ function field_test_entity_info_translatable($entity_type = NULL, $translatable } return $stored_value; } - -/** - * Form combining two separate entities. - * - * @deprecated Use \Drupal\field_test\Form\FieldTestForm::testEntityNestedForm() - */ -function field_test_entity_nested_form($form, &$form_state, $entity_1, $entity_2) { - // First entity. - foreach (array('id', 'type') as $key) { - $form[$key] = array( - '#type' => 'value', - '#value' => $entity_1->$key->value, - ); - } - $form_state['form_display'] = entity_get_form_display($entity_1->entityType(), $entity_1->bundle(), 'default'); - field_attach_form($entity_1, $form, $form_state); - - // Second entity. - $form['entity_2'] = array( - '#type' => 'details', - '#title' => t('Second entity'), - '#tree' => TRUE, - '#parents' => array('entity_2'), - '#weight' => 50, - ); - foreach (array('id', 'type') as $key) { - $form['entity_2'][$key] = array( - '#type' => 'value', - '#value' => $entity_2->$key->value, - ); - } - $form_state['form_display'] = entity_get_form_display($entity_1->entityType(), $entity_1->bundle(), 'default'); - field_attach_form($entity_2, $form['entity_2'], $form_state); - - $form['save'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#weight' => 100, - ); - - return $form; -} - -/** - * Validate handler for field_test_entity_nested_form(). - */ -function field_test_entity_nested_form_validate($form, &$form_state) { - $entity_1 = entity_create('entity_test', array( - 'id' => $form_state['values']['id'], - 'type' => $form_state['values']['type'], - )); - field_attach_extract_form_values($entity_1, $form, $form_state); - field_attach_form_validate($entity_1, $form, $form_state); - - $entity_2 = entity_create('entity_test', array( - 'id' => $form_state['values']['entity_2']['id'], - 'type' => $form_state['values']['entity_2']['type'], - )); - field_attach_extract_form_values($entity_2, $form['entity_2'], $form_state); - field_attach_form_validate($entity_2, $form['entity_2'], $form_state); -} - -/** - * Submit handler for field_test_entity_nested_form(). - */ -function field_test_entity_nested_form_submit($form, &$form_state) { - $entity_1 = entity_create('entity_test', array( - 'id' => $form_state['values']['id'], - 'type' => $form_state['values']['type'], - )); - field_attach_extract_form_values($entity_1, $form, $form_state); - $entity_1->save(); - - $entity_2 = entity_create('entity_test', array( - 'id' => $form_state['values']['entity_2']['id'], - 'type' => $form_state['values']['entity_2']['type'], - )); - field_attach_extract_form_values($entity_2, $form['entity_2'], $form_state); - $entity_2->save(); - - drupal_set_message(t('test_entities @id_1 and @id_2 have been updated.', array('@id_1' => $entity_1->id(), '@id_2' => $entity_2->id()))); -} diff --git a/core/modules/field/tests/modules/field_test/field_test.routing.yml b/core/modules/field/tests/modules/field_test/field_test.routing.yml index b7ba00c..cfd60fa 100644 --- a/core/modules/field/tests/modules/field_test/field_test.routing.yml +++ b/core/modules/field/tests/modules/field_test/field_test.routing.yml @@ -2,7 +2,7 @@ field_test.entity_nested_form: path: '/test-entity/nested/{entity_1}/{entity_2}' defaults: _title: 'Nested entity form' - _content: '\Drupal\field_test\Form\FieldTestForm::testEntityNestedForm' + _form: '\Drupal\field_test\Form\NestedEntityTestForm' options: parameters: entity_1: diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Form/FieldTestForm.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Form/FieldTestForm.php deleted file mode 100644 index 8c9e913..0000000 --- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Form/FieldTestForm.php +++ /dev/null @@ -1,25 +0,0 @@ -entityFormHelper = $entity_form_helper; + } + + /** + * {@inheritdoc] + */ + public static function create(ContainerInterface $container) { + return new static($container->get('entity.content.form_helper')); + } + + /** + * {@inheritdoc] + */ + public function getFormId() { + return 'field_test_entity_nested_form'; + } + + /** + * {@inheritdoc] + */ + public function buildForm(array $form, array &$form_state, EntityInterface $entity_1 = NULL, EntityInterface $entity_2 = NULL) { + // First entity. + $form_state['entity_1'] = $entity_1; + $form_state['form_display_1'] = entity_get_render_form_display($entity_1, 'default'); + $this->entityFormHelper->attachWidgets($entity_1, $form_state['form_display_1'], $form, $form_state); + + // Second entity. + $form_state['entity_2'] = $entity_2; + $form_state['form_display_2'] = entity_get_render_form_display($entity_2, 'default'); + $form['entity_2'] = array( + '#type' => 'details', + '#title' => t('Second entity'), + '#tree' => TRUE, + '#parents' => array('entity_2'), + '#weight' => 50, + ); + $this->entityFormHelper->attachWidgets($entity_2, $form_state['form_display_2'], $form['entity_2'], $form_state); + + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#weight' => 100, + ); + + return $form; + } + + /** + * {@inheritdoc] + */ + public function validateForm(array &$form, array &$form_state) { + $entity_1 = $form_state['entity_1']; + $this->entityFormHelper->extractWidgetsValues($entity_1, $form_state['form_display_1'], $form, $form_state); + $this->entityFormHelper->validateWidgetsValues($entity_1, $form_state['form_display_1'], $form, $form_state); + + $entity_2 = $form_state['entity_2']; + $this->entityFormHelper->extractWidgetsValues($entity_2, $form_state['form_display_2'], $form['entity_2'], $form_state); + $this->entityFormHelper->validateWidgetsValues($entity_2, $form_state['form_display_2'], $form['entity_2'], $form_state); + } + + /** + * {@inheritdoc] + */ + public function submitForm(array &$form, array &$form_state) { + $entity_1 = $form_state['entity_1']; + $entity_1->save(); + + $entity_2 = $form_state['entity_2']; + $entity_2->save(); + + drupal_set_message($this->t('test_entities @id_1 and @id_2 have been updated.', array('@id_1' => $entity_1->id(), '@id_2' => $entity_2->id()))); + } + +} diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 9194d3f..cde8286 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -32,13 +32,12 @@ * - Entity hooks: Generic hooks for "entity" operations. These are always * invoked on all modules. * - * Here is a list of the node and entity hooks that are invoked, field - * operations, and other steps that take place during node operations: + * Here is a list of the node and entity hooks that are invoked, and other + * steps that take place during node operations: * - Instantiating a new node: * - hook_node_create() (all) * - hook_entity_create() (all) * - Creating a new node (calling $node->save() on a new node): - * - field_attach_presave() * - hook_node_presave() (all) * - hook_entity_presave() (all) * - Node and revision records are written to the database @@ -47,7 +46,6 @@ * - hook_node_access_records() (all) * - hook_node_access_records_alter() (all) * - Updating an existing node (calling $node->save() on an existing node): - * - field_attach_presave() * - hook_node_presave() (all) * - hook_entity_presave() (all) * - Node and revision records are written to the database @@ -94,7 +92,6 @@ * existing node, it will already be loaded; see the Loading section above): * - hook_node_prepare_form() (all) * - hook_entity_prepare_form() (all) - * - field_attach_form() * - Validating a node during editing form submit (calling * node_form_validate()): * - hook_node_validate() (all) diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module index a932868..54281d2 100644 --- a/core/modules/system/tests/modules/form_test/form_test.module +++ b/core/modules/system/tests/modules/form_test/form_test.module @@ -1972,13 +1972,6 @@ function form_test_form_user_register_form_alter(&$form, &$form_state) { '#value' => t('Rebuild'), '#submit' => array('form_test_user_register_form_rebuild'), ); - // If requested, add the test field by attaching the node page form. - if (\Drupal::request()->request->has('field')) { - $node = entity_create('node', array( - 'type' => 'page', - )); - field_attach_form($node, $form, $form_state); - } } /**