how to create plugin in drupal8

Plugin is everywhere in Drupal 8. The plugins are inevitable while developing Drupal8 portals. Plugins are OOPS replacement of info hooks (D7) and any hooks associated with info hooks. Eg: Blocks, Views etc

Here I am explaining how to create our own plugin in Drupal8

The source code can be found in https://github.com/naushunaushad/fruitsplugin

We are going to create Fruits plugin type. We can create different fruits plugin such Apple, Orange, Grape etc and each fruits provides us vitamins and minerals they have.

First we need to create plugin type Fruits. We need the followings to create a plugin type Fruits.

1. Fruits plugin manager

Fruits plugin manager is the central controlling class of Fruits plugin type. It will describe the plugin path, interface and discovery methods. It also contain alter hooks information and caching information.

namespace Drupal\fruits\Plugin;

use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;

/*
 * Provides FruitsPluginManager
 */

class FruitsPluginManager extends DefaultPluginManager {
    
    /*
     * @param $namespaces used for checking plugin implemenations
     * @param $cache_backend used for caching the plugin defenition
     * @param $module_hanlder used to invoke alter hooks
     */

    public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
        parent::__construct('Plugin/Fruits', $namespaces, $module_handler, 'Drupal\fruits\Plugin\FruitsInterface', 'Drupal\fruits\Annotation\Fruits');
        //hook_fruits_fruits_info_later
        $this->alterInfo('fruits_fruits_info');
        $this->setCacheBackend($cache_backend, 'fruits_fruits_plugins');
    }    

}

Check the constructor, you can see lot of parameters.

$namespaces is used for checking the route paths of plugin implementations, $cache_backend is used for caching the plugin defenition, $module_handler is used to invoke alter hooks

'Plugin/Fruits' is the path of Fruits plugin, 'Drupal\fruits\Plugin\FruitsInterface' is the interface used for this plugin type, 'Drupal\fruits\Annotation\Fruits' is the annotation used for this plugin type (Annotation is the discovery method for our Fruits plugin)

$this->alterInfo('fruits_fruits_info'); means we can alter the plugin using modulename_fruits_fruits_info_alter() hooks in our module.

2. Create a services called fruits.fruits_manager in your module services.yml file

services:
    fruits.fruits_manager:        
        class: Drupal\fruits\Plugin\FruitsPluginManager
        parent: default_plugin_manager

3. Create FruitsInterface that we already used in plugin manager


namespace Drupal\fruits\Plugin;

use Drupal\Component\Plugin\PluginInspectionInterface;

/*
 * Provides FruitsInterface
 */

interface FruitsInterface extends PluginInspectionInterface {
    public function vitamins();
    public function minerals();    
}


Here we have two abstract methods vitamins and minerals. We can add this method in our fruits plugins to provide the vitamins and minerals informations

4. create annotation class 'Drupal\fruits\Annotation\Fruits'

Please check that it should be another folder  'Annotation'

namespace Drupal\fruits\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
 * Defines a fruits annotation object.
 *
 * @Annotation
 */

class Fruits extends Plugin {
    
   /*
    *  The plugin ID
    *  @var string
    */
    
    public $id;
    
    /*
    *  The Label
    *  @var string
    */
   
    public $label;
    
    /*
    *  Fruits Familiy
    *  @var string
    */
    
    public $family;

}

Here @Annotation is required. Otherwise the annotation parser cant identify the plugins.

5. Create a base class that needed for our plugins. This is not mandatory. But this is the best practice.

namespace Drupal\fruits\Plugin;

use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\fruits\Plugin\FruitsInterface as FruitsPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class FruitsBase extends PluginBase implements FruitsPluginInterface, ContainerFactoryPluginInterface {
    /*
     * @param $configuration used for handling plugin configuration
     * @param $plugin_id ID of plugin
     * @param $plugin_definition Defenition of plugin
     */
    public function __construct(array $configuration, $plugin_id, $plugin_definition) {
        parent::__construct($configuration, $plugin_id, $plugin_definition);   
    }
    
    /*
     * {@inheritdoc}
     */    
    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
        return new static( $configuration, $plugin_id, $plugin_definition);
    }
    
    /*
     * plugin methods
     * get vitamins info
     */
    public function vitamins(){        
       return ['A','B','C','D','E','K','G','B1','B2','B3','B5','B7'];
    }
    
    /*
     * plugin methods
     * get minerals info
     */
    public function minerals(){
       return ['calcium', 'chloride', 'magnesium', 'phosphorus', 'potassium', 'sodium', 'zinc'];  
    }
    

}

Our plugin type is ready. So that we can check how to create plugins under this plugin type.

In your module directory src/Plugin/Fruits you can create plugin. Here I created AppleFruits plulgin. We can extend this from FruitsBase . The annotation should be added on top of the class for plugin discovery.


namespace Drupal\testfruits\Plugin\Fruits;

use Drupal\fruits\Plugin\FruitsBase;

/**
 * Provides an 'Apple Fruits' data.
 *
 * @Fruits(
 *   id = "apple_fruits",
 *   label = @Translation("Apple"),
 *   family = @Translation("Rosaceae")
 * )
 */
class AppleFruits extends FruitsBase {  
   
  /**
   * {@inheritdoc}
   */  
  public function vitamins(){        
      return ['A','B','C'];
  }
 
  /**
   * {@inheritdoc}
   */  
  public function minerals(){
     return ['calcium'];  
  }
}