Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Feb 02 2018
Feb 02

A few years ago I started using the PhpStorm IDE for PHP development, was immediately smitten and, after a bit of use, wrote a blog post with some tips I found for makig better use of the tools PhpStorm gives you.

In the four years since then there have been some new developments. Firstly, of course, Drupal 8 was finally released – and, consequently, the one complaint I had back in 2013 about the $MODULE$ variable only working in the module file itself became more of a problem. (Also, I added one more live template that's very useful for Drupal 8.)
But secondly, a few weeks ago PhpStorm finally added scripting support for live templates, so it's now possible to write more powerful templates that way – and fix the $MODULE$ variable.

The new di live template

In general, when writing OOP code for Drupal 8 (that is, for almost all Drupal 8 code) you should use dependency injection as much as possible. There's several different styles for doing that, I'm using one which uses setter methods and calls them in create() (instead of adding all injected objects to the constructor). This makes inheritance easier and keeps the constructor “cleaner” – and becomes much easier with a good live template:

  /**
   * The $NAME$.
   *
   * @var $INTERFACE$|null
   */
  protected $$$PROP_NAME$;

  /**
   * Retrieves the $NAME$.
   *
   * @return $INTERFACE$
   *   The $NAME$.
   */
  public function get$UC_PROP_NAME$() {
    $plugin->set$UC_PROP_NAME$($container->get('$SERVICE$'));

    return $this->$PROP_NAME$ ?: \Drupal::service('$SERVICE$');
  }

  /**
   * Sets the $NAME$.
   *
   * @param $INTERFACE$ $$$VAR_NAME$
   *   The new $NAME$.
   *
   * @return $this
   */
  public function set$UC_PROP_NAME$($INTERFACE$ $$$VAR_NAME$) {
    $this->$PROP_NAME$ = $$$VAR_NAME$;
    return $this;
  }

Variable definitions:

Name Expression Skip if defined VAR_NAME N SERVICE N INTERFACE clipboard() Y NAME underscoresToSpaces(VAR_NAME) Y UC_NAME underscoresToCamelCase(VAR_NAME) Y UC_PROP_NAME capitalize(PROP_NAME) Y

Usage:

  1. Copy the service interface's FQN to your clipboard.
  2. Put the service ID either into a secondary clipboard (e.g., middle mouse button on Linux) or remember it.
  3. Execute live template (at the position where you want the getter and setter).
  4. Input variable name (in snake_case), then input service name.
  5. Move the property definition and the create() line (temporarily stored as the first line of the getter in the template) to their appropriate places.
  6. In the code, alway use the getter method for accessing the service.

Fixing the $MODULE$ variable

Since the code for this is quite complex, we better just put it into a separate file. So, first download the script file and save it to some known location, then simply use the (absolute) path to the script file as the argument for groovyScript(), like this:

groovyScript(

This can be used for all the live templates that contain a $MODULE$ variable (though it will, of course, be less useful for the procedural ones, than for the simple m template).

Apr 05 2017
Apr 05

TL; DR: After more than three years of development (and several blog posts), the Drupal 8 port of the Search API module is finally getting a stable release! The current Beta 5 release will be the last Beta, and there should be a release candidate and the final, stable release within the next two weeks. So, test it now, if you haven't already, to help eliminate any remaining (major) bugs!

The current status in detail

Last Saturday, April 1, I committed the fix for issue #2682369: Fix problems with overridden config entities, the last (and incredibly complicated – dedicated blog post upcoming) release blocker[1] for the stable release. The day after, I created the final Beta 5 release to give everyone still a few more days for testing.

Unless testing of this new Beta release discovers a serious enough problem, the first release candidate (RC) will be released on the coming weekend, April 8 or 9. Finally, if that doesn't uncover any major problems, the first stable release, Search API 8.x-1.0, will follow the weekend after that, on April 15 or 16. And then: rejoicing, parties and a little shield icon, to make everyone feel better.

To be clear, though, the Search API has been pretty usable for a long time (at least a year, I guess – possibly more, depending on your requirements), and we didn't have any major bugs or problems (unless you used config overrides) for some time now. The release blockers were mostly due to desired framework changes which would have been more difficult to do after a stable release, especially with other modules with stable releases already building on it. There were also several usability problems classified as release blockers, which was probably a mistake in hindsight. Having a clearer UI is nice, and helps avoid floods of bug reports due to confused users – but it shouldn't delay a stable release, and possibly dissuade site builders from using a basically working module. It also shouldn't prevent us from getting Security Team coverage, of course.

One plea regarding testing this last Beta: In case you are using config overrides for search indexes or servers in your site's setup or development workflow, it would be very helpful if you could verify that this last release fixes all associated problems you might have had – indexing/deleting using the wrong server (settings), overridden values being written back to the database, etc. This was a recent and pretty complex fix, so it would be great to feel more confident about that.

Also perhaps note-worthy: I've used the last few days of Dev Days Seville in March to finally update the module's documentation to Drupal 8. While it's still not quite finished, especially the documentation for developers, it should already provide a pretty good guide to the module's functionality and should help to answer most questions for first-time (or even advanced) users.

What's new

Compared to Drupal 7, the Search API in Drupal 8 is a massive step forwards – we didn't just spend those three years twiddling our thumbs, after all! Here's a run-down of the most important improvements we made:

Indexes can contain more than one type of item/entity

This might have been the single largest restriction in the Drupal 7 Search API: indexes could only be created for a single entity type (or custom item type). If you wanted a site search across entity types, you had to either use the Multi-Index Searches modules or, since August 2015, use the "Multiple types" item type, essentially a workaround-type backport of the D8 functionality.

In Drupal 8, however, creating an index for multiple item types (now called "datasources") is baked right into the framework, all parts of the Search API will support this natively. Furthermore, changing or re-configuring (think: changing the content types of nodes indexed) datasources is also supported right by the framework, and all such changes will be handled without any need to manually correct tracking data, or clear the index, etc.

Overhaul of the "Fields" tab and functionality

Also a continuing source of frustration, even if mostly just a usability annoyance, the "Fields" tab has seen a major revamp. Instead of seeing all available properties right on one page (killing the web server in some cases, for particularly large sites) and enabling the ones you want, we've transitioned to a UI inspired by Views: we only list the fields that are currently indexed and use a second page/dialog for discovery of available properties and adding the ones desired. This is also a lot more convenient than the old "Add related fields" box for indexing fields of related entities or data structures.

Furthermore, it's now possible to add the same property more than once, eliminating the need to use the "Aggregated fields" work-around of creating duplicates of a field. You can also freely change the human-readable name and the machine name of a field, and some processor-generated fields (like "Rendered HTML output") even allow per-field configuration (so you could have two "Rendered HTML output" fields, with each using different view modes, for different purposes). Also, like Views, changes made to the fields (but only to them) won't be effective immediately, but need to be explicitly saved afterwards. This avoids triggering a reindex numerous times when changing fields.

Improved language/translation support

Language support has also been vastly improved. While multilingual Drupal 7 sites needed the Search API Entity Translations contrib modules (at least when using the "entity translation" system, not "content translation") for getting proper translation support, which then didn't work natively with some other contrib modules, support for Drupal 8's new translation system is included right in the Search API itself. Automatically, all entities will be indexed once for every one of their translations, and each entity-based search item is identified not only by entity ID, but also by its translation language. Furthermore, additional internal improvements also make it easier for other modules (e.g., Search API Solr Multilingual) to add additional language-specific functionality.

Processors are more powerful than ever

This item is something that's probably only apparent as a developer, but processors have become a lot more powerful, being able to influence a lot of different areas of the search system. For non-developers, the most notable change is probably that the previous distinction between "processors" and "data alterations" has been removed – but not only have processors gained all the functionality previously held by data alterations, several other functions have been added, too.

For site builders, though, the main benefit here (in addition to more useful processors potentially becoming available) is a cleaner, less confusing user interface.

Objects everywhere! Also not really anything that non-developers will notice, but internally the Search API has fully embraced Drupal 8's move to object-oriented programming (OOP). While its Drupal 7 version was already among the most OOP-using modules available (I'm pretty sure), this gained even more momentum in this new version. Not only did we, of course, make use of all the new OOP-based APIs in Core, like entities, plugins, services, etc. – we also converted a lot of the internal data structures (which, like Core, often used "magic" array structures in Drupal 7) to properly classed objects (e.g., indexed items, search results, fields) and moved most helper functions to methods on service classes. Great test coverage The last item, which no-one ever gets really excited about, but is actually also a huge step forward, is that we have improved test coverage throughout the module tremendously – aided a lot, of course, by the corresponding improvements in Drupal Core and on drupal.org. This (hopefully) means a lot less bugs that go undiscovered, a lot less regressions when introducing new features, simpler, more confident refactoring of old code and consequently less technical debt.

Other projects

Search API has always relied on additional contrib modules to provide powerful search solutions – with just the Search API module, you wouldn't get far. So, what is the current status of related projects in Drupal 8?

  • First off, the Database Search module (which provides the "Database" backend for servers) has been moved back into the Search API project itself, so this is already completely stable and very thoroughly tested. So, if you have a smaller site and don't require any advanced functionality (as offered by Solr or Elasticsearch), by just downloading the Search API project you can already set up a pretty good database-based search.
  • The Facets module (replacement for the Drupal 7 Facet API module) is still in Alpha as there are still some API changes and breaks planned. However, its basic functionality is already there and quite extensive, and it's already used on quite a few production sites. So, if you want to add facetting to your search, you should at least give it a try.
  • The Search API Solr module is in a similar position: It's already used on a lot of production sites, but there's still some rough edges (especially regarding support of optional features) and the module is currently considered to be in Beta state. However, the test coverage is quite good, so there shouldn't be any larger bugs lurking under the surface and, if you want to use Solr for your site, you shouldn't hesitate to give it a try. (Just beware that, like all modules with no stable releases, it's currently not covered by the Drupal Security Team, so security issues are a larger risk than usual.)
  • If you want to use Elasticsearch, things look a bit worse. There are (confusingly) several modules in Drupal 7, but only Elasticsearch Connector has a Drupal 8 version yet. And since even the Drupal 7 version is not yet beyond Alpha (and the Drupal 8 version has nothing more than a dev snapshot release), it may take some time until it becomes stable. I haven't evaluated the code or functionality, though, so it might actually already work quite well – if you have information, please leave a comment below!
  • The Pages module has been ported to Drupal 8 (and is currently maintained) by swentel. It, too, is already used on some production sites and has most of its functionality working fine (though some adaptions might be needed for the lasted Search API changes). It's currently in Alpha state.
  • Preliminary ports also exist for both the Autocomplete and the Location module, with at least some functionality already working. The former of those will probably be my next focus, since it was one of the most popular extension modules in Drupal 7 (apart from the mostly-ported ones mentioned above). And the latter is set to be the focus of an upcoming Google Summer of Code project by dbjpanda, so we'll probably have a full-fledged Drupal 8 version of this by the end of August.
  • The Saved Searches module has not been ported yet. I plan to start porting it once the Autocomplete port has been finished and I get the time.

Credits

Once again, I have to state that this release wouldn't have been possible, and the development would have taken even longer, if it weren't for a lot of helping hands who did their part in moving this module port along. Chief among them are Acquia, who sponsored two months of my time a year ago to focus on this port, and Joris Vercammen (borisson_) who was an invaluable help just by discussing ideas and reviewing patches (and being probably the second-best informed regarding the Drupal 8 Search API module). Thanks to both of them, but also to everyone else who contributed – much too many to list them all!

[1] To be accurate, there are still two open but postponed ones, but those are mere clean-up issues that will be committed right before RC creation.

Image credit: bayasaa

Oct 06 2015
Oct 06

This past week, a new open database for economic, demographic, and market data was launched called Vizala. Vizala is a Drupal site and at the core of its user experience is a module called Views Save that allows users to personalize the site by saving views of data that are meaningful to them.

Views Save takes all of the functionality and flexibility provided by the Views module and its extensions, and enhances it by allowing users to save/bookmark any view of content. Basically, if Views can display it, then it can be saved with Views Save. It works with any configuration set by administrators or users including exposed filters, sorting, and any options added by extensions. Views Save works with all Views display types, plugins, handlers etc.

When a view is saved, it becomes an entity in Drupal. This allows you to use Views to generate lists of saved views per user. Views Bulk Operations (VBO) can be used to allow users to manage their lists. Saved views being stored as entities also allows you to take take advantage of a lot of other Drupal functionality including Fields and content permissions. Future development may involve Rules integration to allow alerts to be created that are based on saved views.

Example use cases for Views Save include …

  1. Sites that allow users to customize and save maps
  2. Analytics sites that allow you to save any view of the data, and create recurring reports
  3. When notifications are not required for a site, it could also be a replacement for the Search API Saved Searches module
Aug 11 2014
Aug 11

The greatest thing about all the refactoring in Drupal 8 is that, in general, a lot of those special Drupalisms used nowhere else were thrown out and replaced by sound design patterns, industry best practices and concepts that newcomers from other branches of programming will have an easy time of recognizing and using. While I can understand that this is an annoyance for some who have got used to the Drupalisms (and who haven't got a formal education in programming), as someone with a CS degree and a background in Java I was overjoyed at almost anything new I learned about Drupal 8, which, in my opinion, just made Drupal so much cleaner.
But, of course, this has already been discussed in a lot of other blog posts, podcasts, sessions, etc., by a lot of other people.

What I want to discuss today is one of the few instances where it seems this principle was violated and a new Drupalism, not known anywhere else (as far as I can tell, at least – if I'm mistaken I'd be grateful to be educated in the comments), introduced: plugin derivatives.
Probably some of you have already seen it there somewhere, especially if you were foolish enough to try to understand the new block system (if you succeeded, I salute you!), but I bet (or, hope) most of you had the same reaction as me: a very puzzled look and an involuntary “What the …?” In my case, this question was all the more pressing because I first stumbled upon plugin derivatives in my own module – Frédéric Hennequin had done a lot of the initial work of porting the module and since there was a place where they fit perfectly, he used them. Luckily, I came across this in Szeged where Bram Goffings was close by and could explain this to me slowly until it sank in. (Looking at the handbook documentation now, it actually looks quite good, but I remember that, back then, I had no idea what they were talking about.)
So, without (even) further ado, let me now share this arcane knowledge with you!

What, and why, are plugin derivatives?

The problem

Plugin derivatives, even though very Drupalistic (?), are actually a rather elegant solution for an interesting (and pressing) problem: dynamically defining plugins.
For example, take Search API's "datasource" plugins. These provide item types that can be indexed by the Search API, a further abstraction from the "entity" concept to be able to handle non-entities (or, indeed, even non-Drupal content). We of course want to provide an item type for each entity type, but we don't know beforehand which entity types there will be on a site – also, since entities can be accessed with a common API we can use the same code for all entity types and don't want a new class for each.
In Drupal 7, this was trivial to do:

/**
 * Implements hook_search_api_item_type_info().
 */
function search_api_search_api_item_type_info() {
 
$types = array();
  foreach (
entity_get_property_info() as $type => $property_info) {
    if (
$info = entity_get_info($type)) {
     
$types[$type] = array(
       
'name' => $info['label'],
       
'datasource controller' => 'SearchApiEntityDataSourceController',
       
'entity_type' => $type,
      );
    }
  }
  return
$types;
}
?>

Since plugin definition happens in a hook, we can just loop over all entity types, set the same controller class for each, and put an additional entity_type key into the definition so the controller knows which entity type it should use.

Now, in Drupal 8, there's a problem: as discussed in the previous part of this series, plugins now generally use annotations on the plugin class for the definition. That, in turn, would mean that a single class can only represent a single plugin, and since you can't (or at least really, really shouldn't) dynamically define classes there's also not really any way to dynamically define plugins.
One possible workaround would be to just use the alter hook which comes with nearly any plugin type and dynamically add the desired plugins there – however, that's not really ideal as a general solution for the problem, especially since it also occurs in core in several places. (The clearest example here are probably menu blocks – for each menu, you want one block plugin defined.)

The solution

So, as you might have guessed, the solution to this problem was the introduction of the concept of derivatives. Basically, every time you define a new plugin of any type (as long as the manager inherits from DefaultPluginManager you can add a deriver key to its definition, referencing a class. This deriver class will then automatically be called when the plugin system looks for plugins of that type and allows the deriver to multiply the plugin's definition, adding or altering any definition keys as appropriate. It is, essentially, another layer of altering that is specific to one plugin, serves a specific purpose (i.e., multiplying that plugin's definition) and occurs before the general alter hook is invoked.

Hopefully, an example will make this clearer. Let's see how we used this system in the Search API to solve the above problem with datasources.

How to use derivatives

So, how do we define several datasource plugins with a single class? Once you understand how it works (or what it's supposed to do) it's thankfully pretty easy to do. We first create our plugin like normally (or, just copy it from Drupal 7 and fix class name and namespace), but add the deriver key and internally assume that the plugin definition has an additional entity_type key which will tell us which entity type this specific datasource plugin should work with.

So, we put the following into src/Plugin/SearchApi/Datasource/ContentEntityDatasource.php:

namespace Drupal\search_api\Plugin\SearchApi\Datasource;

/**
 * @SearchApiDatasource(
 *   id = "entity",
 *   deriver = "Drupal\search_api\Plugin\SearchApi\Datasource\ContentEntityDatasourceDeriver"
 * )
 */
class ContentEntityDatasource extends DatasourcePluginBase {

  public function </span>loadMultiple(array $ids) {
   
// In the real code, this of course uses dependency injection, not a global function.
   
return entity_load_multiple($this->pluginDefinition['entity_type'], $ids);
  }

  </span>// Plus a lot of other methods …

}
?>

Note that, even though we can skip even required keys in the definition (like label here), we still have to set an id. This is called the "plugin base ID" and will be used as a prefix to all IDs of the derivative plugin definitions, as we'll see in a bit.
The deriver key is of course the main thing here. The namespace and name are arbitrary (the standard is to use the same namespace as the plugin itself, but append "Deriver" to the class name), the class just needs to implement the DeriverInterface – nothing else is needed. There is also ContainerDeriverInterface, a sub-interface for when you want dependency injection for creating the deriver, and an abstract base class, DeriverBase, which isn't very useful though, since the interface only has two methods. Concretely, the two methods are: getDerivativeDefinitions(), for getting all derivative definitions, and getDerivativeDefinition() for getting a single one – the latter usually simply a two-liner using the former.

Therefore, this is what src/Plugin/SearchApi/Datasource/ContentEntityDatasourceDeriver.php looks like:

namespace Drupal\search_api\Plugin\SearchApi\Datasource;

class </span>ContentEntityDatasourceDeriver implements DeriverInterface {

  public function </span>getDerivativeDefinition($derivative_id, $base_plugin_definition) {
   
$derivatives = $this->getDerivativeDefinitions($base_plugin_definition);
    return isset(
$derivatives[$derivative_id]) ? $derivatives[$derivative_id] : NULL;
  }

  public function </span>getDerivativeDefinitions($base_plugin_definition) {
   
$base_plugin_id = $base_plugin_definition['id'];
   
$plugin_derivatives = array();
    foreach (\
Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type_definition) {
      if (
$entity_type_definition instanceof ContentEntityType) {
       
$label = $entity_type_definition->getLabel();
       
$plugin_derivatives[$entity_type_id] = array(
         
'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $entity_type_id,
         
'label' => $label,
         
'description' => $this->t('Provides %entity_type entities for indexing and searching.', array('%entity_type' => $label)),
         
'entity_type' => $entity_type_id,
        ) +
$base_plugin_definition;
      }
    }
    return
$plugin_derivatives;
  }

}
?>

As you see, getDerivativeDefinitions() just returns an array with derivative plugin definitions – keyed by what's called their "derivative ID" and their id key set to a combination of base ID and derivative ID, separated by PluginBase::DERIVATIVE_SEPARATOR (which is simply a colon (":")). We additionally set the entity_type key for all definitions (as we used in the plugin) and also set the other definition keys (as defined in the annotation) accordingly.

And that's it! If your plugin type implements DerivativeInspectionInterface (which the normal PluginBase class does), you also have handy methods for finding out a plugin's base ID and derivative ID (if any). But usually the code using the plugins doesn't need to be aware of derivatives and can simply handle them like any other plugin. Just be aware that this leads to plugin IDs now all potentially containing colons, and not only the usual "alphanumerics plus underscores" ID characters.

A side note about nomenclature

This is a bit confusing actually, especially as older documentation remains unupdated: The new individual plugins that were derived from the base defintion are referred to as "derivative plugin definitions", "plugin derivatives" or just "derivatives". Confusingly, though, the class creating the derivatives was also called a "derivative class" (and the key in the plugin definition was, consequently, derivative).
In #1875996: Reconsider naming conventions for derivative classes, this discrepancy was discussed and eventually resolved by renaming the classes creating derivative definitions (along with their interfaces, etc.) to "derivers".
If you are reading documentation that is more than a few months old, hopefully this will prevent you from some confusion.

Other posts in this series

Image credit: DonkeyHotey

Aug 01 2014
Aug 01

The new plugin system is another large and important change in Drupal 8. While you were pretty much on your own if you wanted to provide some kind of plugin system in Drupal 7 (which a lot of modules do – think Views, Rules, etc.) there is now a very sophisticated framework to easily implement plugins and to define your own plugin types in a clean, extensible and very flexible way. And the good news for everyone familiar (code-wise) with the Search API in Drupal 7: the system is very much in line with how Search API implemented plugins itself, so the switch is pretty easy for us.

What is a plugin?

There have already been a lot of great blog posts about the plugin system in Drupal 8, so I won't go into much detail here. Generally speaking, a plugin is a self-contained unit providing a certain, well-defined functionality. A plugin type is defined by a single, central system (which is usually also the only place where the plugins are used), but other modules can then provide their own plugins with an individual implementation of that functionality.

Probably not a very helpful explanation, it's not that easy to explain – but, to use an example, blocks are now plugins in Drupal 8. The Block module provides the "Block" plugin type, which defines how blocks can be displayed on a page and how they can be configured. The Block module also provides all the code utilizing those plugins, letting the user place blocks on the site, ensuring they are displayed when and where appropriate, etc. Other modules then provide implementations of that "Block" plugin type – i.e., individual plugins – that implement different kinds of blocks: the User module provides a block with the login form, the Language module the "Language switcher" block, the Book module a block displaying the current book's navigation, etc.
But there are literally dozens of other types of plugins already included in Drupal core, and contrib is bound to add countless more – four (currently) by the Search API, for example. Entity types, the various Views handlers (fields, filters, sorts, …) and plugins (display, row, …), field types, WYSIWYG editors – all of those are now just different types of plugins, all using one new, large, unified framework to provide their functionality.

Ingredients for a new plugin type

So, what do we need to add our own plugin type to that growing list? Well, basically just a plugin manager, which (as explained last time) is a special type of service – that's it. Your new plugin type becomes available by just implementing a single method. Of course, to make your plugin type more useful and easier to use for others, you should add a few more things: an interface describing what plugins of that type should be able to do, an abstract base class for implementing that interface (unless it doesn't make sense in your case) and (optionally) a dedicated annotation for your plugin type.

Let's go through those with the example of Search API processors.

The plugin manager

As mentioned earlier, the plugin manager is simply a special kind of service. There is a base class (DefaultPluginManager) which already takes care of 95% of what's needed, you literally only need to override the constructor. So, we save the following in search_api/src/Processor/ProcessorPluginManager.php:

class ProcessorPluginManager extends DefaultPluginManager {

  public function </span>__construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
   
parent::__construct('Plugin/search_api/processor', $namespaces, $module_handler, 'Drupal\search_api\Annotation\SearchApiProcessor');
   
$this->setCacheBackend($cache_backend, 'search_api_processors');
   
$this->alterInfo('search_api_processor_info');
  }

}
?>

(To save space, I skip the namespace, imports and comments for all examples.)
This can be used as kind of a template, the base class will then take care of everything that's necessary to make the plugin system work. Here's what each of the lines do:

  • The __construct() method signature contains the service's dependencies, which will be injected when the plugin manager is constructed. $namespaces basically contains all root folders for sub-namespaces – i.e., every module's src/ and/or lib/ directory, plus a few more. The others are the services that handle caching and module interaction (e.g., invoking hooks).
  • The parent constructor sets the plugin manager up for this specific plugin type. The two variables are of course just passed on as-is, the other two parameters represent the following:
    • Plugin/search_api/processor is the sub-dir (and therefore sub-namespace) in which plugins of this type have to be located for automatic detection. That means, e.g., if the Views module would want to provide a Search API processor, it would have to be in namespace \Drupal\views\Plugin\search_api\processor, otherwise it wouldn't be found.
      The general standard for this parameter is either Plugin/[module]/[component] (like here) or Plugin/[ModuleComponent] (so, Plugin/SearchApiProcessor in this case). I think the former is generally preferred if your module defines more than one plugin type, while the latter can be cleaner/more practical if your module only defines a single one – I'm not completely sure myself, though. They probably shouldn't be mixed in a single module, in any case.
      (As a side note, I cheated here a bit, we currently use the sub-dir Plugin/SearchApi/Processor for processors. As I'm pretty sure that's wrong, though, we'll change that shortly.)
    • \Drupal\search_api\Annotation\SearchApiProcessor is the fully qualified class name of the annotation that will be used for defining plugins of your type. (See here if you have no idea what I'm talking about.) You can just skip this parameter, in which case it will default to \Drupal\Component\Annotation\Plugin – i.e., the generic @Plugin annotation. Core's validation constraints, e.g., use this instead of an individual annotation. Don't ask me what the decision process is here, but I'm pretty sure it's generally encouraged to use a dedicated annotation for your plugin types.
  • The second line just sets up caching for the plugin type, to avoid having to re-discover the available plugins each time they are required. search_api_processors here is the cache key that will be used to store the cached entries. You can also pass an array of cache tags as an optional third parameter to setCacheBackend(), which can be useful in special scenarios (i.e., when the cached plugin data should always be cleared alongside other cached data) but usually shouldn't be done.
  • The last line just determines that modules can alter the available processor definitions using hook_search_api_processor_info_alter(array &$definitions). Make sure to also document that hook in your module's MODULE.api.php file!

And that's already it for the class, all very straight-forward. Now, the only thing that's missing for the service is its entry in the search_api.services.yml file:

services:
  plugin.manager.search_api.processor:
    class: Drupal\search_api\Processor\ProcessorPluginManager
    parent: default_plugin_manager

The service ID here is generally plugin.manager.[module].[component]. Then we simply specify the class we just created and use the handy parent shortcut to copy over the same constructor dependencies that (usually) all plugin managers have. This is no higher magic but just references (and partly copies over) the following definition from core.services.yml:

services:
  default_plugin_manager:
    abstract: true
    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']

You'll recognize the arguments as exactly those we used in our plugin manager's constructor.

The @SearchApiProcessor annotation

Since we wanted a dedicated annotation instead of the generic @Plugin, we still have to create the SearchApiProcessor annotation class to get the plugin type basically working. This is also very easy, we just put the following into search_api/src/Annotation/SearchApiProcessor.php:

class SearchApiProcessor extends Plugin {

  public </span>$id;

  public </span>$label;

  public </span>$description;

}
?>

That's all – you just define your plugin annotation's properties and inherit from Plugin, that base class will take care of everything else. The properties aren't even necessary, they are just there to document for other developers what properties the plugin type expects in plugin definitions. (And, though not used in this case, to set defaults for optional properties.) The base class doesn't care about that, it will just save all the properties it encounters in the definition, regardless of whether they are defined or not. (Note, though, that the id property is "magic" – if you want to use that for anything other than the plugin ID (and have the plugin ID in a different property), then you'll have to override getId() in your annotation class.)
The only thing you do have to take care of is that annotation autloading will only work if you put the annotation into the \Drupal\[module]\Annotation namespace.

The plugin interface and base class

With the above done, you already have a fully functional new plugin type: other modules can define their own Search API processors and we can easily get all defined processors with the following bit of code:

$processor_plugin_manager = \Drupal::service('plugin.manager.search_api.processor');
$processor_definitions = $processor_plugin_manager->getDefinitions();
foreach (
$processor_definitions as $processor_id => $processor_definition) {
 
$processors[$processor_id] = $processor_plugin_manager->createInstance($processor_id, $processor_settings[$processor_id]);
}
?>

However, while this is a complete working example as far as Drupal's plugin framework is concerned, it is of course not really practical since we don't specify anywhere what we expect from processor plugins, so modules that want to provide their own processors don't know which methods to provide and how they should behave (without combing the whole module code of the Search API for any calls to processor plugins). So, it is more or less required to also define an interface for plugins of your new type that implementing modules would have to use.

The specific methods in the interface of course differ from case to case, but there are a lot of interfaces (and corrsponding base classes or traits) provided in Drupal Core for functionality that is often required for plugins. E.g., PluginInspectionInterface lets you retrieve a plugin's ID and definition, DerivativeInspectionInterface helps dealing with plugin derivatives (look out for an upcoming blog post about those) and PluginBase is a great base class for plugins which implements both of these interfaces and additionally provides a few other handy methods for child classes (first and foremost, a t() method for doing dependency-injected translation). PluginFormInterface provides methods for plugins that should have a configuration form, usually used in conjunction with ConfigurablePluginInterface, which represents plugins with configuration. And ContainerFactoryPluginInterface, as the last one used in the Search API, provides a static create() method for easily implementing proper dependency injection. There are more, though, so take a look through the \Drupal\Component\Plugin and \Drupal\Core\Plugin namespaces before adding custom methods to your plugin interface.

The whole interface then looks like this (conceptually):

interface ProcessorInterface extends PluginInspectionInterface, DerivativeInspectionInterface, DrupalConfigurablePluginInterface, PluginFormInterface, ContainerFactoryPluginInterface {

  </span>// Put plugin type-specific methods here.

}
?>

This interface is usually put into the same directory (and, therefore, namespace) as the plugin manager (since there is nothing else that really links the plugin manager to the interface), as is a default base class for the plugin type that implements the interface and helps modules avoid boilerplate code when providing their own plugins:

abstract class ProcessorPluginBase extends PluginBase implements ProcessorInterface {

  </span>// Here, provide default implementations for all methods of the interface (for which it makes sense).

}
?>

And that's all, now you've completely defined a new plugin type for your module. Modules can now provide plugins of that type like this:

/**
 * @SearchApiProcessor(
 *   id = "example_some_processor",
 *   label = @Translation("Some processor"),
 *   description = @Translation("Description of the processor.")
 * )
 */
class SomeProcessor extends ProcessorPluginBase {

  </span>// Plugin code.

}
?>

Other posts in this series

Image credit: findicons.com

Jul 24 2014
Jul 24

Even though there was somewhat of a delay since my last post in this series, it seems no-one else has really covered any of the advanced use cases of Drupal 8 in tutorials yet. So, here is the next installment in my series. I initially wanted to cover creating a new plugin type, but since that already requires creating a new servive, I thought I'd cover that smaller part first and then move on to plugin types in the next post.
I realize that now already a lot more people have started on their Drupal 8 modules, but perhaps this will make this series all the more useful.

Services in Drupal 8

First, a short overview of what a service even is. Basically it is a component (represented as a class) providing a certain, limited range of functionality. The database is a service, the entity manager (which is what you now use for loading entities) is a service, translation, configuration – everything handled by services. Getting the current user – also a service now, ridding us of the highly unclean global variable.
In general, a lot of what was previously a file in includes/ containing some functions with a common prefix is now a service (or split into multiple services).

The upsides of this is that the implementation and logic is cleanly bundled and properly encapsulated, that all these components can easily be switched out by contrib or later core updates, and that these systems can also be very well tested with unit tests. Even more, since services can be used with dependency injection, it also makes it much easier to test all other classes that use any of these services (if they can use dependency injection and do it properly).

(For reference, here is the official documentation on services.)

Dependency injection

This has been covered already in a lot of other blog posts, probably since it is both a rather central concept in Drupal 8, and a bit complicated when you first encounter it. However, before using it, I should still at least skim over the topic. Feel free to skip to the next heading if you feel you already know what dependency injection is and how it roughly works in Drupal 8.

Dependency injection is a programming technique where a class with external dependencies (e.g., a mechanism for translating) explicitly defines these dependencies (in some form) and makes the class which constructs it responsible for supplying those dependencies. That way, the class itself can be self-contained and doesn't need to know about where it can get those dependencies, or use any global functions or anything to achieve that.

Consider for example the following class:

class ExampleClass {

  public function </span>getDefinition() {
    return array(
     
'label' => t('example class'),
     
'type' => 'foo',
    );
  }

}
?>

For translating the definition label, this explicitly uses the global t() function. Now, what's bad about this I, hear you ask, it worked well enough in Drupal 7, right?
The problem is that it becomes almost impossible to properly unit-test that method without bootstrapping Drupal to the point where the t() function becomes available and functional. It's also more or less impossible to switch out Drupal's translation mechanism without hacking core, since there is no way to redirect the call to t().

But if translation is done by a class with a defined interface (in other words, a service), it 's possible to do this much cleaner:

class ExampleClass {

  public function </span>__construct(TranslationServiceInterface $translation) {
   
$this->translation = $translation;
  }

  public function </span>getDefinition() {
    return array(
     
'label' => $this->translation->translate('example class'),
     
'type' => 'foo',
    );
  }

}
?>

Then our example class just has to make it easily possible for code that wants to instantiate it to know how to pass its dependencies to it. In Drupal, there are two ways to do this, depending on what you are creating:

  • Services, which themselves use dependency injection to get their dependencies (as you will see in a minute) have a definition in a YAML file that exactly states which services need to be passed to the service's constructor.
  • Almost anything else (I think) uses a static create() method which just receives a container of all available services and is then responsible for passing the correct ones to the constructor.

In either case, the idea is that subclasses/replacements of ExampleClass can easily use other dependencies without any changes being necessary to code elsewhere instantiating the class.

Creating a custom service

So, when would you want to create your own service in a module? Generally, the .module file should more or less only contain hook implementations now, any general helper functions for the module should live in classes (so they can be easily grouped by functionality, and the code can be lazy-loaded when needed). The decision to make that class into a service then depends on the following questions:

  • Is there any possibility someone would want to swap out the implementation of the class?
  • Do you want to unit-test the class?
  • Relatedly, do you want dependency injection in the class?

I'm not completely sure myself about how to make these decisions, though. We're still thinking about what should and shouldn't be a service in the Search API, currently there is (apart from the ones for plugins) only one service there:

The "Server task manager" service

The "server tasks" system, which already existed in D7, basically just ensures that when any operations on a server (e.g., removing or adding an index, deleting items, …) fails for some reason (e.g., Solr is temporarily unreachable) it is regularly retried to always ensure a consistent server state. While in D7 the system consisted of just a few functions, in D8 it was decided to encapsulate the functionality in a dedicated service, the "Server task manager".

Defining an interface and a class for the service

The first thing you need, so the service can be properly swapped out later, is an interface specifying exactly what the service should be able to do. This completely depends on your use case for the service, nothing to keep in mind here (and also no special namespace or anything). In our case, for server tasks:

namespace Drupal\search_api\Task;

interface </span>ServerTaskManagerInterface {

  public function </span>execute(ServerInterface $server = NULL);

  public function </span>add(ServerInterface $server, $type, IndexInterface $index = NULL, $data = NULL);

  public function </span>delete(array $ids = NULL, ServerInterface $server = NULL, $index = NULL);

}
?>

(Of course, proper PhpDocs are essential here, I just skipped them for brevity's sake.)

Then, just create a class implementing the interface. Again, namespace and everything else is completely up to you. In the Search API, we opted to put interface and class (they usually should be in the same namespace) into the namespace \Drupal\search_api\Task. See here for their complete code.
For this post, the only relevant part of the class code is the constructor (the rest just implements the interface's methods):

class ServerTaskManager implements ServerTaskManagerInterface {

  public function </span>__construct(Connection $database, EntityManagerInterface $entity_manager) {
   
$this->database = $database;
   
$this->entity_manager = $entity_manager;
  }

}
?>

As you can see, we require the database connection and the entity manager as dependencies, and just included them in the constructor. We then save them to properties to be able to use them later in the other methods.

Now we just need to tell Drupal about our service and its dependencies.

The services.yml file

As mentioned earlier, services need a YAML definition to work, where they also specify their dependencies. For this, each module can have a MODULE.services.yml file listing services it wants to publish.

In our case, search_api.services.yml looks like this (with the plugin services removed):

services:
  search_api.server_task_manager:
    class: Drupal\search_api\Task\ServerTaskManager
    arguments: ['@database', '@entity.manager']

As you see, it's pretty simple: we assign some ID for the service (search_api.server_task_manager – properly namespaced by having the module name as the first part), specify which class the service uses by default (which, like the other definition keys, can then be altered by other modules) and specify the arguments for its constructor (i.e., its dependencies). database and entity.manager in this example are just IDs of other services defined elsewhere (in Drupal core's core.services.yml, in this case).

There are more definition keys available here, and also more features that services support, but that's more or less the gist of it. Once you have its definition in the MODULE.services.yml file, you are ready to use your new service.

Using a service

You already know one way of using a service: you can specify it as an argument for another service (or any other dependency injection-enabled component). But what if you want to use it in a hook, or any other place where dependency injection is not available (like entities, annoyingly)?

You simply do this:

/** @var \Drupal\search_api\Task\ServerTaskManagerInterface $server_task_manager */
$server_task_manager = \Drupal::service('search_api.server_task_manager');
$server_task_manager->execute();
?>

That's it, now all our code needing server tasks functionality benefits from dependency injection and all the other Drupal 8 service goodness.

Other posts in this series

Image credit: Didi

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web