As the plugin system gets closer and closer to being committed for D8, I've built a plugin example module to show how it can be used. This is a very simple example at the moment, but should get the basic point across. I wanted to at least point people here at it, even if it's not really ready for inclusion into the Examples module.

http://drupal.org/sandbox/eclipsegc/1663586

Eclipse

Comments

bobbyaldol’s picture

Assigned: Unassigned » bobbyaldol
mile23’s picture

Status: Active » Postponed

Marking as postponed for now: #1861598: Examples for Drupal 8 needs relatively stable feature set

Please continue development and discussion as desired.

mile23’s picture

Status: Postponed » Needs work
joachim’s picture

Status: Needs work » Needs review
StatusFileSize
new12.28 KB

Here's an example of defining a plugin type.

It defines a Sandwich plugin type, by implementing a SandwichPluginManager. The module also has a single definition of a Sandwich plugin.

What's not yet covered here:

- use of a class other than @Plugin for the annotation. I'm not actually sure yet when you'd want to do that, and it adds complexity to the example anyway.
- use of derivative plugins. Again, these add complexity and I think it's crucial for this example to be simple. I think derivative plugins should be a separate example module. If they are in the same module, then it's harder to work out which parts of the code are needed for which functionality -- and plugin functionality is split over several places, so it's hard to keep track.

There are various TODO comments in the code where I could really use input from someone who knows the plugin system better than I do!

Status: Needs review » Needs work

The last submitted patch, 1668362.examples.plugin-type-example-module.patch, failed testing.

joachim’s picture

Status: Needs work » Needs review

Hmmm I don't think this patch is responsible for Drupal\block_example\Tests\BlockExampleTest failing! :D

mile23’s picture

Looks good, FTMP. Thanks.

But... No tests and too many @todos. :-)

mile23’s picture

Status: Needs review » Needs work
joachim’s picture

I'll write some tests.

For the TODOs, I need input from, ideally, some of the brains behind the Plugin API on D8 -- because they are things I simply don't know or understand. If someone can provide answers here to the questions in the TODOs, I'll happily incorporate them into the code comments and reroll the patch.

joachim’s picture

Status: Needs work » Needs review
StatusFileSize
new15.29 KB

New patch with test.

Status: Needs review » Needs work

The last submitted patch, 1668362.10.examples.plugin-type-example-module.patch, failed testing.

joachim’s picture

Status: Needs work » Needs review
StatusFileSize
new15.5 KB

I found a bug in the plugin manager constructor.

mile23’s picture

Status: Needs review » Needs work

...

mile23’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 1668362.12.examples.plugin-type-example-module.patch, failed testing.

longwave’s picture

Issue summary: View changes
+// TODO: explain this
+namespace Drupal\plugin_type_example\Plugin\Sandwich;
+
+// TODO: explain what these are for.
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Component\Annotation\Plugin;

Not sure this needs explaining here in particular; "namespace" and "use" are standard PHP OO constructs nowadays.

+ * Because the plugin manager class SandwichPluginManager uses annotated class
+ * discovery, our ham sandwich only needs to exist within the Plugin\Sandwich
+ * namespace to be declared as a plugin.
+ * TODO! ^^^ is the above correct?

In a nutshell, this is correct; the PluginManager constructor declares the namespace prefix of the plugins that it manages.

+ * Note that this array may NOT have a terminal comma, unlike PHP.
This is no longer correct, terminal commas are now allowed in annotations.

+ * @Plugin(
It is now recommended that plugins define their own plugin-specific annotations, by implementing a class in the \Drupal\module\Annotation namespace. This lets them declare and document the keys used in that specific plugin.

+   * This is just an example method on our plugin that we can call to get
+   * something back. As this is returning static data, in a real-world situation
+   * this would be better off in the annotation.

No, the annotation should only be used for metadata that is specifically required to instantiate the plugin, or for example data that might be needed to display a list of all available plugins where the user selects one. This means many plugin annotations can be reduced to a plugin ID, a label and perhaps a description.

+    # TODO: explain what these magic strings mean and how they work!
+    arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']

Arguments beginning with @ are the names of other services are being injected into this service. Many common ones are found in /core/core.services.yml. More docs on this can be found at https://drupal.org/node/2133171

joachim’s picture

Status: Needs work » Needs review
StatusFileSize
new15.68 KB

Thanks!

Here's a new patch with those changes, and also some updating for changes in D8.

Status: Needs review » Needs work

The last submitted patch, 17: 1668362.17.examples.plugin-type-example-module.patch, failed testing.

socketwench’s picture

Status: Needs work » Needs review
StatusFileSize
new15.78 KB
new2.16 KB

Fixed plugin manager service def, routing, controller.

Status: Needs review » Needs work

The last submitted patch, 19: 1668362.19.examples.plugin-type-example-module.patch, failed testing.

socketwench’s picture

Status: Needs work » Needs review
StatusFileSize
new15.78 KB
new1.07 KB

Fixed broken Plugin test. This patch will still turn red because of existing problems in other submodules. (We really need to fix those.)

EDIT: SPELLING.

Status: Needs review » Needs work

The last submitted patch, 21: 1668362.21.examples.plugin-type-example-module.patch, failed testing.

mile23’s picture

Branch failures get very high priority when I'm done traveling.

Also patch reviews. :-)

But mostly, thanks for sticking with it.

socketwench’s picture

mile23’s picture

mile23’s picture

  1. +++ b/plugin_type_example/lib/Drupal/plugin_type_example/SandwichPluginManager.php
    @@ -0,0 +1,64 @@
    +   // TODO: explain all of the params in more detail!!
    +   *
    +   * @param string|bool $subdir
    +   *   The plugin's subdirectory, for example Plugin/views/filter.
    +   * @param \Traversable $namespaces
    +   *   An object that implements \Traversable which contains the root paths
    +   *   keyed by the corresponding namespace to look for plugin implementations.
    +   * @param array $annotation_namespaces
    +   *   (optional) The namespaces of classes that can be used as annotations.
    +   *   Defaults to an empty array.
    +   * TODO: incomplete params!
    +   */
    +  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
    

    Docs don't match arguments.

  2. +++ b/plugin_type_example/lib/Drupal/plugin_type_example/SandwichPluginManager.php
    @@ -0,0 +1,64 @@
    +    // This sets the caching method for our plugin definitions.
    +    // TODO: explain what the parameters mean and do.
    +    $this->setCacheBackend($cache_backend, $language_manager, 'sandwich_info');
    

    Ideally we'd have @see cache_example,

socketwench’s picture

Issue tags: +Needs tests, +Needs PSR-4
mile23’s picture

StatusFileSize
new15.82 KB
new23.91 KB

Here's a very incomplete patch with the PSR-4 changes, and the addition of a *.menu_links.yml file.

Still needs a test for the menu links, and also: It fails to display its description page. Is going to need a bunch of work still. (I ran out of time last night.)

mile23’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 28: 1668362_27.patch, failed testing.

cgalli’s picture

Hey Mile, if you agree, I will take care of this module over the coming weeks. With the support of berdir...

mile23’s picture

Assigned: bobbyaldol » Unassigned

Sure. You and @bobbyaldol can arm-wrestle for it if he's still interested. :-)

m4olivei’s picture

StatusFileSize
new16.98 KB
new4.32 KB

Here's an updated patch that should work with the latest D8. A few thing have changed since this last patch that I can tell:

* DefaultPluginManager class now also takes the name of an interface. It uses this to force plugins to adhere to it.
* DefaultPluginManager::setCacheBackend() method arguments changed
* I also make the plugin_type_example.routing.yml file consistent with others in the module.

I'll continue working on this as I have time and hopefully round it out, finishing up the remaining TODO's as best I know how. For now though, this patch makes it so that it'll actually work.

mile23’s picture

Status: Needs work » Needs review
Issue tags: -Needs PSR-4

Status: Needs review » Needs work

The last submitted patch, 33: 1668362_33.patch, failed testing.

mile23’s picture

Looks good. Here's some review:

  1. +++ b/plugin_type_example/plugin_type_example.menu_links.yml
    @@ -0,0 +1,3 @@
    +plugin_type_example.description:
    +  title: Plugin Type Example
    +  route_name: plugin_type_example
    
    +++ b/plugin_type_example/plugin_type_example.routing.yml
    @@ -0,0 +1,8 @@
    +plugin_type_example.description:
    +  path: '/examples/plugin_type_example'
    

    I see a menu_links.yml and a route, but I don't see the menu when I enable the module.

    Also: Needs a link in the toolbar (from examples module).

  2. +++ b/plugin_type_example/plugin_type_example.module
    @@ -0,0 +1,42 @@
    + * This example is part of the Examples for Developers Project
    + * which you can download and experiment with at
    + * http://drupal.org/project/examples
    + *
    + * Start at the class \Drupal\plugin_type_example\SandwichPluginManager, which
    + * defines our plugin type manager.
    

    Remove the explanation of examples, and add a brief walk-through of what the module demonstrates, and where to see each thing.

  3. +++ b/plugin_type_example/src/Plugin/Sandwich/ExampleHamSandwich.php
    @@ -0,0 +1,67 @@
    +  public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
    +    parent::__construct($configuration, $plugin_id, $plugin_definition);
    +  }
    

    Do we really need this constructor?

  4. +++ b/plugin_type_example/src/SandwichInterface.php
    @@ -0,0 +1,24 @@
    +interface SandwichInterface extends PluginInspectionInterface, DerivativeInspectionInterface {
    

    Nice to have an interface.

  5. +++ b/plugin_type_example/src/SandwichPluginManager.php
    @@ -0,0 +1,68 @@
    +   // TODO: explain all of the params in more detail!!
    ...
    +   * TODO: incomplete params!
    

    Yah, coding standards. :-)

    Also, any narrative explanation of why you need certain services or dependencies should be in the docblock, with only a brief description in @param.

  6. +++ b/plugin_type_example/src/SandwichPluginManager.php
    @@ -0,0 +1,68 @@
    +   * @param array $annotation_namespaces
    +   *   (optional) The namespaces of classes that can be used as annotations.
    +   *   Defaults to an empty array.
    

    Marked as optional, but no default is provided in the method signature.

  7. +++ b/plugin_type_example/src/SandwichPluginManager.php
    @@ -0,0 +1,68 @@
    +    // TODO: explain what the parameters mean and do.
    

    Is this relevant?

m4olivei’s picture

StatusFileSize
new19.96 KB
new16.42 KB

Thanks for the review!

I think I covered everything. I also combed through all the files and updated some docs and made some coding standards tweaks. Responses to your comments:

1. I've banged my head against the wall on this. I don't know why it's not showing up in the menu. Help?
2. Done
3. Nope, gone.
4. Agreed, created.
5. Done. I think I did anyhow.
6. Updated these docs based on some other core plugin managers.
7. Couldn't hurt, I've added some commentary there.

I also added a second plugin, ExampleMeatballSandwich. Thought it better demonstrated plugin types to have more than one. It also helps show off the alter hook better.

mile23’s picture

Status: Needs work » Needs review

You can set the issue to 'needs review' and the testbot will start. :-)

The last submitted patch, 33: 1668362_33.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 37: 1668362_37.patch, failed testing.

m4olivei’s picture

StatusFileSize
new19.71 KB
new1.66 KB

Ahh, right. Here is an updated patch to fix the tests. Removed a getInfo method, which seemed like an artifact, b/c the description seems to come from the class docblock. Also set the test $profile to 'minimal' b/c that's the only way I could get the testPluginExamplePage test to pass, otherwise it was returning 403 on that first assertion. Anyhow. Crossing fingers for test success.

m4olivei’s picture

Status: Needs work » Needs review

The last submitted patch, 33: 1668362_33.patch, failed testing.

The last submitted patch, 37: 1668362_37.patch, failed testing.

m4olivei’s picture

Assigned: Unassigned » m4olivei
mile23’s picture

Status: Needs review » Needs work

Cool, thanks.

A few more things:

  1. +++ b/plugin_type_example/plugin_type_example.module
    @@ -0,0 +1,63 @@
    + * To see the plugins in action visit /examples/plugin_type_example.  The output
    + * is rendered in Drupal\plugin_type_example\Controller\PluginTypeExampleController::description().
    + * Read through that method to see how to use plugins using the
    + * plugin.manager.sandwich service.
    

    Wrap at 80. It's OK if the class name and method don't wrap.

  2. +++ b/plugin_type_example/src/Controller/PluginTypeExampleController.php
    @@ -0,0 +1,87 @@
    +    $build['intro'] = array(
    +      '#markup' => t('The plugin type example page.'),
    +    );
    

    Add a description of what this page is showing, referencing the plugin classes.

  3. +++ b/plugin_type_example/src/Controller/PluginTypeExampleController.php
    @@ -0,0 +1,87 @@
    +    // Get our plugin manager from the service container.
    +    // The string we pass is the machine name of the service, which is set in
    +    // the plugin_type_example.services.yml file.
    +    // We get back an instance of our SandwichPluginManager class.
    +    $manager = \Drupal::service('plugin.manager.sandwich');
    

    We should inject this service.

m4olivei’s picture

Status: Needs work » Needs review
StatusFileSize
new21.54 KB
new5.76 KB

Thanks again for the feedback. Really appreciate it. TIL how to inject a service into a controller. Updated patches attached.

mile23’s picture

StatusFileSize
new6.23 KB

I did some minor updates and coding standards stuff. Patch attached.

Also:

+++ b/plugin_type_example/plugin_type_example.module
@@ -0,0 +1,64 @@
+/**
+ * Implements hook_sandwich_info_alter().
+ *
+ * This hook allows other modules to alter the definitions of all the Sandwich
+ * plugins. The module that creates the plugin type probably wouldn't implement
+ * this itself, this is just for demonstration.
+ *
+ * This hook is invoked by SandwichPluginManager::__construct().
+ *
+ * @param $sandwich_plugin_info
+ *   This is the array of plugin definitions.
+ */
+function plugin_type_example_sandwich_info_alter(&$sandwich_plugin_info) {

So the docblock here should only say 'Implements hook_sandwich_info_alter().' We also need an api.php file because we're defining a hook. Docs: https://www.drupal.org/coding-standards/docs#hooks

mile23’s picture

StatusFileSize
new21.77 KB
m4olivei’s picture

StatusFileSize
new22.37 KB
new2.1 KB

api.php file created and doc block for plugin_type_example_sandwich_info_alter() updated. Also wrapped a string in t().

  • Mile23 committed af12d5e on 8.x-1.x authored by m4olivei
    Issue #1668362 by m4olivei, joachim, Mile23, socketwench, longwave: D8...
mile23’s picture

Status: Needs review » Fixed
Issue tags: -Needs tests

Great, thanks!

Committed, with some coding standards fixes.

m4olivei’s picture

w00t!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.