Writing a Drupal 8 Image Effect Plugin for Image Styles

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:

  • Autoloading and PSR-0 to ensure Drupal knows how to load your plugin class.
  • The annotation found at the top of the plugin class, allowing you to specify metadata about your plugin.
  • Extending the right PluginBase for your purposes. Each type of plugin will define their own plugin base.
  • Implementing ContainerFactoryPluginInterface while optional, enforces the correct use of dependency injection for the plugin system.

Hopefully this can help people onto the right track when writing image effects and maybe shine some light on Drupal’s new plugin system.

Author Info

Sam Becker
Developer

Sam is a passionate developer and Star Trek fan who loves tinkering with all things web. He maintains several Drupal modules and has way too many Github repos.

Comments

Yeah, Image effects will be much more structured and therefore easier to define, develop and maintain in D8.
However, a big warning: the image system is still in heavy flux. Thus already converting your image effects to D8 will require rerolls.

Besides a number of structural issues, there is also an issue that will make life for your effect easier. Some help (review, thinking along) on the issues would be highly appreciated (issues #2105863 and #2168511).

Thanks for the input fietserwin. It's interesting to note from an outside perspective, not having seen those issues, that the image effect system is still in flux. On the surface the developer experience seemed very good. Good luck with those two issues and thanks for your work on core.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.