diff --git a/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php b/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php index 566bfe3..487cf5b 100644 --- a/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php +++ b/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php @@ -1,4 +1,5 @@ TRUE, 'entity_type_ids' => array(), - ) + parent::defaultStorageSettings(); + ); } /** * {@inheritdoc} */ public static function defaultFieldSettings() { - return array( - 'node' => array( - 'handler' => 'default', - 'handler_settings' => array(), - ) - ); + return array(); } /** @@ -129,7 +125,6 @@ class DynamicEntityReferenceItem extends ConfigurableEntityReferenceItem { /** * {@inheritdoc} - * @todo update */ public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) { // @todo inject this. @@ -161,17 +156,15 @@ class DynamicEntityReferenceItem extends ConfigurableEntityReferenceItem { $settings_form = array(); $field = $form_state->get('field'); - $labels = \Drupal::entityManager()->getEntityTypeLabels(TRUE); - $options = $labels['Content']; $settings = $this->getSettings(); - - if ($settings['exclude_entity_types']) { - $entity_type_ids = array_diff_key($options, $settings['entity_type_ids'] ?: array()); - } - else { - $entity_type_ids = array_intersect_key($options, $settings['entity_type_ids'] ?: array()); - } + $entity_type_ids = $this->getAllEntityTypeIds($settings); foreach (array_keys($entity_type_ids) as $entity_type_id) { + $settings += array( + $entity_type_id => array( + 'handler' => 'default', + 'handler_settings' => array(), + ), + ); // We put the dummy value here so selection plugins can work. $field->settings['target_type'] = $entity_type_id; $field->settings['handler'] = $settings[$entity_type_id]['handler']; @@ -189,13 +182,17 @@ class DynamicEntityReferenceItem extends ConfigurableEntityReferenceItem { * The form where the settings form is being included in. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state of the (entire) configuration form. - * @todo update */ public static function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) { if ($form_state->hasValue('field')) { $settings = $form_state->getValue(array('field', 'settings')); foreach (array_keys($settings) as $entity_typ_id) { - $form_state->unsetValue(array('field', 'settings', $entity_typ_id, 'handler_submit')); + $form_state->unsetValue(array( + 'field', + 'settings', + $entity_typ_id, + 'handler_submit', + )); } $form_state->get('field')->settings = $form_state->getValue(array('field', 'settings')); } @@ -227,4 +224,40 @@ class DynamicEntityReferenceItem extends ConfigurableEntityReferenceItem { return $this->values; } + /** + * {@inheritdoc} + */ + public function preSave() { + if ($this->hasUnsavedEntity()) { + $this->entity->save(); + } + // Handle the case where an unsaved entity was directly set using the public + // 'entity' property and then saved before this entity. In this case + // ::hasUnsavedEntity() will return FALSE but $this->target_id and + // $this->target_type will still be empty. + if (empty($this->target_id) && $this->entity) { + $this->target_type = $this->entity->getEntityTypeId(); + $this->target_id = $this->entity->id(); + } + } + + /** + * Helper function to get all the entity type ids that can be referenced. + * + * @return string[] + * All the entity type ids that can be referenced. + */ + protected static function getAllEntityTypeIds($settings) { + $labels = \Drupal::entityManager()->getEntityTypeLabels(TRUE); + $options = $labels['Content']; + + if ($settings['exclude_entity_types']) { + $entity_type_ids = array_diff_key($options, $settings['entity_type_ids'] ?: array()); + } + else { + $entity_type_ids = array_intersect_key($options, $settings['entity_type_ids'] ?: array()); + } + return $entity_type_ids; + } + } diff --git a/src/Plugin/Field/FieldWidget/DynamicEntityReferenceWidget.php b/src/Plugin/Field/FieldWidget/DynamicEntityReferenceWidget.php index 13d5f04..6a3567e 100644 --- a/src/Plugin/Field/FieldWidget/DynamicEntityReferenceWidget.php +++ b/src/Plugin/Field/FieldWidget/DynamicEntityReferenceWidget.php @@ -98,8 +98,10 @@ class DynamicEntityReferenceWidget extends AutocompleteWidget { * Checks whether a content entity is referenced. * * @param string $target_type - * The value target entity type + * The value target entity type. + * * @return bool + * TRUE if a content entity is referenced. */ protected function isContentReferenced($target_type = NULL) { $target_type_info = \Drupal::entityManager()->getDefinition($target_type); @@ -115,10 +117,17 @@ class DynamicEntityReferenceWidget extends AutocompleteWidget { if (!empty($element['#value'])) { // If this is the default value of the field. if ($form_state->hasValue('default_value_input')) { - $values = $form_state->getValue(array('default_value_input', $element['#field_name'], $element['#delta'])); + $values = $form_state->getValue(array( + 'default_value_input', + $element['#field_name'], + $element['#delta'], + )); } else { - $values = $form_state->getValue(array($element['#field_name'], $element['#delta'])); + $values = $form_state->getValue(array( + $element['#field_name'], + $element['#delta'], + )); } // Take "label (entity id)', match the id from parenthesis. if ($this->isContentReferenced($values['target_type']) && preg_match("/.+\((\d+)\)/", $element['#value'], $matches)) { @@ -127,10 +136,27 @@ class DynamicEntityReferenceWidget extends AutocompleteWidget { elseif (preg_match("/.+\(([\w.]+)\)/", $element['#value'], $matches)) { $value = $matches[1]; } - if (!$value) { + $auto_create = $this->getHandlerSetting('auto_create', $values['target_type']); + if ($value === NULL) { // Try to get a match from the input string when the user didn't use the // autocomplete but filled in a value manually. - $value = $this->validateAutocompleteInput($values['target_type'], $element['#value'], $element, $form_state, $form); + $this->fakeFieldSettings($values['target_type']); + /** @var \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface $handler */ + $handler = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($this->fieldDefinition); + $value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form, !$auto_create); + } + if (!$value && $auto_create && (count($this->getHandlerSetting('target_bundles', $values['target_type'])) == 1)) { + // Auto-create item. See + // \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave(). + $value = array( + 'target_id' => NULL, + 'entity' => $this->createNewEntity($element['#value'], $element['#autocreate_uid']), + // Keep the weight property. + '_weight' => $element['#weight'], + ); + // Change the element['#parents'], so in form_set_value() we + // populate the correct key. + array_pop($element['#parents']); } } @@ -138,55 +164,32 @@ class DynamicEntityReferenceWidget extends AutocompleteWidget { } /** - * {@inheritdoc} + * Sets the fake field settings values. + * + * @param string $entity_type_id + * The id of the entity type. */ - public function validateAutocompleteInput($target_type, $input, &$element, FormStateInterface $form_state, $form) { - // @todo Make this a service. - $controller = new DynamicEntityReferenceController(\Drupal::service('entity.query')); - $bundled_entities = $controller->getReferenceableEntities($target_type, $input, '=', 6); - $params = array( - '%value' => $input, - '@value' => $input, - ); - $entities = array(); - foreach ($bundled_entities as $entities_list) { - $entities += $entities_list; - } - if (empty($entities)) { - // Error if there are no entities available for a required field. - $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; - $params['%multiple'] = implode('", "', $multiples); - $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)".', $params)); - } - else { - // Take the one and only matching entity. - return key($entities); - } + protected function fakeFieldSettings($entity_type_id) { + $settings = $this->getFieldSettings(); + $this->fieldDefinition->settings['target_type'] = $entity_type_id; + $this->fieldDefinition->settings['handler'] = $settings[$entity_type_id]['handler']; + $this->fieldDefinition->settings['handler_settings'] = $settings[$entity_type_id]['handler_settings']; } /** - * {@inheritdoc} + * Returns the value of a setting for the entity reference selection handler. + * + * @param string $setting_name + * The setting name. + * @param string $entity_type_id + * The id of the entity type. + * + * @return mixed + * The setting value. */ - protected function getEntityType(FieldItemListInterface $items, $delta) { - // The autocomplete widget outputs one entity label per form element. - if (isset($items[$delta])) { - return $items[$delta]->target_type; - } - - return FALSE; + protected function getHandlerSetting($setting_name, $entity_type_id) { + $settings = $this->getFieldSettings(); + return isset($settings[$entity_type_id]['handler_settings'][$setting_name]) ? $settings[$entity_type_id]['handler_settings'][$setting_name] : NULL; } }