Change record status: 
Project: 
Introduced in branch: 
9.4.x
Introduced in version: 
9.4.9
Description: 

A CKEditor 5 plugin definition can now optionally declare a deriver, to remove the need to abuse hook_ckeditor5_plugin_info_alter() to dynamically declare multiple plugins dependent on external factors (e.g. configuration, a web service, …), for example to provide one button per supported type of fish:

Before
MYMODULE_embed_herring:
  ckeditor5:
    plugins: [foo.Foo]
  drupal:
    label: Embed herring
    library: MYMODULE/LIBRARY
    class: Drupal\MYMODULE\Plugin\CKEditor5Plugin\FishEmbedder
    toolbar_items:
      fish_embedder_herring:
        label: Embed herring
    elements:
      - <fish>
      - <fish type="herring">

MYMODULE_embed_trout:
  ckeditor5:
    plugins: [foo.Foo]
  drupal:
    label: Embed trout
    library: MYMODULE/LIBRARY
    class: Drupal\MYMODULE\Plugin\CKEditor5Plugin\FishEmbedder
    toolbar_items:
      fish_embedder_trout:
        label: Embed trout
    elements:
      - <fish>
      - <fish type="trout">
After
The plugin annotation key-value pairs that are the same across all derived plugin definitions can be defined in the base plugin definition:
MYMODULE_embed:
  ckeditor5:
    plugins: [foo.Foo]
  drupal:
    library: MYMODULE/LIBRARY
    class: Drupal\MYMODULE\Plugin\CKEditor5Plugin\FishEmbedder
    deriver: Drupal\MYMODULE\Plugin\CKEditor5Plugin\FishEmbedderDeriver
    elements:
      - <fish>

+

class FishEmbedderDeriver extends DeriverBase {
  public function getDerivativeDefinitions($base_plugin_definition) {
    assert($base_plugin_definition instanceof CKEditor5PluginDefinition);
    foreach (['herring', 'trout'] as $id) {
      $definition = $base_plugin_definition->toArray();
      $definition['id'] = $id;
      $definition['drupal']['label'] = sprintf("Embed %s", $id);
      $definition['drupal']['elements'][] = sprintf('<fish type="%s">', $id);
      $definition['drupal']['toolbar_items']["fish_embedder_$id"]['label'] = $definition['drupal']['label'];
      $this->derivatives[$id] = new CKEditor5PluginDefinition($definition);
    }
    return $this->derivatives;
  }
}

… and the key-value pairs that are unique to each derived plugin definition can be specified in the deriver.

Note: CKEditor 5 plugin definitions can also be specified on the class annotation, and there too a deriver can be specified:

/**
 * @CKEditor5Plugin(
 *   id = "MYMODULE_embed",
 *   ckeditor5 = @CKEditor5AspectsOfCKEditor5Plugin(
 *     plugins = {"foo.Foo"},
 *   ),
 *   drupal = @DrupalAspectsOfCKEditor5Plugin(
 *     elements = {"<fish>"},
 *     deriver = "Drupal\MYMODULE\Plugin\CKEditor5Plugin\FishEmbedderDeriver",
 *   )
 * )
 */
class Foo extends CKEditor5PluginDefault {
Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

james.williams’s picture

Seems a little fishy to me!

ComputerMinds

mark_fullmer’s picture

I'd been floundering a bit integrating CKEditor 5 plugins in Drupal, so I'm glad to see this help plugins scale. Here's how I am understanding this change's porpoise:

1. https://www.drupal.org/project/ckeditor5_embedded_content , which defines a *single* CKEditor plugin that can be extended to add more options to that single plugin (see those extensions in ckeditor5_embedded_content_examples/src/Plugin/EmbeddedContent/). This would *not* use a deriver, since there's only one CKEditor plugin (see ckeditor5_embedded_content.ckeditor5.yml )
2. In contrast, some of the plugins in Drupal core (core/modules/ckeditor5/ckeditor5.ckeditor5.yml), perhaps such as those related to images, could use a deriver to register multiple buttons.