diff --git a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php index 2a52b79..4d1a257 100644 --- a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php +++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Tags; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\Textfield; use Drupal\Core\Site\Settings; @@ -147,7 +148,7 @@ public static function validateEntityAutocomplete(array &$element, FormStateInte 'handler_settings' => $element['#selection_settings'], ); $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options); - $autocreate = (bool) $element['#autocreate']; + $autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface; $input_values = $element['#tags'] ? Tags::explode($element['#value']) : array($element['#value']); foreach ($input_values as $input) { @@ -167,13 +168,14 @@ public static function validateEntityAutocomplete(array &$element, FormStateInte // Auto-create item. See an example of how this is handled in // \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave(). $value[] = array( - 'entity' => static::createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']) + 'entity' => $handler->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']), ); } } // Check that the referenced entities are valid, if needed. - if ($element['#validate_reference'] && !$autocreate && !empty($value)) { + if ($element['#validate_reference'] && !empty($value)) { + // Validate existing entities. $ids = array_reduce($value, function ($return, $item) { if (isset($item['target_id'])) { $return[] = $item['target_id']; @@ -189,6 +191,30 @@ public static function validateEntityAutocomplete(array &$element, FormStateInte } } } + + // Validate newly created entities. + $new_entities = array_reduce($value, function ($return, $item) { + if (isset($item['entity'])) { + $return[] = $item['entity']; + } + return $return; + }); + + if ($new_entities) { + if ($autocreate) { + $valid_new_entities = $handler->validateReferenceableNewEntities($new_entities); + $invalid_new_entities = array_diff_key($new_entities, $valid_new_entities); + } + else { + // If the selection handler does not support referencing newly + // created entities, all of them should be invalidated. + $invalid_new_entities = $new_entities; + } + + foreach ($invalid_new_entities as $entity) { + $form_state->setError($element, t('This entity (%type: %label) cannot be referenced.', array('%type' => $element['#target_type'], '%label' => $entity->label()))); + } + } } // Use only the last value if the form element does not support multiple @@ -310,37 +336,4 @@ public static function extractEntityIdFromAutocompleteInput($input) { return $match; } - /** - * Creates a new entity from a label entered in the autocomplete input. - * - * @param string $entity_type_id - * The entity type ID. - * @param string $bundle - * The bundle name. - * @param string $label - * The entity label. - * @param int $uid - * The entity owner ID. - * - * @return \Drupal\Core\Entity\EntityInterface - */ - protected static function createNewEntity($entity_type_id, $bundle, $label, $uid) { - $entity_manager = \Drupal::entityManager(); - - $entity_type = $entity_manager->getDefinition($entity_type_id); - $bundle_key = $entity_type->getKey('bundle'); - $label_key = $entity_type->getKey('label'); - - $entity = $entity_manager->getStorage($entity_type_id)->create(array( - $bundle_key => $bundle, - $label_key => $label, - )); - - if ($entity instanceof EntityOwnerInterface) { - $entity->setOwnerId($uid); - } - - return $entity; - } - } diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php index 564b673..132b9a7 100644 --- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php @@ -46,22 +46,6 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON public function validateReferenceableEntities(array $ids); /** - * Validates which newly created entities can be referenced. - * - * This method should replicate the logic implemented by - * validateReferenceableEntities(), but applied to newly created entities that - * have not been saved yet. - * - * @param \Drupal\Core\Entity\EntityInterface[] $entities - * An array of entities to check. - * - * @return \Drupal\Core\Entity\EntityInterface[] - * The incoming $entities parameter, filtered for valid entities. Array keys - * are preserved. - */ - public function validateReferenceableNewEntities(array $entities); - - /** * Allows the selection to alter the SelectQuery generated by EntityFieldQuery. * * @param \Drupal\Core\Database\Query\SelectInterface $query diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionWithAutocreateInterface.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionWithAutocreateInterface.php new file mode 100644 index 0000000..f81d5fa --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionWithAutocreateInterface.php @@ -0,0 +1,52 @@ +entityManager->getDefinition($entity_type_id); + $bundle_key = $entity_type->getKey('bundle'); + $label_key = $entity_type->getKey('label'); + + $entity = $this->entityManager->getStorage($entity_type_id)->create(array( + $bundle_key => $bundle, + $label_key => $label, + )); + + if ($entity instanceof EntityOwnerInterface) { + $entity->setOwnerId($uid); + } + + return $entity; + } + + /** + * {@inheritdoc} + */ public function validateReferenceableNewEntities(array $entities) { return array_filter($entities, function ($entity) { if (isset($this->configuration['handler_settings']['target_bundles'])) { diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php index b43c28c..f5696cd 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php @@ -10,6 +10,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface; +use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -101,8 +102,16 @@ public function validate($value, Constraint $constraint) { // Add violations on deltas with a new entity that is not valid. if ($new_entities) { - $valid_new_entities = $handler->validateReferenceableNewEntities($new_entities); - $invalid_new_entities = array_diff_key($new_entities, $valid_new_entities); + if ($handler instanceof SelectionWithAutocreateInterface) { + $valid_new_entities = $handler->validateReferenceableNewEntities($new_entities); + $invalid_new_entities = array_diff_key($new_entities, $valid_new_entities); + } + else { + // If the selection handler does not support referencing newly created + // entities, all of them should be invalidated. + $invalid_new_entities = $new_entities; + } + foreach ($invalid_new_entities as $delta => $entity) { $this->context->buildViolation($constraint->invalidAutocreateMessage) ->setParameter('%type', $target_type_id) diff --git a/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php b/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php index 63587bb..e558e58 100644 --- a/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php +++ b/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php @@ -15,6 +15,9 @@ */ class EntityReferenceFieldItemList extends FieldItemList implements EntityReferenceFieldItemListInterface { + /** + * {@inheritdoc} + */ public function getConstraints() { $constraints = parent::getConstraints(); $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager(); diff --git a/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php index 2761092..ea89c9e 100644 --- a/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php +++ b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php @@ -42,6 +42,18 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') /** * {@inheritdoc} */ + public function createNewEntity($entity_type_id, $bundle, $label, $uid) { + $entity = parent::createNewEntity($entity_type_id, $bundle, $label, $uid); + + // In order to create a referenceable comment, it needs to published. + $entity->status->value = CommentInterface::PUBLISHED; + + return $entity; + } + + /** + * {@inheritdoc} + */ public function validateReferenceableNewEntities(array $entities) { $entities = parent::validateReferenceableNewEntities($entities); // Mirror the conditions checked in buildEntityQuery(). diff --git a/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php b/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php index 232c741..abd06da 100644 --- a/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php +++ b/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php @@ -41,6 +41,19 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') /** * {@inheritdoc} */ + public function createNewEntity($entity_type_id, $bundle, $label, $uid) { + $entity = parent::createNewEntity($entity_type_id, $bundle, $label, $uid); + + // In order to create a referenceable file, it needs to have a "permanent" + // status. + $entity->status->value = FILE_STATUS_PERMANENT; + + return $entity; + } + + /** + * {@inheritdoc} + */ public function validateReferenceableNewEntities(array $entities) { $entities = parent::validateReferenceableNewEntities($entities); $entities = array_filter($entities, function ($entity) { diff --git a/core/modules/node/src/Plugin/EntityReferenceSelection/NodeSelection.php b/core/modules/node/src/Plugin/EntityReferenceSelection/NodeSelection.php index e707e79..5d7954a 100644 --- a/core/modules/node/src/Plugin/EntityReferenceSelection/NodeSelection.php +++ b/core/modules/node/src/Plugin/EntityReferenceSelection/NodeSelection.php @@ -51,6 +51,18 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') /** * {@inheritdoc} */ + public function createNewEntity($entity_type_id, $bundle, $label, $uid) { + $entity = parent::createNewEntity($entity_type_id, $bundle, $label, $uid); + + // In order to create a referenceable node, it needs to published. + $entity->status->value = NODE_PUBLISHED; + + return $entity; + } + + /** + * {@inheritdoc} + */ public function validateReferenceableNewEntities(array $entities) { $entities = parent::validateReferenceableNewEntities($entities); // Mirror the conditions checked in buildEntityQuery(). diff --git a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php index af44db1..1fefc00 100644 --- a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php +++ b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php @@ -260,15 +260,6 @@ public function validateReferenceableEntities(array $ids) { } /** - * {@inheritdoc} - */ - public function validateReferenceableNewEntities(array $entities) { - // ViewsSelection does not supprt autocreate, there is no way to check that - // an unsaved entity will match a View once it gets saved. - return []; - } - - /** * Element validate; Check View is valid. */ public static function settingsFormValidate($element, FormStateInterface $form_state, $form) {