diff --git a/PLUGIN_TYPES.md b/PLUGIN_TYPES.md index aca9831..991f5cc 100644 --- a/PLUGIN_TYPES.md +++ b/PLUGIN_TYPES.md @@ -19,6 +19,11 @@ on the class, but the default class takes the following: - operations_provider_class (optional): the fully qualified name of a class that implements `\Drupal\plugin\PluginType\PluginTypeOperationsProviderInterface`. Defaults to `\Drupal\plugin\PluginType\DefaultPluginTypeOperationsProvider`. +- plugin_configuration_schema_id (optional): the ID of the plugin's + configuration schema. Two following replacement tokens are supported: + - [plugin_type_id]: The ID of the plugin's type. + - [plugin_id]: the plugin's ID. + Defaults to "plugin.plugin_configuration.[plugin_type_id].[plugin_id]" A configuration schema MAY be provided for all configurable plugin types. If a schema is provided, its name MUST be like diff --git a/config/schema/plugin.schema.yml b/config/schema/plugin.schema.yml index 087f2c8..5af9b9a 100644 --- a/config/schema/plugin.schema.yml +++ b/config/schema/plugin.schema.yml @@ -28,13 +28,13 @@ plugin.plugin_configuration.*: "field.value.plugin:*": label: Plugin collection field value mapping: - plugin_type_id: - label: Plugin type ID - type: string plugin_id: label: Plugin ID type: string plugin_configuration: label: Plugin configuration - type: plugin.plugin_configuration.[%parent.plugin_type_id].[%parent.plugin_id]: + type: "[%parent.plugin_configuration_schema_id]" + plugin_configuration_schema_id: + label: Plugin configuration + type: string type: config_object diff --git a/plugin.plugin_type.yml b/plugin.plugin_type.yml index 18c1c02..999dec5 100644 --- a/plugin.plugin_type.yml +++ b/plugin.plugin_type.yml @@ -9,11 +9,13 @@ block: provider: block plugin_manager_service_id: plugin.manager.block plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\BlockPluginDefinitionDecorator + plugin_configuration_schema_id: block.settings.[plugin_id] entity_reference_selector: label: Entity reference selector provider: core plugin_manager_service_id: plugin.manager.entity_reference_selection plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: entity_reference_selection.[plugin_id] field_type: label: Field type/item provider: core @@ -151,6 +153,7 @@ migrate_source: provider: migrate plugin_manager_service_id: plugin.manager.migrate.source plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: migrate.source.[plugin_id] migrate_process: label: Migration process provider: migrate @@ -161,6 +164,7 @@ migrate_destination: provider: migrate plugin_manager_service_id: plugin.manager.migrate.destination plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: migrate.destination.[plugin_id] migrate_id_map: label: Migration ID map provider: migrate @@ -213,11 +217,13 @@ views_area: provider: views plugin_manager_service_id: plugin.manager.views.area plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: views.area.[plugin_id] views_argument: label: Views argument provider: views plugin_manager_service_id: plugin.manager.views.argument plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: views.argument.[plugin_id] views_argument_default: label: Views argument default provider: views @@ -253,11 +259,13 @@ views_field: provider: views plugin_manager_service_id: plugin.manager.views.field plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: views.field.[plugin_id] views_filter: label: Views filter provider: views plugin_manager_service_id: plugin.manager.views.filter plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: views.filter.[plugin_id] views_join: label: Views join provider: views @@ -278,6 +286,7 @@ views_relationship: provider: views plugin_manager_service_id: plugin.manager.views.relationship plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: views.relationship.[plugin_id] views_row: label: Views row provider: views @@ -288,6 +297,7 @@ views_sort: provider: views plugin_manager_service_id: plugin.manager.views.sort plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + plugin_configuration_schema_id: views.sort.[plugin_id] views_style: label: Views style provider: views diff --git a/src/Plugin/Field/FieldType/PluginCollectionItemBase.php b/src/Plugin/Field/FieldType/PluginCollectionItemBase.php index 779853a..8a3f00d 100644 --- a/src/Plugin/Field/FieldType/PluginCollectionItemBase.php +++ b/src/Plugin/Field/FieldType/PluginCollectionItemBase.php @@ -12,9 +12,7 @@ use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\TypedData\DataDefinition; -use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\TypedData\MapDataDefinition; -use Drupal\Core\TypedData\TypedDataInterface; /** * Provides a base for plugin collection field items. @@ -24,14 +22,6 @@ abstract class PluginCollectionItemBase extends FieldItemBase implements PluginC /** * {@inheritdoc} */ - public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) { - parent::__construct($definition, $name, $parent); - $this->get('plugin_type_id')->setValue($this->getPluginType()->getId()); - } - - /** - * {@inheritdoc} - */ public function __get($name) { // @todo Remove this override once https://www.drupal.org/node/2413471 has // been fixed. @@ -129,9 +119,6 @@ abstract class PluginCollectionItemBase extends FieldItemBase implements PluginC * {@inheritdoc} */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { - $properties['plugin_type_id'] = DataDefinition::create('string') - ->setLabel(t('Plugin type ID')) - ->setReadOnly(TRUE); $properties['plugin_id'] = DataDefinition::create('plugin_id') ->setLabel(t('Plugin ID')); $properties['plugin_configuration'] = MapDataDefinition::create('plugin_configuration') @@ -192,9 +179,9 @@ abstract class PluginCollectionItemBase extends FieldItemBase implements PluginC // ID and configuration. parent::getValue() skips computed properties, so we // must return them here. return [ - 'plugin_type_id' => $this->get('plugin_type_id')->getValue(), 'plugin_id' => $this->get('plugin_id')->getValue(), 'plugin_configuration' => $this->get('plugin_configuration')->getValue(), + 'plugin_configuration_schema_id' => $this->getPluginType()->getPluginConfigurationSchemaId($this->get('plugin_id')->getValue()), ]; } diff --git a/src/PluginType/ConfigurablePluginTypeInterface.php b/src/PluginType/ConfigurablePluginTypeInterface.php new file mode 100644 index 0000000..20718ed --- /dev/null +++ b/src/PluginType/ConfigurablePluginTypeInterface.php @@ -0,0 +1,25 @@ +pluginDefinitionDecoratorClass = $definition['plugin_definition_decorator_class']; } + if (isset($definition['plugin_configuration_schema_id'])) { + if (!is_string($definition['plugin_configuration_schema_id'])) { + throw new \InvalidArgumentException(sprintf('The plugin type definition "plugin_configuration_schema_id" item must be a string, but %s was given.', gettype($definition['field_type']))); + } + $this->configurationSchemaId = $definition['plugin_configuration_schema_id']; + } + $plugin_configuration_schema_id = $this->getPluginConfigurationSchemaId('*'); + if (!$typed_config_manager->hasConfigSchema($plugin_configuration_schema_id)) { + throw new \InvalidArgumentException(sprintf('The plugin type definition "plugin_configuration_schema_id" item references the configuration schema "%s" ("%s"), which does not exist.', $plugin_configuration_schema_id, $this->configurationSchemaId)); + } $operations_provider_class = array_key_exists('operations_provider_class', $definition) ? $definition['operations_provider_class'] : DefaultPluginTypeOperationsProvider::class; $this->operationsProvider = $class_resolver->getInstanceFromDefinition($operations_provider_class); $this->pluginManager = $plugin_manager; @@ -131,7 +156,7 @@ class PluginType implements PluginTypeInterface { * {@inheritdoc} */ public static function createFromDefinition(ContainerInterface $container, array $definition) { - return new static($definition, $container->get('string_translation'), $container->get('class_resolver'), $container->get($definition['plugin_manager_service_id'])); + return new static($definition, $container->get('string_translation'), $container->get('class_resolver'), $container->get($definition['plugin_manager_service_id']), $container->get('config.typed')); } /** @@ -199,4 +224,11 @@ class PluginType implements PluginTypeInterface { return $this->fieldType; } + /** + * {@inheritdoc} + */ + public function getPluginConfigurationSchemaId($plugin_id) { + return str_replace(['[plugin_type_id]', '[plugin_id]'], [$this->id, $plugin_id], $this->configurationSchemaId); + } + } diff --git a/tests/src/Unit/Controller/ListPluginTypesTest.php b/tests/src/Unit/Controller/ListPluginTypesTest.php index 0db8d7c..084cc91 100644 --- a/tests/src/Unit/Controller/ListPluginTypesTest.php +++ b/tests/src/Unit/Controller/ListPluginTypesTest.php @@ -8,6 +8,7 @@ namespace Drupal\Tests\plugin\Unit\Controller; use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\plugin\Controller\ListPluginTypes; @@ -99,6 +100,11 @@ class ListPluginTypesTest extends UnitTestCase { $plugin_manager = $this->getMock(PluginManagerInterface::class); + $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class); + $typed_config_manager->expects($this->atLeastOnce()) + ->method('hasConfigSchema') + ->willReturn(TRUE); + $plugin_type_id_a = $this->randomMachineName(); $plugin_type_label_a = $this->randomMachineName(); $plugin_type_description_a = $this->randomMachineName(); @@ -108,7 +114,7 @@ class ListPluginTypesTest extends UnitTestCase { 'description' => $plugin_type_description_a, 'provider' => $this->randomMachineName(), ]; - $plugin_type_a = new PluginType($plugin_type_definition_a, $this->stringTranslation, $class_resolver, $plugin_manager); + $plugin_type_a = new PluginType($plugin_type_definition_a, $this->stringTranslation, $class_resolver, $plugin_manager, $typed_config_manager); $plugin_type_id_b = $this->randomMachineName(); $plugin_type_label_b = $this->randomMachineName(); $plugin_type_description_b = ''; @@ -118,7 +124,7 @@ class ListPluginTypesTest extends UnitTestCase { 'description' => $plugin_type_description_b, 'provider' => $this->randomMachineName(), ]; - $plugin_type_b = new PluginType($plugin_type_definition_b, $this->stringTranslation, $class_resolver, $plugin_manager); + $plugin_type_b = new PluginType($plugin_type_definition_b, $this->stringTranslation, $class_resolver, $plugin_manager, $typed_config_manager); $plugin_types = [ $plugin_type_id_a => $plugin_type_a, diff --git a/tests/src/Unit/Controller/ListPluginsTest.php b/tests/src/Unit/Controller/ListPluginsTest.php index c264075..365ea91 100644 --- a/tests/src/Unit/Controller/ListPluginsTest.php +++ b/tests/src/Unit/Controller/ListPluginsTest.php @@ -8,6 +8,7 @@ namespace Drupal\Tests\plugin\Unit\Controller; use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\plugin\Controller\ListPlugins; @@ -101,6 +102,11 @@ class ListPluginsTest extends UnitTestCase { $plugin_manager = $this->getMock(PluginManagerInterface::class); + $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class); + $typed_config_manager->expects($this->atLeastOnce()) + ->method('hasConfigSchema') + ->willReturn(TRUE); + $plugin_type_id = $this->randomMachineName(); $plugin_type_label = $this->randomMachineName(); @@ -109,7 +115,7 @@ class ListPluginsTest extends UnitTestCase { 'label' => $plugin_type_label, 'provider' => $this->randomMachineName(), ]; - $plugin_type = new PluginType($plugin_type_definition, $this->stringTranslation, $class_resolver, $plugin_manager); + $plugin_type = new PluginType($plugin_type_definition, $this->stringTranslation, $class_resolver, $plugin_manager, $typed_config_manager); $this->pluginTypeManager->expects($this->atLeastOnce()) ->method('getPluginType') diff --git a/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php b/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php index 3154a0c..9753bc2 100644 --- a/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php +++ b/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php @@ -8,6 +8,7 @@ namespace Drupal\Tests\plugin\Unit\Plugin\Field\FieldType; use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\plugin\Plugin\Field\FieldType\PluginCollectionItemDeriver; use Drupal\plugin\PluginType\PluginType; @@ -71,6 +72,11 @@ class PluginCollectionItemDeriverTest extends UnitTestCase { $plugin_manager = $this->getMock(PluginManagerInterface::class); + $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class); + $typed_config_manager->expects($this->atLeastOnce()) + ->method('hasConfigSchema') + ->willReturn(TRUE); + $provider = $this->randomMachineName(); $plugin_type_id_a = $this->randomMachineName(); @@ -82,7 +88,7 @@ class PluginCollectionItemDeriverTest extends UnitTestCase { 'description' => $plugin_type_description_a, 'provider' => $this->randomMachineName(), ]; - $plugin_type_a = new PluginType($plugin_type_definition_a, $string_translation, $class_resolver, $plugin_manager); + $plugin_type_a = new PluginType($plugin_type_definition_a, $string_translation, $class_resolver, $plugin_manager, $typed_config_manager); $plugin_type_id_b = $this->randomMachineName(); $plugin_type_label_b = $this->randomMachineName(); $plugin_type_description_b = ''; @@ -92,7 +98,7 @@ class PluginCollectionItemDeriverTest extends UnitTestCase { 'description' => $plugin_type_description_b, 'provider' => $this->randomMachineName(), ]; - $plugin_type_b = new PluginType($plugin_type_definition_b, $string_translation, $class_resolver, $plugin_manager); + $plugin_type_b = new PluginType($plugin_type_definition_b, $string_translation, $class_resolver, $plugin_manager, $typed_config_manager); $plugin_types = [$plugin_type_a, $plugin_type_b]; diff --git a/tests/src/Unit/PluginType/PluginTypeTest.php b/tests/src/Unit/PluginType/PluginTypeTest.php index 1e729ef..2fe775a 100644 --- a/tests/src/Unit/PluginType/PluginTypeTest.php +++ b/tests/src/Unit/PluginType/PluginTypeTest.php @@ -8,6 +8,7 @@ namespace Drupal\Tests\plugin\Unit\PluginType; use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator; use Drupal\plugin\PluginDefinition\PluginDefinitionDecoratorInterface; @@ -63,11 +64,17 @@ class PluginTypeTest extends UnitTestCase { $class_resolver = $this->getMock(ClassResolverInterface::class); + $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class); + $typed_config_manager->expects($this->atLeastOnce()) + ->method('hasConfigSchema') + ->willReturn(TRUE); + $this->pluginManager = $this->getMock(PluginManagerInterface::class); $this->container = $this->getMock(ContainerInterface::class); $map = [ ['class_resolver', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $class_resolver], + ['config.typed', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $typed_config_manager], ['string_translation', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->getStringTranslationStub()], [$this->pluginTypeDefinition['plugin_manager_service_id'], ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->pluginManager], ]; @@ -129,6 +136,27 @@ class PluginTypeTest extends UnitTestCase { } /** + * @covers ::getPluginConfigurationSchemaId + */ + public function testGetPluginConfigurationSchemaIdWithDefaultId() { + $plugin_id = 'FooBarQux'; + $expected_schema_id = sprintf('plugin.plugin_configuration.%s.%s', $this->pluginTypeDefinition['id'], $plugin_id); + $this->assertSame($expected_schema_id, $this->sut->getPluginConfigurationSchemaId($plugin_id)); + } + + /** + * @covers ::getPluginConfigurationSchemaId + */ + public function testGetPluginConfigurationSchemaIdWithDefinedId() { + $plugin_id = 'FooBarQux'; + $schema_id = 'foo_bar.qux.[plugin_id]'; + $this->pluginTypeDefinition['plugin_configuration_schema_id'] = $schema_id; + $this->sut = PluginType::createFromDefinition($this->container, $this->pluginTypeDefinition); + $expected_schema_id = 'foo_bar.qux.' . $plugin_id; + $this->assertSame($expected_schema_id, $this->sut->getPluginConfigurationSchemaId($plugin_id)); + } + + /** * @covers ::ensureTypedPluginDefinition * @covers ::createFromDefinition * @covers ::__construct