Issue #2718697 by damiankloip, dawehner: EntityAutocomplete element cannot handle GET input values

8.2.x
Alex Pott 2016-05-08 12:55:05 -05:00
parent 853202a000
commit 677116e149
4 changed files with 99 additions and 28 deletions

View File

@ -57,19 +57,33 @@ class EntityAutocomplete extends Textfield {
if (is_array($element['#default_value']) && $element['#tags'] !== TRUE) { if (is_array($element['#default_value']) && $element['#tags'] !== TRUE) {
throw new \InvalidArgumentException('The #default_value property is an array but the form element does not allow multiple values.'); throw new \InvalidArgumentException('The #default_value property is an array but the form element does not allow multiple values.');
} }
elseif (!is_array($element['#default_value'])) { elseif (!empty($element['#default_value']) && !is_array($element['#default_value'])) {
// Convert the default value into an array for easier processing in // Convert the default value into an array for easier processing in
// static::getEntityLabels(). // static::getEntityLabels().
$element['#default_value'] = array($element['#default_value']); $element['#default_value'] = array($element['#default_value']);
} }
if ($element['#default_value'] && !(reset($element['#default_value']) instanceof EntityInterface)) { if ($element['#default_value']) {
throw new \InvalidArgumentException('The #default_value property has to be an entity object or an array of entity objects.'); if (!(reset($element['#default_value']) instanceof EntityInterface)) {
} throw new \InvalidArgumentException('The #default_value property has to be an entity object or an array of entity objects.');
}
// Extract the labels from the passed-in entity objects, taking access // Extract the labels from the passed-in entity objects, taking access
// checks into account. // checks into account.
return static::getEntityLabels($element['#default_value']); return static::getEntityLabels($element['#default_value']);
}
}
// Potentially the #value is set directly, so it contains the 'target_id'
// array structure instead of a string.
if ($input !== FALSE && is_array($input)) {
$entity_ids = array_map(function(array $item) {
return $item['target_id'];
}, $input);
$entities = \Drupal::entityTypeManager()->getStorage($element['#target_type'])->loadMultiple($entity_ids);
return static::getEntityLabels($entities);
} }
} }
@ -136,6 +150,7 @@ class EntityAutocomplete extends Textfield {
*/ */
public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) { public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
$value = NULL; $value = NULL;
if (!empty($element['#value'])) { if (!empty($element['#value'])) {
$options = array( $options = array(
'target_type' => $element['#target_type'], 'target_type' => $element['#target_type'],
@ -146,27 +161,35 @@ class EntityAutocomplete extends Textfield {
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options); $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
$autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface; $autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface;
$input_values = $element['#tags'] ? Tags::explode($element['#value']) : array($element['#value']); // GET forms might pass the validated data around on the next request, in
foreach ($input_values as $input) { // which case it will already be in the expected format.
$match = static::extractEntityIdFromAutocompleteInput($input); if (is_array($element['#value'])) {
if ($match === NULL) { $value = $element['#value'];
// Try to get a match from the input string when the user didn't use }
// the autocomplete but filled in a value manually. else {
$match = static::matchEntityByTitle($handler, $input, $element, $form_state, !$autocreate); $input_values = $element['#tags'] ? Tags::explode($element['#value']) : array($element['#value']);
}
if ($match !== NULL) { foreach ($input_values as $input) {
$value[] = array( $match = static::extractEntityIdFromAutocompleteInput($input);
'target_id' => $match, 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.
elseif ($autocreate) { $match = static::matchEntityByTitle($handler, $input, $element, $form_state, !$autocreate);
/** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface $handler */ }
// Auto-create item. See an example of how this is handled in
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave(). if ($match !== NULL) {
$value[] = array( $value[] = array(
'entity' => $handler->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']), 'target_id' => $match,
); );
}
elseif ($autocreate) {
/** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface $handler */
// Auto-create item. See an example of how this is handled in
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
$value[] = array(
'entity' => $handler->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']),
);
}
} }
} }

View File

@ -28,7 +28,7 @@ class Name extends InOperator {
'#target_type' => 'user', '#target_type' => 'user',
'#tags' => TRUE, '#tags' => TRUE,
'#default_value' => $default_value, '#default_value' => $default_value,
'#process_default_value' => FALSE, '#process_default_value' => $this->isExposed(),
); );
$user_input = $form_state->getUserInput(); $user_input = $form_state->getUserInput();

View File

@ -137,6 +137,17 @@ class HandlerFilterUserNameTest extends ViewTestBase {
$this->drupalGet($path, $options); $this->drupalGet($path, $options);
$this->assertRaw(t('There are no entities matching "%value".', array('%value' => implode(', ', $users)))); $this->assertRaw(t('There are no entities matching "%value".', array('%value' => implode(', ', $users))));
// Pass in an invalid target_id in for the entity_autocomplete value format.
// There should be no errors, but all results should be returned as the
// default value for the autocomplete will not match any users so should
// be empty.
$options['query']['uid'] = [['target_id' => 9999]];
$this->drupalGet($path, $options);
// The actual result should contain all of the user ids.
foreach ($this->accounts as $account) {
$this->assertRaw($account->id());
}
// Pass in an invalid username and a valid username. // Pass in an invalid username and a valid username.
$users = array($this->randomMachineName(), $this->names[0]); $users = array($this->randomMachineName(), $this->names[0]);
$users = array_map('strtolower', $users); $users = array_map('strtolower', $users);
@ -156,6 +167,18 @@ class HandlerFilterUserNameTest extends ViewTestBase {
foreach ($this->accounts as $account) { foreach ($this->accounts as $account) {
$this->assertRaw($account->id()); $this->assertRaw($account->id());
} }
// Pass in just valid user IDs in the entity_autocomplete target_id format.
$options['query']['uid'] = array_map(function($account) {
return ['target_id' => $account->id()];
}, $this->accounts);
$this->drupalGet($path, $options);
$this->assertNoRaw('Unable to find user');
// The actual result should contain all of the user ids.
foreach ($this->accounts as $account) {
$this->assertRaw($account->id());
}
} }
} }

View File

@ -310,6 +310,31 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
$this->assertEqual($form['tags_access']['#value'], $expected); $this->assertEqual($form['tags_access']['#value'], $expected);
} }
/**
* Tests ID input is handled correctly.
*
* E.g. This can happen with GET form parameters.
*/
public function testEntityAutocompleteIdInput() {
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
//$form = $form_builder->getForm($this);
$form_state = (new FormState())
->setMethod('GET')
->setValues([
'single' => [['target_id' => $this->referencedEntities[0]->id()]],
'single_no_validate' => [['target_id' => $this->referencedEntities[0]->id()]],
]);
$form_builder->submitForm($this, $form_state);
$form = $form_state->getCompleteForm();
$expected_label = $this->getAutocompleteInput($this->referencedEntities[0]);
$this->assertSame($expected_label, $form['single']['#value']);
$this->assertSame($expected_label, $form['single_no_validate']['#value']);
}
/** /**
* Returns an entity label in the format needed by the EntityAutocomplete * Returns an entity label in the format needed by the EntityAutocomplete
* element. * element.