From ae4848f51d13b6494c82ea9f8193646bfb6bf355 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 17 Mar 2015 09:55:45 +0000
Subject: [PATCH] Issue #2436835 by amateescu, alexpott: Unable to create
 config schema for entity type specific entity reference selection plugin

---
 core/config/schema/core.data_types.schema.yml | 37 +++++++++++++++--
 .../SelectionPluginManager.php                | 30 ++++++++++----
 .../SelectionPluginManagerInterface.php       | 13 ++++++
 .../Field/FieldType/EntityReferenceItem.php   |  3 +-
 .../config/schema/entity_reference.schema.yml | 41 -------------------
 .../entity_reference/entity_reference.module  | 34 ++++++++++-----
 .../src/ConfigurableEntityReferenceItem.php   | 11 +----
 .../src/Tests/EntityReferenceAdminTest.php    |  2 +-
 .../EntityReferenceItemTest.php               | 36 +++++++++++++++-
 .../file/config/schema/file.schema.yml        |  3 ++
 .../src/Tests/d6/MigrateFieldInstanceTest.php | 11 +++--
 .../config/schema/taxonomy.schema.yml         |  3 ++
 .../FieldType/TaxonomyTermReferenceItem.php   |  9 ++++
 .../user/config/schema/user.schema.yml        | 20 +++++++++
 .../schema/views.entity_reference.schema.yml  |  2 +-
 15 files changed, 176 insertions(+), 79 deletions(-)
 delete mode 100644 core/modules/entity_reference/config/schema/entity_reference.schema.yml

diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 86014e4e9d74..edd97ac973d6 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -552,7 +552,7 @@ field.value.changed:
 
 field.storage_settings.entity_reference:
   type: mapping
-  label: 'Entity reference settings'
+  label: 'Entity reference field storage settings'
   mapping:
     target_type:
       type: string
@@ -560,14 +560,14 @@ field.storage_settings.entity_reference:
 
 field.field_settings.entity_reference:
   type: mapping
-  label: 'Entity reference settings'
+  label: 'Entity reference field settings'
   mapping:
     handler:
       type: string
       label: 'Reference method'
     handler_settings:
-      type: entity_reference.[%parent.handler].handler_settings
-      label: 'Reference method settings'
+      type: entity_reference_selection.[%parent.handler]
+      label: 'Entity reference selection plugin settings'
 
 field.value.entity_reference:
   type: mapping
@@ -750,3 +750,32 @@ text_format:
       label: 'Text format'
       # The text format should not be translated as part of the string
       # translation system, so this is not marked as translatable.
+
+# Schema for the configuration of the Entity reference selection plugins.
+
+entity_reference_selection:
+  type: mapping
+  label: 'Entity reference selection plugin configuration'
+  mapping:
+    target_bundles:
+      type: sequence
+      label: 'types'
+      sequence:
+        type: string
+        label: 'Type'
+    sort:
+      type: mapping
+      label: 'Sort settings'
+      mapping:
+        field:
+          type: string
+          label: 'Sort by'
+        direction:
+          type: string
+          label: 'Sort direction'
+    auto_create:
+      type: boolean
+      label: 'Create referenced entities if they don''t already exist'
+
+entity_reference_selection.*:
+  type: entity_reference_selection
diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php
index 089dee807a6d..199a8cfbb70e 100644
--- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php
@@ -45,19 +45,35 @@ public function getInstance(array $options) {
 
     // Initialize default options.
     $options += array(
-      'handler' => 'default',
+      'handler' => $this->getPluginId($options['target_type'], 'default'),
       'handler_settings' => array(),
     );
 
+    // A specific selection plugin ID was already specified.
+    if (strpos($options['handler'], ':') !== FALSE) {
+      $plugin_id = $options['handler'];
+    }
+    // Only a selection group name was specified.
+    else {
+      $plugin_id = $this->getPluginId($options['target_type'], $options['handler']);
+    }
+
+    return $this->createInstance($plugin_id, $options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginId($target_type, $base_plugin_id) {
     // Get all available selection plugins for this entity type.
-    $selection_handler_groups = $this->getSelectionGroups($options['target_type']);
+    $selection_handler_groups = $this->getSelectionGroups($target_type);
 
     // Sort the selection plugins by weight and select the best match.
-    uasort($selection_handler_groups[$options['handler']], array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
-    end($selection_handler_groups[$options['handler']]);
-    $plugin_id = key($selection_handler_groups[$options['handler']]);
+    uasort($selection_handler_groups[$base_plugin_id], array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
+    end($selection_handler_groups[$base_plugin_id]);
+    $plugin_id = key($selection_handler_groups[$base_plugin_id]);
 
-    return $this->createInstance($plugin_id, $options);
+    return $plugin_id;
   }
 
   /**
@@ -86,7 +102,7 @@ public function getSelectionHandler(FieldDefinitionInterface $field_definition,
     $options = array(
       'target_type' => $field_definition->getFieldStorageDefinition()->getSetting('target_type'),
       'handler' => $field_definition->getSetting('handler'),
-      'handler_settings' => $field_definition->getSetting('handler_settings'),
+      'handler_settings' => $field_definition->getSetting('handler_settings') ?: array(),
       'entity' => $entity,
     );
     return $this->getInstance($options);
diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManagerInterface.php
index 81d555d8d099..278e3c4cc584 100644
--- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManagerInterface.php
@@ -16,6 +16,19 @@
  */
 interface SelectionPluginManagerInterface extends PluginManagerInterface {
 
+  /**
+   * Gets the plugin ID for a given target entity type and base plugin ID.
+   *
+   * @param string $target_type
+   *   The target entity type.
+   * @param string $base_plugin_id
+   *   The base plugin ID (e.g. 'default' or 'views').
+   *
+   * @return string
+   *   The plugin ID.
+   */
+  public function getPluginId($target_type, $base_plugin_id);
+
   /**
    * Returns selection plugins that can reference a specific entity type.
    *
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index aeee37d0fd7a..81664ba53866 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -59,7 +59,8 @@ public static function defaultStorageSettings() {
    */
   public static function defaultFieldSettings() {
     return array(
-      'handler' => 'default',
+      'handler' => 'default:' . (\Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'user'),
+      'handler_settings' => array(),
     ) + parent::defaultFieldSettings();
   }
 
diff --git a/core/modules/entity_reference/config/schema/entity_reference.schema.yml b/core/modules/entity_reference/config/schema/entity_reference.schema.yml
deleted file mode 100644
index cfb29eace38b..000000000000
--- a/core/modules/entity_reference/config/schema/entity_reference.schema.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-# Schema for the configuration files of the Entity Reference module.
-
-entity_reference.default.handler_settings:
-  type: mapping
-  label: 'View handler settings'
-  mapping:
-    target_bundles:
-      type: sequence
-      label: 'types'
-      sequence:
-        type: string
-        label: 'Type'
-    sort:
-      type: mapping
-      label: 'Sort settings'
-      mapping:
-        field:
-          type: string
-          label: 'Sort by'
-        direction:
-          type: string
-          label: 'Sort direction'
-    filter:
-      type: mapping
-      label: 'Filter settings'
-      mapping:
-        type:
-          type: string
-          label: 'Filter by'
-        role:
-          type: sequence
-          label: 'Restrict to the selected roles'
-          sequence:
-            type: string
-            label: 'Role'
-    auto_create:
-      type: boolean
-      label: 'Create referenced entities if they don''t already exist'
-    include_anonymous:
-      type: boolean
-      label: 'Include the anonymous user in the matched entities.'
diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module
index 526c60cc6705..889eaf3a50e4 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -13,6 +13,7 @@
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\FieldStorageConfigInterface;
+use Drupal\field\FieldConfigInterface;
 
 /**
  * Implements hook_help().
@@ -84,20 +85,33 @@ function entity_reference_field_storage_config_update(FieldStorageConfigInterfac
     return;
   }
 
-  if (empty($field_storage->bundles)) {
-    // Field storage has no fields.
-    return;
+  foreach ($field_storage->getBundles() as $bundle) {
+    $field = FieldConfig::loadByName($field_storage->getTargetEntityTypeId(), $bundle, $field_storage->getName());
+    $field->settings['handler_settings'] = array();
+    $field->save();
   }
+}
 
-  $field_name = $field_storage->getName();
+/**
+ * Implements hook_ENTITY_TYPE_presave() for 'field_config'.
+ *
+ * Determine the selection handler plugin ID for an entity reference field.
+ */
+function entity_reference_field_config_presave(FieldConfigInterface $field) {
+  if ($field->getType() != 'entity_reference') {
+    // Only act on entity reference fields.
+    return;
+  }
 
-  foreach ($field_storage->bundles() as $entity_type => $bundles) {
-    foreach ($bundles as $bundle) {
-      $field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
-      $field->settings['handler_settings'] = array();
-      $field->save();
-    }
+  if ($field->isSyncing()) {
+    // Don't change anything during a configuration sync.
+    return;
   }
+
+  $target_type = $field->getFieldStorageDefinition()->getSetting('target_type');
+  $selection_manager = \Drupal::service('plugin.manager.entity_reference_selection');
+  list($current_handler) = explode(':', $field->getSetting('handler'), 2);
+  $field->settings['handler'] = $selection_manager->getPluginId($target_type, $current_handler);
 }
 
 /**
diff --git a/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php
index 5f0f75d73944..c464ebf604fa 100644
--- a/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php
@@ -42,15 +42,6 @@ public static function defaultStorageSettings() {
     return $settings;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function defaultFieldSettings() {
-    return array(
-      'handler_settings' => array(),
-    ) + parent::defaultFieldSettings();
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -149,7 +140,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
       }
       elseif (array_key_exists($selection_group_id . ':' . $this->getSetting('target_type'), $selection_plugins[$selection_group_id])) {
         $selection_group_plugin = $selection_group_id . ':' . $this->getSetting('target_type');
-        $handlers_options[$selection_group_id] = String::checkPlain($selection_plugins[$selection_group_id][$selection_group_plugin]['base_plugin_label']);
+        $handlers_options[$selection_group_plugin] = String::checkPlain($selection_plugins[$selection_group_id][$selection_group_plugin]['base_plugin_label']);
       }
     }
 
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
index 19ef644bbf9b..e3fcc1664ee4 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
@@ -78,7 +78,7 @@ public function testFieldAdminHandler() {
     $this->drupalPostForm(NULL, array(), t('Save field settings'));
 
     // The base handler should be selected by default.
-    $this->assertFieldByName('field[settings][handler]', 'default');
+    $this->assertFieldByName('field[settings][handler]', 'default:node');
 
     // The base handler settings should be displayed.
     $entity_type_id = 'node';
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
index c1179f072da5..6c0b4a8db39d 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
@@ -11,6 +11,8 @@
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\Tests\FieldUnitTestBase;
 use Drupal\taxonomy\Entity\Term;
 use Drupal\taxonomy\Entity\Vocabulary;
@@ -27,7 +29,7 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('entity_reference', 'taxonomy', 'text', 'filter');
+  public static $modules = array('entity_reference', 'taxonomy', 'text', 'filter', 'views');
 
   /**
    * The taxonomy vocabulary to test with.
@@ -214,4 +216,36 @@ public function testEntitySaveOrder() {
     $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
   }
 
+  /**
+   * Tests that the 'handler' field setting stores the proper plugin ID.
+   */
+  public function testSelectionHandlerSettings() {
+    $field_name = Unicode::strtolower($this->randomMachineName());
+    $field_storage = FieldStorageConfig::create(array(
+      'field_name' => $field_name,
+      'entity_type' => 'entity_test',
+      'type' => 'entity_reference',
+      'settings' => array(
+        'target_type' => 'entity_test'
+      ),
+    ));
+    $field_storage->save();
+
+    // Do not specify any value for the 'handler' setting in order to verify
+    // that the default value is properly used.
+    $field = FieldConfig::create(array(
+      'field_storage' => $field_storage,
+      'bundle' => 'entity_test',
+    ));
+    $field->save();
+
+    $field = FieldConfig::load($field->id());
+    $this->assertTrue($field->getSetting('handler') == 'default:entity_test');
+
+    $field->settings['handler'] = 'views';
+    $field->save();
+    $field = FieldConfig::load($field->id());
+    $this->assertTrue($field->getSetting('handler') == 'views');
+  }
+
 }
diff --git a/core/modules/file/config/schema/file.schema.yml b/core/modules/file/config/schema/file.schema.yml
index 70bf21f6f0df..bbf4048c1616 100644
--- a/core/modules/file/config/schema/file.schema.yml
+++ b/core/modules/file/config/schema/file.schema.yml
@@ -46,6 +46,9 @@ base_file_field_field_settings:
     handler:
       type: string
       label: 'Reference method'
+    handler_settings:
+      type: entity_reference_selection.[%parent.handler]
+      label: 'Entity reference selection settings'
     file_directory:
       type: string
       label: 'File directory'
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php
index 05cc4671ace1..2165619f7ec0 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php
@@ -134,12 +134,17 @@ public function testFieldInstanceSettings() {
       'display_field' => FALSE,
       'display_default' => FALSE,
       'uri_scheme' => 'public',
-      'handler' => 'default',
+      // This value should be 'default:file' but the test does not migrate field
+      // storages so we end up with the default value for this setting.
+      'handler' => 'default:node',
+      'handler_settings' => array(),
       'target_bundle' => NULL,
     );
+    $field_settings = $field->getSettings();
+    ksort($expected);
+    ksort($field_settings);
     // This is the only way to compare arrays.
-    $this->assertFalse(array_diff_assoc($field->getSettings(), $expected));
-    $this->assertFalse(array_diff_assoc($expected, $field->getSettings()));
+    $this->assertIdentical($expected, $field_settings);
 
     // Test a link field.
     $field = FieldConfig::load('node.story.field_test_link');
diff --git a/core/modules/taxonomy/config/schema/taxonomy.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.schema.yml
index 79703eba9a3d..25124f734177 100644
--- a/core/modules/taxonomy/config/schema/taxonomy.schema.yml
+++ b/core/modules/taxonomy/config/schema/taxonomy.schema.yml
@@ -62,6 +62,9 @@ field.field_settings.taxonomy_term_reference:
     handler:
       type: string
       label: 'Reference method'
+    handler_settings:
+      type: entity_reference_selection.[%parent.handler]
+      label: 'Entity reference selection settings'
 
 field.value.taxonomy_term_reference:
   type: mapping
diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
index 898c9de004e3..c2ed1f0af2b5 100644
--- a/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
+++ b/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
@@ -47,6 +47,15 @@ public static function defaultStorageSettings() {
     ) + parent::defaultStorageSettings();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultFieldSettings() {
+    return array(
+      'handler' => 'default:taxonomy_term',
+    ) + parent::defaultFieldSettings();
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 40bde952152a..f38fd4cfb13b 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -174,3 +174,23 @@ condition.plugin.user_role:
       type: sequence
       sequence:
         type: string
+
+entity_reference_selection.default:user:
+  type: entity_reference_selection
+  mapping:
+    filter:
+      type: mapping
+      label: 'Filter settings'
+      mapping:
+        type:
+          type: string
+          label: 'Filter by'
+        role:
+          type: sequence
+          label: 'Restrict to the selected roles'
+          sequence:
+            type: string
+            label: 'Role'
+    include_anonymous:
+      type: boolean
+      label: 'Include the anonymous user in the matched entities.'
diff --git a/core/modules/views/config/schema/views.entity_reference.schema.yml b/core/modules/views/config/schema/views.entity_reference.schema.yml
index 1451368ab70b..027c62fa46c1 100644
--- a/core/modules/views/config/schema/views.entity_reference.schema.yml
+++ b/core/modules/views/config/schema/views.entity_reference.schema.yml
@@ -1,6 +1,6 @@
 # Schema for the views entity reference selection plugins.
 
-entity_reference.views.handler_settings:
+entity_reference_selection.views:
   type: mapping
   label: 'View handler settings'
   mapping:
-- 
GitLab