diff --git a/core/core.services.yml b/core/core.services.yml index e4343f1..b993e6c 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -350,7 +350,7 @@ services: tags: - { name: event_subscriber } entity.autocomplete_matcher: - class: Drupal\system\EntityAutocompleteMatcher + class: Drupal\Core\Entity\EntityAutocompleteMatcher arguments: ['@plugin.manager.entity_reference_selection'] plugin.manager.entity_reference_selection: class: Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager diff --git a/core/lib/Drupal/Core/Render/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php similarity index 84% rename from core/lib/Drupal/Core/Render/Element/EntityAutocomplete.php rename to core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php index 88e05d9..8a95237 100644 --- a/core/lib/Drupal/Core/Render/Element/EntityAutocomplete.php +++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php @@ -2,13 +2,14 @@ /** * @file - * Contains \Drupal\Core\Render\Element\EntityAutocomplete. + * Contains \Drupal\Core\Entity\Element\EntityAutocomplete. */ -namespace Drupal\Core\Render\Element; +namespace Drupal\Core\Entity\Element; use Drupal\Component\Utility\Tags; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\Element\Textfield; use Drupal\user\EntityOwnerInterface; /** @@ -30,10 +31,7 @@ public function getInfo() { $info['#selection_handler'] = 'default'; $info['#selection_settings'] = array(); $info['#tags'] = FALSE; - // @todo Use an array for the 'autocreate API' below? - $info['#autocreate'] = FALSE; - $info['#autocreate_bundle'] = NULL; - $info['#autocreate_uid'] = \Drupal::currentUser()->id(); + $info['#autocreate'] = NULL; $info['#element_validate'] = array(array($class, 'validateEntityAutocomplete')); array_unshift($info['#process'], array($class, 'processEntityAutocomplete')); @@ -62,11 +60,24 @@ public function getInfo() { * * @return array * The form element. + * + * @throws \InvalidArgumentException + * Exception thrown when the #target_type or #autocreate['bundle'] are + * missing. */ public static function processEntityAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) { // Nothing to do if there is no target entity type. if (empty($element['#target_type'])) { - return $element; + throw new \InvalidArgumentException('Missing required #target_type parameter.'); + } + + // Provide default values and sanity checks for the #autocreate parameter. + if ($element['#autocreate']) { + if (!isset($element['#autocreate']['bundle'])) { + throw new \InvalidArgumentException("Missing required #autocreate['bundle'] parameter."); + } + // Default the autocreate user ID to the current user. + $element['#autocreate']['uid'] = $element['#autocreate']['uid'] ?: \Drupal::currentUser()->id(); } $element['#autocomplete_route_name'] = 'system.entity_autocomplete'; @@ -114,11 +125,11 @@ public static function validateEntityAutocomplete(&$element, FormStateInterface 'target_id' => $match, ); } - elseif ($autocreate && $element['#autocreate_bundle']) { + elseif ($autocreate) { // Auto-create item. See // \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave(). $value[] = array( - 'entity' => static::createNewEntity($element['#target_type'], $element['#autocreate_bundle'], $input, $element['#autocreate_uid']) + 'entity' => static::createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']) ); } } @@ -148,7 +159,7 @@ public static function validateEntityAutocomplete(&$element, FormStateInterface * * @return \Drupal\Core\Entity\EntityInterface */ - protected static function createNewEntity($entity_type_id, $bundle ,$label, $uid) { + protected static function createNewEntity($entity_type_id, $bundle, $label, $uid) { $entity_manager = \Drupal::entityManager(); $entity_type = $entity_manager->getDefinition($entity_type_id); diff --git a/core/modules/system/src/EntityAutocompleteMatcher.php b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php similarity index 86% rename from core/modules/system/src/EntityAutocompleteMatcher.php rename to core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php index a052209..859f613 100644 --- a/core/modules/system/src/EntityAutocompleteMatcher.php +++ b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php @@ -2,22 +2,22 @@ /** * @file - * Contains \Drupal\system\EntityAutocompleteMatcher. + * Contains \Drupal\Core\Entity\EntityAutocompleteMatcher. */ -namespace Drupal\system; +namespace Drupal\Core\Entity; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Tags; use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface; /** - * Helper class to get autocompletion results for entity reference. + * Matcher class to get autocompletion results for entity reference. */ class EntityAutocompleteMatcher { /** - * The Entity reference selection handler plugin manager. + * The entity reference selection handler plugin manager. * * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface */ @@ -27,7 +27,7 @@ class EntityAutocompleteMatcher { * Constructs a EntityAutocompleteMatcher object. * * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager - * The Entity reference selection handler plugin manager. + * The entity reference selection handler plugin manager. */ public function __construct(SelectionPluginManagerInterface $selection_manager) { $this->selectionHandlerManager = $selection_manager; @@ -36,9 +36,6 @@ public function __construct(SelectionPluginManagerInterface $selection_manager) /** * Returns matched labels based on a given search string. * - * This function can be used by other modules that wish to pass a mocked - * definition of the field on instance. - * * @param string $target_type * The ID of the target entity type. * @param string $selection_handler @@ -52,7 +49,8 @@ public function __construct(SelectionPluginManagerInterface $selection_manager) * Thrown when the current user doesn't have access to the specifies entity. * * @return array - * A list of matched entity labels. + * An array of matched entity labels, in the format required by the AJAX + * autocomplete API (e.g. array('value' => $value, 'label' => $label)). * * @see \Drupal\system\Controller\EntityAutocompleteController */ diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php index 90b81d5..bc646ec 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php @@ -100,15 +100,19 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#target_type' => $this->getFieldSetting('target_type'), '#selection_handler' => $this->getFieldSetting('handler'), '#selection_settings' => $this->getFieldSetting('handler_settings'), - '#autocreate' => $this->getSelectionHandlerSetting('auto_create'), - '#autocreate_bundle' => $this->getAutocreateBundle(), - '#autocreate_uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id(), '#maxlength' => 1024, '#default_value' => implode(', ', $this->getLabels($items, $delta)), '#size' => $this->getSetting('size'), '#placeholder' => $this->getSetting('placeholder'), ); + if ($this->getSelectionHandlerSetting('auto_create')) { + $element['#autocreate'] = array( + 'bundle' => $this->getAutocreateBundle(), + 'uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id() + ); + } + return array('target_id' => $element); } @@ -178,12 +182,13 @@ protected function getAutocreateBundle() { if (($target_bundles = $this->getSelectionHandlerSetting('target_bundles')) && count($target_bundles) == 1) { $bundle = reset($target_bundles); } - // Otherwise try to see if the entity type has a single bundle and use it. - elseif (($bundles = entity_get_bundles($this->getFieldSetting('target_type'))) && count($bundles) == 1) { + // Otherwise use the first bundle as a fallback. + else { + // @todo Expose a proper UI for choosing the bundle for autocreated + // entities in https://www.drupal.org/node/2412569. + $bundles = entity_get_bundles($this->getFieldSetting('target_type')); $bundle = key($bundles); } - // @todo Expose a proper UI for choosing the bundle for autocreated - // entities in https://www.drupal.org/node/2412569. } return $bundle; diff --git a/core/modules/system/src/Controller/EntityAutocompleteController.php b/core/modules/system/src/Controller/EntityAutocompleteController.php index 071941e..bd47031 100644 --- a/core/modules/system/src/Controller/EntityAutocompleteController.php +++ b/core/modules/system/src/Controller/EntityAutocompleteController.php @@ -10,7 +10,7 @@ use Drupal\Component\Utility\Tags; use Drupal\Component\Utility\Unicode; use Drupal\Core\Controller\ControllerBase; -use Drupal\system\EntityAutocompleteMatcher; +use Drupal\Core\Entity\EntityAutocompleteMatcher; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -21,17 +21,17 @@ class EntityAutocompleteController extends ControllerBase { /** - * The autocomplete helper for entity references. + * The autocomplete matcher for entity references. * - * @var \Drupal\system\EntityAutocompleteMatcher + * @var \Drupal\Core\Entity\EntityAutocompleteMatcher */ protected $entityAutocompleteMatcher; /** * Constructs a EntityAutocompleteController object. * - * @param \Drupal\system\EntityAutocompleteMatcher $entity_autocomplete_matcher - * The autocompletion helper for entity references. + * @param \Drupal\Core\Entity\EntityAutocompleteMatcher $entity_autocomplete_matcher + * The autocomplete matcher for entity references. */ public function __construct(EntityAutocompleteMatcher $entity_autocomplete_matcher) { $this->entityAutocompleteMatcher = $entity_autocomplete_matcher; @@ -59,7 +59,7 @@ public static function create(ContainerInterface $container) { * The settings that will be passed to the selection handler. * * @return \Symfony\Component\HttpFoundation\JsonResponse - * The matched entity labels as JSON. + * The matched entity labels as a JSON response. */ public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings = '') { $matches = array(); diff --git a/core/modules/user/src/Tests/UserEntityReferenceTest.php b/core/modules/user/src/Tests/UserEntityReferenceTest.php index 81645b2..215bf98 100644 --- a/core/modules/user/src/Tests/UserEntityReferenceTest.php +++ b/core/modules/user/src/Tests/UserEntityReferenceTest.php @@ -79,7 +79,7 @@ function testUserSelectionByRole() { $user3->save(); - /** @var \Drupal\system\EntityAutocompleteMatcher $autocomplete */ + /** @var \Drupal\Core\Entity\EntityAutocompleteMatcher $autocomplete */ $autocomplete = \Drupal::service('entity.autocomplete_matcher'); $matches = $autocomplete->getMatches('user', 'default', $field_definition->getSetting('handler_settings'), 'aabb');