diff --git a/src/Plugin/Field/FieldWidget/InlineEntityFormComplex.php b/src/Plugin/Field/FieldWidget/InlineEntityFormComplex.php index 0859058..f52d21d 100644 --- a/src/Plugin/Field/FieldWidget/InlineEntityFormComplex.php +++ b/src/Plugin/Field/FieldWidget/InlineEntityFormComplex.php @@ -3,6 +3,8 @@ namespace Drupal\inline_entity_form\Plugin\Field\FieldWidget; use Drupal\Component\Utility\NestedArray; +use Drupal\Component\Utility\Tags; +use Drupal\Core\Entity\Element\EntityAutocomplete; use Drupal\Core\Entity\EntityDisplayRepositoryInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; @@ -566,12 +568,69 @@ class InlineEntityFormComplex extends InlineEntityFormBase implements ContainerF // If the inline entity form is still open, then its entity hasn't // been transferred to the IEF form state yet. if (empty($values) && !empty($widget_state['form'])) { - // @todo Do the same for reference forms. if ($widget_state['form'] == 'add') { $element = NestedArray::getValue($form, [$field_name, 'widget', 'form']); $entity = $element['inline_entity_form']['#entity']; $values[] = ['entity' => $entity]; } + else if ($widget_state['form'] == 'ief_add_existing') { + $element = NestedArray::getValue($form, [$field_name, 'widget', 'form'])['entity_id']; + if (!empty($element['#value'])) { + $options = array( + 'target_type' => $element['#target_type'], + 'handler' => $element['#selection_handler'], + 'handler_settings' => $element['#selection_settings'], + ); + /** @var /Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler */ + $handler = \Drupal::service('plugin.manager.entity_reference_selection') + ->getInstance($options); + $input_values = $element['#tags'] ? Tags::explode($element['#value']) : array($element['#value']); + + foreach ($input_values as $input) { + $match = EntityAutocomplete::extractEntityIdFromAutocompleteInput($input); + if ($match === NULL) { + // Try to get a match from the input string when the user didn't use + // the autocomplete but filled in a value manually. + $entities_by_bundle = $handler->getReferenceableEntities($input, '='); + $entities = array_reduce($entities_by_bundle, function ($flattened, $bundle_entities) { + return $flattened + $bundle_entities; + }, []); + $params = array( + '%value' => $input, + '@value' => $input, + ); + if (empty($entities)) { + $form_state->setError($element, t('There are no entities matching "%value".', $params)); + } + elseif (count($entities) > 5) { + $params['@id'] = key($entities); + // Error if there are more than 5 matching entities. + $form_state->setError($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)".', $params)); + } + elseif (count($entities) > 1) { + // More helpful error if there are only a few matching entities. + $multiples = array(); + foreach ($entities as $id => $name) { + $multiples[] = $name . ' (' . $id . ')'; + } + $params['@id'] = $id; + $form_state->setError($element, t('Multiple entities match this reference; "%multiple". Specify the one you want by appending the id in parentheses, like "@value (@id)".', array('%multiple' => implode('", "', $multiples)) + $params)); + } + else { + // Take the one and only matching entity. + $values += array( + 'target_id' => key($entities), + ); + } + } + else { + $values += array( + 'target_id' => $match, + ); + } + } + } + } } // Sort values by weight. uasort($values, '\Drupal\Component\Utility\SortArray::sortByWeightElement'); diff --git a/src/Tests/ComplexWidgetWebTest.php b/src/Tests/ComplexWidgetWebTest.php index 974c8a0..121024f 100644 --- a/src/Tests/ComplexWidgetWebTest.php +++ b/src/Tests/ComplexWidgetWebTest.php @@ -432,6 +432,42 @@ class ComplexWidgetWebTest extends InlineEntityFormTestBase { } /** + * Tests if referencing an existing entity works without submitting the form. + */ + public function testReferencingExistingEntitiesNoSubmit() { + // Allow addition of existing nodes. + $this->setAllowExisting(TRUE); + $title = $this->randomMachineName(); + + $this->drupalCreateNode([ + 'type' => 'ief_reference_type', + 'title' => $title, + 'first_name' => $this->randomMachineName(), + 'last_name' => $this->randomMachineName(), + ]); + $node = $this->drupalGetNodeByTitle($title); + $this->assertTrue($node, 'Created ief_reference_type node "' . $node->label() . '"');; + + $this->drupalGet($this->formContentAddUrl); + $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-multi-actions-ief-add-existing"]')); + $this->assertResponse(200, 'Opening inline form for existing entity was successful.'); + + $parent_title = $this->randomMachineName(); + $edit = [ + 'multi[form][entity_id]' => $node->getTitle() . ' (' . $node->id() . ')', + 'title[0][value]' => $parent_title, + ]; + + // Create ief_test_complex node. + $this->assertFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field found.'); + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertResponse(200, 'Submission of parent entity was successful.'); + $this->assertNoText(t("This value should not be null."), "The error message 'This value cannot be null' was not found in the page."); + $node = $this->drupalGetNodeByTitle($parent_title); + $this->assertTrue($node, 'Created ief_reference_type node.'); + } + + /** * Test if invalid values get correct validation messages in reference existing entity form. * * Also checks if existing entity reference form can be canceled.