diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php index 8635755..1f5a4c2 100644 --- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php @@ -3,7 +3,6 @@ namespace Drupal\Core\Entity\EntityReferenceSelection; use Drupal\Component\Plugin\ConfigurablePluginInterface; -use Drupal\Component\Utility\NestedArray; use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; @@ -55,17 +54,13 @@ public function getConfiguration() { * {@inheritdoc} */ public function setConfiguration(array $configuration) { - // Merge defaults. - $this->configuration = NestedArray::mergeDeep( - $this->defaultConfiguration(), - $configuration - ); // In order to keep backward compatibility, we copy all settings, except // 'target_type', 'handler' and 'entity' under 'handler_settings', following // the structure from the field config. If the plugin was instantiated using - // the 'handler_settings' level, then those values will take precedence. The - // backward compatibility aware configuration will have the next structure: + // the 'handler_settings' level, those values will be used. In case of + // conflict, the root level settings will take precedence. The backward + // compatibility aware configuration will have the next structure: // - target_type // - handler (will be removed in Drupal 9.0.x, it's the plugin id) // - entity @@ -79,13 +74,25 @@ public function setConfiguration(array $configuration) { // ... // - setting_N // @todo Remove 'handler' and 'handler_settings' in Drupal 9.0.x. - if (array_key_exists('handler_settings', $this->configuration) && is_array($this->configuration['handler_settings'])) { - $this->configuration = $this->configuration['handler_settings'] + $this->configuration; + + $default_config = $this->defaultConfiguration(); + if (isset($default_config['handler_settings'])) { + throw new \InvalidArgumentException("{$this->getPluginDefinition()['class']}::defaultConfiguration() should not contain a 'handler_settings' key. All settings should be placed in the root level."); } - // Sync back 'handler_settings'. + + // Extract the backward compatibility level from passed configuration, if any. + $handler_settings = !empty($configuration['handler_settings']) ? $configuration['handler_settings'] : []; + unset($configuration['handler_settings']); + + // Merge in BC layer and defaults. Settings passed in the root level take + // precedence over BC settings. + $this->configuration = $configuration + $handler_settings + $default_config; + + // Synchronize back 'handler_settings'. + // @todo Remove this sync in Drupal 9.0.x foreach ($this->configuration as $key => $value) { // Filter out keys that belong strictly to the root level. - if (!in_array($key, ['handler', 'target_type', 'entity'])) { + if (!in_array($key, ['handler', 'target_type', 'entity', 'handler_settings'])) { $this->configuration['handler_settings'][$key] = $value; } } diff --git a/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php b/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php index 9e14ff9..f4ec44e 100644 --- a/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php +++ b/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php @@ -15,6 +15,17 @@ class EntityReferenceSelectionUnitTest extends UnitTestCase { /** + * Tests invalid default configuration. + * + * @covers ::defaultConfiguration + * @expectedException \InvalidArgumentException + */ + public function testInvalidDefaultConfiguration() { + (new TestSelectionWithInvalidDefaultConfiguration([], 'test_selector', ['class' => 'TestSelectionWithInvalidDefaultConfiguration'])) + ->getConfiguration(); + } + + /** * Provides test data for ::testSetConfiguration() * * @return array @@ -48,10 +59,10 @@ public function providerTestSetConfiguration() { [ [ 'setting1' => 'foo', - 'setting2' => 'this will be overwritten', + 'setting2' => ['bar', 'baz'], 'handler_settings' => [ - // Settings under 'handler_settings' take precedence. - 'setting2' => ['bar', 'baz'], + // Same setting from root level takes precedence. + 'setting2' => 'this will be overwritten', ] ], ], @@ -76,22 +87,55 @@ public function testSetConfiguration($options) { 'handler' => 'test_selector', 'entity' => NULL, 'setting1' => 'foo', - 'setting2' => [ - 'bar', - 'baz', + 'setting2' => ['bar', 'baz'], + 'setting3' => 'foobar', + 'handler_settings' => [ + 'setting1' => 'foo', + 'setting2' => ['bar', 'baz'], + 'setting3' => 'foobar', ], + ]; + + $this->assertArrayEquals($expected, $selection->getConfiguration()); + } + + /** + * Tests the selection handler plugin BC structure. + * + * @covers ::setConfiguration + */ + public function testSetConfigurationBcLevel() { + $config = [ + 'target_type' => 'some_entity_type_id', + 'handler' => 'test_selector', + 'setting1' => 'foo', + ]; + $selection = new TestSelection($config, 'test_selector', []); + + $expected = [ + 'target_type' => 'some_entity_type_id', + 'handler' => 'test_selector', + 'entity' => NULL, + 'setting1' => 'foo', + 'setting2' => ['qux'], + 'setting3' => 'foobar', 'handler_settings' => [ 'setting1' => 'foo', - 'setting2' => [ - 'bar', - 'baz', - ], - ] + 'setting2' => ['qux'], + 'setting3' => 'foobar', + ], ]; + $this->assertArrayEquals($expected, $selection->getConfiguration()); + + // Read the stored values and override a setting. $config = $selection->getConfiguration(); - // Sort by keys to get make comparision possible. - $this->assertSame(ksort($expected), ksort($config)); + $config['setting1'] = 'bar'; + $selection->setConfiguration($config); + $expected['setting1'] = 'bar'; + $expected['handler_settings']['setting1'] = 'bar'; + + $this->assertArrayEquals($expected, $selection->getConfiguration()); } } @@ -101,6 +145,13 @@ public function testSetConfiguration($options) { */ class TestSelection extends SelectionPluginBase { + public function defaultConfiguration() { + return [ + 'setting2' => ['qux'], + 'setting3' => 'foobar', + ] + parent::defaultConfiguration(); + } + public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { } public function validateReferenceableEntities(array $ids) { } @@ -108,3 +159,16 @@ public function validateReferenceableEntities(array $ids) { } public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') { } } + +/** + * Provides a testing plugin with invalid default configuration. + */ +class TestSelectionWithInvalidDefaultConfiguration extends TestSelection { + + public function defaultConfiguration() { + return [ + 'handler_settings' => ['foo' => 'bar'], + ]; + } + +}