Khanh Hoang - Kenn
Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.
Recently at Drupal South I had the opportunity to upgrade the Image Style Quality module to Drupal 8. I was lucky enough to be surrounded by some of Australia's top Drupal contributors who were more than happy to lend a hand with the ins and outs. In this blog post I will run through the entire processes of writing an image effect plugin for Drupal 8.
First you'll need an empty module. Image effects use Drupal 8's new plugin system, a generic way of enhancing the functionality of other modules. No procedural code is required for writing plugins, however you'll need a @file docblock comment in module_name.module to adhere to coding standards. All you'll need to get your image effect plugin recognised is create a class in the right directory and add an annotation. Your directory structure should look something like the following:
module_name -- module_name.info.yml -- module_name.module -- lib ---- Drupal ------ module_name -------- Plugin ---------- ImageEffect ------------ ModuleNameImageEffect.php
The two bits of information that need to change to match your specific use case are the module_name and ModuleNameImageEffect.php. The directory structure follows PSR-0 which is a standard that defines a relationship between directory structures and namespaces, so the class you use for your plugin will need to match the filename it resides in. You can read up on what to place in your .yml file here. Below is a cut down example of an entire image effect plugin taken from image style quality.
I have annotated the source of the plugin with verbose comments of my understanding of how the plugin system and the image effect plugin base works.
<?php /** * @file * Contains \Drupal\image_style_quality\Plugin\ImageEffect\ImageStyleQualityImageEffect */ // Ensure the namespace here matches your own modules namespace and directory structure. namespace Drupal\image_style_quality\Plugin\ImageEffect; // The various classes we will be using for the definition and application of our ImageEffect. use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Image\ImageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\image\ImageEffectBase; use Drupal\image\ConfigurableImageEffectInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * A description of the image effect plugin. * * The annotation below is the mechanism that all plugins use. It allows you to specify metadata * about the class. You'll need to update this to match your use case. * * @ImageEffect( * id = "image_style_quality", * label = @Translation("Image Style Quality"), * description = @Translation("Allows you to change the quality of an image, per image style.") * ) */ class ImageStyleQualityImageEffect extends ImageEffectBase implements ConfigurableImageEffectInterface, ContainerFactoryPluginInterface { /** * {@inheritdoc} */ public function applyEffect(ImageInterface $image) { // Apply any effects to the image here. } /** * {@inheritdoc} */ public function getForm() { // Return a configuration form to allow the user to set some options. } /** * {@inheritdoc} */ public function __construct(array $configuration, $plugin_id, array $plugin_definition, ConfigFactoryInterface $config) { // The ConfigFactoryInterface is injected from the create method below. parent::__construct($configuration, $plugin_id, $plugin_definition); } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { // Pull anything out of the container interface and inject it into the plugin's constructor. // In this case I have chosen to inject the entire config factory, however you should be as // specific as possible when using dependency injection. return new static( $configuration, $plugin_id, $plugin_definition, $container->get('config.factory') ); } /** * {@inheritdoc} */ public function getSummary() { // Return a summary of the options the user has chosen. This appears after the image effect // name in the user interface. I have chosen to specify the option the user has selected inside // brackets. This seems to be a convention. $quality = $this->configuration['image_jpeg_quality']; return array( '#markup' => '(' . $quality . '% ' . $this->t('Quality') . ')', ); } } ?>
In this case we have chosen to implement ConfigurableImageEffectInterface and ContainerFactoryPluginInterface. ConfigurableImageEffectInterface ensures that we implement the getForm() method, allowing us to have user configurable options. You can also read some more about dependency injection if you like. It should also be noted the above plugin class is bare bones, additional constructs which organise and structure code can also be used. Things such as additional methods, classes or layers of inheritance are all possible given Drupal 8's OO structure.
The concepts covered when writing an image effect plugin are very similar to the concepts used when writing any other plugin. Plugins are used for many different facets of Drupal 8 development including block definitions, field formatters, migration plugins, entities and views just to name a few. The key concepts to take away from writing a plugin, that will be useable across many different facets of Drupal development are:
Hopefully this can help people onto the right track when writing image effects and maybe shine some light on Drupal’s new plugin system.