Mar 31 2016
Mar 31

In the previous article of this series we’ve started our dive into the Entity Validation and Typed Data APIs. We’ve seen how DataType plugins interact with data definitions and how various constraints can be added to the latter at multiple levels and extension points.

Drupal 8 logo

In this part, we will cover the aspect of actual validation and violation handling. In addition, we will write our own constraint and validator so that we can use custom behaviors in the data validation process.

Validation and Violation Handling

Even though we don’t yet know exactly how constraints are built, we’ve seen how they can be added to Typed Data definitions, including entity fields. Let us now see how we can validate the entities and handle possible violations we find.

When talking about Typed Data we’ve already seen how the validate() method can be called on the DataType plugin instance which holds a data definition. When it comes to entities, this can happen both at entity and field levels.

For instance, we can validate the entire entity using the validate() method:

$entity->set('title', 'this is too long of a title');
$violations = $entity->validate();

In our previous article, we added the Length constraint to Node titles to prevent title strings longer than 5 characters. If that is still in place and we run the code above, the validation should obviously fail. The $violations object is now, however, an EntityConstraintViolationListInterface instance which provides some helper methods for accessing violation data specific to Drupal content entities. It’s worth looking into that interface for all the helper methods available.

To get a list of Entity level violations we can use the getEntityViolations() method but we can also loop through all of them. Once we have our individual ConstraintViolationInterface instances, we can inspect them for what went wrong. For instance, we can get the error message with getMessage(), the property path that failed with getPropertyPath() and the invalid value with getInvalidValue(), among other useful things.

When it comes to fields, the property path is in the following format: title.0.value. This includes the field name, the key (delta) of the individual field item in the list and the actual property name. This represents the property path of our violation above.

Apart from calling validation on the entire entity (which may be superfluous at times), we can also do so directly on each field:

$entity->set('title', 'this is too long of a title');
$violations = $entity->get('title')->validate();

In this case, $violations is again an instance of ConstraintViolationListInterface and can be looped over to inspect each violation. This time, though, the property path changes to no longer include the field name: 0.value.

And lastly, we can even validate the individual items in the list:

$violations = $entity->get('title')->get(0)->validate();

As we can expect, the difference now is that the property path will only show value in the violation since we know exactly what we are validating: the first data definition in the list.

Constraints and Validators

We only touched upon the aspect of constraints and validators but let us better understand how they work by creating one ourselves. We will create one single constraint and validator but that can be used for both Node entities and their fields. It’s always better to have constraints targeted to the data definition we want but for the sake of brevity, we’ll do it all in one constraint to see how all these options could be handled.

The business case of our example is to have a constraint that we can apply to any string-based content entity field that would force the string to contain a certain alphanumeric code. The latter will be passed on as an option so it can be reused. If it’s applied to a Node entity, we want to make sure the title of the node contains the code. So let’s get started.

First, inside our demo module, which can be found in this git repository, we need to create the Validation folder inside the Plugin folder of our namespace (the src/ directory). Inside that, we need the Constraint folder. This is because constraints are plugins expected to be defined in there.

Second, inside Constraint/, we can create our constraint class:

<?php



namespace Drupal\demo\Plugin\Validation\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\MissingOptionsException;



class HasCodeConstraint extends Constraint {

  
  public $messageNoCode = 'The string <em>%string</em> does not contain the necessary code: %code.';

  
  public $code;

  
  public function __construct($options = NULL) {
    if ($options !== NULL && is_string($options)) {
      parent::__construct(['code' => $options]);
    }

    if ($options !== NULL && is_array($options) && isset($options['code'])) {
      parent::__construct($options);
    }

    if ($this->code === NULL) {
      throw new MissingOptionsException('The code option is required', __CLASS__);
    }
  }
}

As we mentioned earlier, the constraint plugin class is relatively simple. It has the expected annotation which, among boilerplate metadata, also specifies what type of data this constraint can be applied to. We chose both a simple string and the Node entity type. We then declare two public properties: a message to be used when the constraint fails (with placeholders being populated inside the validator) and the actual code to be checked for (populated by the constructor of the parent Constraint class). Inside our constructor, we check if the options passed are a string or array (just to be a bit flexible) and throw an exception if the code parameter is not passed as an option in any shape or form.

One of the tasks of the parent Constraint class is to specify which class will be used to validate this constraint. By default, it is a class named like the constraint itself but with Validate appended at the end (HasCodeConstraintValidator). If we wanted to create a differently named and/or namespaced class, we would have to override the validatedBy() method and return the fully qualified name of the class we want.

Let’s now see the HasCodeConstraintValidator class since for us the default is fine:

<?php



namespace Drupal\demo\Plugin\Validation\Constraint;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;


class HasCodeConstraintValidator extends ConstraintValidator {

  
  protected $context;

  
  public function validate($data, Constraint $constraint) {
    if (!$constraint instanceof HasCodeConstraint) {
      throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\HasCodeConstraint');
    }

       if ($data instanceof NodeInterface) {
      $this->validateNodeEntity($data, $constraint);
      return;
    }

       if ($data instanceof FieldItemListInterface) {
      foreach ($data as $key => $item) {
        $violation = $this->validateString($item->value, $constraint);
        if ($violation instanceof ConstraintViolationBuilderInterface) {
          $violation->atPath('title')->addViolation();
        }
      }
      return;
    }

       if (is_string($data)) {
      $violation = $this->validateString($data, $constraint);
      if ($violation instanceof ConstraintViolationBuilderInterface) {
        $violation->addViolation();
        return;
      }
    }
  }

  
  protected function validateNodeEntity($node, $constraint) {
    foreach ($node->title as $item) {
      $violation = $this->validateString($item->value, $constraint);
      if ($violation instanceof ConstraintViolationBuilderInterface) {
        $violation->atPath('title')
          ->addViolation();
      }
    }
  }

  
  protected function validateString($string, $constraint) {
    if (strpos($string, $constraint->code) === FALSE) {
      return $this->context->buildViolation($constraint->messageNoCode, array('%string' => $string, '%code' => $constraint->code));
    }
  }

}

The main job of this class is to implement the validate() method and build violations onto the current execution context if the data passed to it is somehow invalid. The latter can be more than one type of data, depending on what the constraint is applied to.

In our example, we use the constraint for entities, field item lists and primitive data as well. This is just to save us some space. But the logic is that if a Node entity is passed, the code is checked in its title while for the the field items an iteration is performed to check the values of the fields. And of course, the constraint can also be added to individual field items in which case the $data variable would be the value of the data definition.

The $context property has a handy buildViolation() method which allows us to specify a number of things related to what actually failed (path, message, etc).

So if we want to make use of this constraint, we can apply what we learned earlier and do one of the following 3 things:

Inside hook_entity_type_alter():

$node = $entity_types['node'];
$node->addConstraint('HasCode', ['code' => 'UPK']);

This adds the constraint to the Node entities so all node titles now have to contain the code UPK.

Inside hook_entity_base_field_info_alter() or hook_entity_bundle_field_info_alter(), one of the following two lines:

$title->addConstraint('HasCode', ['code' => 'UPK']);
$title->addPropertyConstraints('value', ['HasCode' => ['code' => 'UPK']]);

Where $title is a field definition and the first case adds the constraint to the entire list of items while the latter adds it straight to the individual item.

These three possibilities cover the three cases the constraint validator handles in our example. And that is pretty much it. Not too difficult.

In this article, we’ve continued our dive into the Entity Validation API by looking at two things: validation and violation handling, and the creation of custom constraints. We’ve seen that once applied onto entities or field data definitions, constraints can be very easily validated and inspected for violations. This API borrows a lot from Symfony and makes it a breeze to decouple validation from the Form API.

Also easy, as we’ve seen, is to extend the existing pool of validation criteria available. This is done via constraint plugins, all coupled with their own validators and which can be written in very reusable ways. It’s very interesting to play around with these and see how all the pieces interact. And it is also a great learning experience.

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Mar 29 2016
Mar 29

Data validation is a very important part of any application. Drupal 7 has a great Form API that can handle complex validation of submitted data, which can then be turned into entities. However, form level validation is problematic. For example, it becomes difficult to handle entity (data) validation programmatically. We have to either reimplement validation logic or mimic form submissions in code. This makes any data interaction dependent on the form system and this is a bad idea.

Drupal 8 logo

With the introduction of subsystems such as the REST API, Drupal 8 needed something better to handle this problem. The Symfony Validation component was chosen and Drupal 8 builds on top of it to tailor for the realties of the Typed Data and plugin based Entity system. So now, form submissions validate entities just like REST calls do or any other programmatic interaction with the entities easily can.

In this article, and its followup, we will explore the Drupal 8 Entity Validation API, see how it works, and how it can be extended. To better understand this, we will also take a look at the Typed Data API which underpins the entity system in Drupal 8. There will be some code examples that already exist in core but we will also write a bit of our own code which can be found in this git repository within the demo module.

Typed Data

Typed Data is the Drupal 8 API that was built to provide a consistent way of interacting with data or metadata about the data itself. Why is this important for our topic? Because validation is defined and invoked on typed data objects.

Two important components of this API stand out: the data definition and the DataType plugins. The role of the former is to define data and how interaction works with it (including things like settings or validation constraints). The role of the latter is to provide a way to get and set values from that type of data. When they are instantiated, data type plugins make use of data definition instances passed on by the plugin manager. The latter can also infer which data definition needs to be used by a DataType plugin type.

Let’s see an example:

$definition = DataDefinition::create('string')
    ->addConstraint('Length', array('max' => 20));

We created a string data definition and applied the Length constraint to it. Constraints are a key part of the validation API and they define the type of validation that will run on the data. They are coupled with a validator that actually performs the task, but we will see more about constraints and validators in the second part of this series.

Next, we use the DataType plugin manager to create the actual data type plugin instance:

$string_typed_data = \Drupal::typedDataManager()->create($definition, 'my string');

We loaded the manager service statically here for brevity but you should use dependency injection in your project if you are in a class context. The create() method on the TypedDataManager takes the data definition as the first parameter, the actual value as its second and returns a DataType plugin instance of the type that matches the definition: in our case StringData. For complex data, DataType plugins can specify in their annotations which data definition class they need to use. And for more information about plugins in Drupal 8, make sure you check out my previous articles on the topic.

One of the methods on this plugin instance is validate(), which triggers data validation against all the constraints that have been applied to the definition and returns an instance of Symfony’s ConstraintViolationList. By iterating over it or using methods like count() or get() we can check if the data is valid and which constraints have failed if not.

In our example, the violation list should have no validation errors because the string we used is under 20 characters. Had we used a longer string when creating the plugin, we would have had one violation represented by a Symfony ConstraintViolationInterface instance with a message, offending value and even a property path.

Typed Data and Content Entities

Now that we know a bit about the Typed Data API, let’s see how this applies to content entities.

Entity data in Drupal 7 is split between entity properties (usually the columns on the entity table) and Field API fields that are configured through the UI. In Drupal 8, they have been brought under the same umbrella so the old properties become fields as well. However, a difference still remains in that some fields (mainly the old entity properties) are defined as base fields while the rest are configurable fields.

The data definitions for these fields are BaseFieldDefinition and FieldConfig, but they are both implementors of the same FieldDefinitionInterface (which is a complex extender of the DataDefinitionInterface – the interface directly implemented by DataDefinition we saw earlier).

Each individual field holds data in a special FieldItemListInterface implementation and is always a list of individual FieldItem plugins (even if there is only one real value in the field). Each of these plugins extends a DataType plugin and uses a type of DataDefinitionInterface implementation itself (usually FieldItemDataDefinition). Things are quite complex at this level so it’s well worth taking a closer look at the makeup of entity fields to better understand this architecture.

Adding Entity and Field Constraints

Constraints in Drupal 8 are also plugins which usually hold a small amount of information about how data is actually being validated, what error message should be used in case of failure and any additional options the validator needs. The validator class (which is referenced by the constraint) is responsible for checking the data. We’ve seen one example, Length, which is in fact the LengthConstraint class validated directly by Symfony’s LengthValidator class. We will see in the second part how to create our own constraint and validator. For now, though, let’s see how we can add existing constraints to content entities and fields.

Entity level constraints

Entity level constraints are added in the annotation of the entity class itself. For example, this is how the Comment entity has defined a constraint for its name/author fields combination:

...
  constraints = {
    "CommentName" = {}
  }
...

In this example, CommentName is the plugin ID of the constraint that will be validated against when saving a comment entity. The opening and closing braces means that the plugin takes no options.

If we wanted to add to or remove a constraint from an existing entity, we’d have to implement hook_entity_type_alter():

function demo_entity_type_alter(array &$entity_types) {
  
  $node = $entity_types['node'];
  $node->addConstraint('ConstraintPluginName', ['array', 'of', 'options']);
}

In this example, we are adding a fictitious constraint to the Node entity and passing an array of options to it.

Field level constraints

There is more than one way to add field level constraints depending on whether the content entity type is defined in our module and whether the type of field we are talking about is a base field or configurable.

If we are defining our own entity type, one of the methods on the actual entity class that we have to implement is baseFieldDefinitions(). This is where we return an array of BaseFieldDefinition field definitions and we can easily add our constraints there. For example, this is how the Node ID base field is being defined:

$fields['nid'] = BaseFieldDefinition::create('integer')
  ->setLabel(t('Node ID'))
  ->setDescription(t('The node ID.'))
  ->setReadOnly(TRUE)
  ->setSetting('unsigned', TRUE);

Similarly to how we added constraints to the DataDefinition instance earlier, the Node entity could also add constraints to the Node ID field, more specifically:

  • to the BaseFieldDefiniton itself, which is, as we saw, the definition for the FieldItemListInterface implementation (the list)
  • to the individual FieldItemDataDefinition items, which are, as we saw, a type of complex data definition for the FieldItemInterface implementations (the items)

If we want to add a constraint to a Node base field (or of any other content entity type not defined by our module), we have to implement hook_entity_base_field_info_alter() and add our constraint there:

function demo_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'node') {
    
    $title = $fields['title'];
    $title->addPropertyConstraints('value', ['Length' => ['max' => 5]]);
  }
}

In the example above, we are adding a constraint to the Node title field to make sure that no title can be longer than 5 characters. To notice is that we are using the addPropertyConstraint() method instead of the addConstraint() one we saw earlier. This is because we are not targeting the definition for the list of items but the individual item definitions themselves (FieldItemDataDefinition).

By using the addConstraint() method, we are adding a constraint to the entire list of items. This means that when the constraint gets validated, the validator receives the entire list not just the value of the individual item.

If we want to add a constraint to a configurable field, the process is quite similar. The difference is that we need to implement hook_entity_bundle_field_info_alter() and work with FieldConfig instances instead of BaseFieldDefinition.

If we want to inspect the constraints that are already set on the field, we can do something like this:

$title = $fields['title'];
$constraints = $title->getConstraints();
$property_constraints = $title->getItemDefinition()->getConstraints();

In this example, $title is either an instance of BaseFieldDefinition or FieldConfig. The $constraints array is, as expected, a list of constraints applied to the entire list of field items while the $property_constraints is an array of constraints applied to the individual field items themselves. We can notice, though, that if we run this code after we’ve applied the Length constraint, we will find inside $property_constraints a ComplexData constraint which wraps over the individual constraints applied to the field values. This is because there can be multiple individual data definitions for a single field item and Drupal by default groups them like so.

Conclusion

In this article, we’ve started looking at the Entity Validation API in Drupal 8. To this end, we also had to get a sense of the Typed Data API which is used as a foundation for the entity system. Once that became a bit clearer, we’ve seen how constraints can be added to various types of data definitions, including those used by entities and fields.

In the next part, we will look at how the actual validation works and how handling the violations that may occur should be done. Additionally, we will create our own constraint and validator and apply our knowledge from this part to various data definition types. Fun!

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Mar 23 2016
Mar 23

Drupal 8 logo

Views is in Drupal 8 core. We all know that by now. Twig is the new templating engine in Drupal 8. This we also know. But do we know how to interact programmatically with the first in order to theme a View using the second? Aside from overriding View templates like with any other subsystem, we have available a more powerful alternative in the form of Views plugins (Display, Style, Row and Field).

In this article, we are going to look at how we can create a custom Style plugin for Views in Drupal 8. We will use the Bootstrap tab markup as a goal and implement a tabbed output for our View results. In the View configuration, the Style settings will allow us to specify which field will be used as the tab navigation copy, leaving the rest of the fields shown in the respective tab panes. Basically, each View result will represent a tab – so this example is not suited for Views which have more than a few results. The main goal is to illustrate how we can create our own Views Style plugins in Drupal 8.

We will not cover the details on how you can use Bootstrap in your project. However, you can check out the documentation page on assets or even this article on how to make sure anonymous users can benefit from jQuery being loaded on the page. And if you want to see the code we write ahead of time, you can find it in this repository within the Demo module.

What Is the Style Plugin?

The Views Style plugin is the one responsible for rendering the listing. Notable examples of core Style plugins are Unformatted List, HTML List, Table or Grid. They are used by the Display plugin and they in turn use Row plugins that represent one item in the listing.

In Drupal 8, all Views plugin types are built using the new Plugin system and share some common functionality (they always extend from the same Views PluginBase).

Let’s now create our own such Style plugin that can be used by most Display types (Page, Block, etc) and which uses the Field row plugin.

The Bootstrap Tabs Style Plugin

The first step is to create our plugin class located in the Plugin/views/style folder of our module:

namespace Drupal\demo\Plugin\views\style;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\style\StylePluginBase;


class BootstrapTabs extends StylePluginBase {

  
  protected $usesRowPlugin = TRUE;

  
  protected $usesGrouping = FALSE;

  
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['tab_nav_field'] = array('default' => '');
    return $options;
  }

  
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);
    $options = $this->displayHandler->getFieldLabels(TRUE);
    $form['tab_nav_field'] = array(
      '#title' => $this->t('The tab navigation field'),
      '#description' => $this->t('Select the field that will be used as the tab navigation. The rest of the fields will show up in the tab content.'),
      '#type' => 'select',
      '#default_value' => $this->options['tab_nav_field'],
      '#options' => $options,
    );
  }
}

The Drupal plugin type we are creating an instance of is ViewsStyle with some basic configuration passed in the annotation. Leaving aside the obvious ones, we have the theme and display_types keys that are worth mentioning. The first declares which theme function this Style plugin will use to render its data while the second declares which kinds of Display plugins this Style can be used by (in our case all Display types which don’t otherwise specify a custom type: normal). For more information on all the available annotation configuration for this plugin type, check out the Drupal\views\Annotation\ViewsStyle annotation class.

Using the two class properties, we declare that our Style uses row plugins but does not allow grouping. Make sure you check out the parent classes to learn more about what other options can be specified like this. For example, the class we are extending already declares that Views fields can be used with the Style plugin.

As mentioned before, using the two methods we create a plugin option and form element to be able to specify which field should act as the tab navigation. Using the current display handler ($this->displayHandler) we can load up all the available View fields the site builder has added to it. And this new form element will be available on the Style settings form:

Drupal 8 Style Plugins

Since we are extending from the StylePluginBase class, there is nothing more we need to do. For the markup output we can rely on the demo_bootstrap_tabs theme which receives the relevant variables from the executed View. If we want, we can override any of the render methods and add more variables, change the theme, or whatever we need. We are good with the defaults, especially since we will implement a preprocessor to handle the variables that the template receives.

The Theme

It’s time to define the demo_bootstrap_tabs theme as we normally do (inside our .module file):


function demo_theme($existing, $type, $theme, $path) {
  return array(
    'demo_bootstrap_tabs' => array(
      'variables' => array('view' => NULL, 'rows' => NULL),
      'path' => drupal_get_path('module', 'demo') . '/templates',
    ),
  );
}

The Style plugin passes the $view object and the resulting $rows by default to the template. It is up to the preprocessor to do a bit of handling of these variables (if needed) before they are sent to the template:


function template_preprocess_demo_bootstrap_tabs(&$variables) {
  $view = $variables['view'];
  $rows = $variables['rows'];
  $variables['nav'] = array();

   $field = $view->style_plugin->options['tab_nav_field'];
  if (!$field || !isset($view->field[$field])) {
    template_preprocess_views_view_unformatted($variables);
    return;
  }

  $nav = array();
  foreach ($rows as $id => $row) {
    $nav[$id] = array(
      '#theme' => 'views_view_field',
      '#view' => $view,
      '#field' => $view->field[$field],
      '#row' => $row['#row'],
    );
  }

  template_preprocess_views_view_unformatted($variables);
  $variables['nav'] = $nav;
}

So what’s happening here? First, we check the Style plugin options for the field name to be used (the one that was selected when configuring the View). If one is not there, we return, but not before doing a bit of default preprocessing that the template_preprocess_views_view_unformatted function already does well. So we delegate to it. Then, we loop through the Views results and build an array of content for our tab navigation. For this, we use the default Views views_view_field theme function to render the selected field. Finally, we pass this array to the template and also run the default preprocessor of the unformatted list style.

The Template

In Drupal 8 there are no more theme functions, everything is now handled in Twig templates. So let’s see how the demo-bootstrap-tabs.html.twig file looks like in our module’s templates folder:

<div>
    
    <ul class="nav nav-tabs" role="tablist">
        {% for tab in nav %}
            {% set active = '' %}
            {% if loop.index0 == 0 %}
                {% set active = 'active' %}
            {% endif %}
            <li role="presentation" class="{{ active }}"><a href="#tab-{{ loop.index0 }}" aria-controls="profile" role="tab" data-toggle="tab">{{ tab }}</a></li>
        {% endfor %}
    </ul>

    
    <div class="tab-content">
        {% for row in rows %}
            {% set active = '' %}
            {% if loop.index0 == 0 %}
                {% set active = 'active' %}
            {% endif %}
            <div role="tabpanel" class="tab-pane {{ active }}" id="tab-{{ loop.index0 }}">{{ row.content }}</div>
        {% endfor %}
    </div>
</div>

As you can see, this is the necessary markup for the Bootstrap tabs. It won’t work, of course, without making sure the relevant Bootstrap styles and script are loaded in your theme first.

The first thing we render are the tab navigation items (from our nav variable). While looping through this array, we also make use of the loop index value in order to default the first item as active and be able to target the tab content panes below using unique IDs. For the actual value of the items, we just print the render array we created in our preprocessor and Drupal takes care of rendering that. That being said, it is probably a good idea to make sure that the field you use here is relatively short, without a link and plain markup. Titles would probably work just fine. But this is a matter of configuring the View accordingly.

Below the navigation, we print the actual view rows, using the same loop index to default the first row as the active tab pane and identify them uniquely so the navigation above can control their visibility. As for the content, we print the entire row.content variable (which is prepared inside template_preprocess_views_view_unformatted) and which contains all the fields in our View. And if we want to not include the field we used for the navigation, we can just exclude that one from display in the View configuration. It will still appear in the navigation (because we explicitly print it there) but not in the main tab pane.

Conclusion

And there we have it. A Views Style plugin to output the View results as Bootstrap tabs. All we need now is to make sure the Bootstrap assets are loaded and simply configure our View to use the new Style plugin. Do keep in mind that this is not meant for Views with lots of results and it only serves as an example to demonstrate how to create Style plugins.

If you have questions, comments, or suggestions, please leave them below!

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Mar 09 2016
Mar 09

Migrate is one of the most established modules in the Drupal ecosystem. So much so that with Drupal 8, a decision has been made to get some of its functionality ported and added to Drupal core. An important reason was that the traditional upgrade between major releases was replaced with a migration of Drupal 6 or 7 content and configuration to Drupal 8.

Drupal 8 logo

Not all the functionality of the Migrate module has been moved into core though. Rather, we have the basic framework within the core migrate module and the functionality serving the upgrade path from Drupal 6 and 7 within the core migrate_drupal module. What’s left can be found in a host of contrib modules. The most important is Migrate Tools which contains, among other things, drush commands and the UI for running migrations. Additionally, the Migrate Source CSV, Migrate Source XML and Migrate Source JSON modules provide plugins for the most used types of migration sources.

In this article we are going to look at how migration works in Drupal 8 by migrating some content into node entities. For simplicity, the data we play with resides in tables in the same database as our Drupal installation. If you want to see the final code, you can check it out in this repository.

Drupal 8 Migration Theory

The structure of a migration in Drupal 8 is made up of three main parts: the source, the process and the destination, all three being built using the new Drupal 8 plugin system. These three parts form a pipeline. The source plugin represents the raw data we are migrating and is responsible for delivering individual rows of it. This data is passed to one or more process plugins that perform any needed manipulation on each row field. Finally, once the process plugins finish preparing the data, the destination plugins save it into Drupal entities (either content or configuration).

Creating a migration involves using such plugins (by either extending or directly using core classes). All of these are then brought together into a special migration configuration entity. This is shipped as module config that gets imported when the module is first enabled or can be constructed using a template (a case we won’t be exploring today). The migration configuration entity provides references to the main plugins used + additional metadata about the migration.

Movie Migration

Let us now get down to it and write a couple of migrations. Our data model is the following: we have two tables in our database: movies and movies_genres. The former has an ID, name and description while the latter has an ID, name and movie ID that maps to a movie from the first table. For the sake of simplicity, this mapping is one on one to avoid the complication of a third table. Here is the MySQL script that you can use to create these tables and populate with a few test records (if you want to follow along):

CREATE TABLE `movies` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `description` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

CREATE TABLE `movies_genres` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `movie_id` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

INSERT INTO `movies` (`id`, `name`, `description`)
VALUES
        (1, 'Big Lebowsky', 'My favorite movie, hands down.'),
        (2, 'Pulp fiction', 'Or this is my favorite movie?');

INSERT INTO `movies_genres` (`id`, `movie_id`, `name`)
VALUES
        (1, 1, 'Comedy'),
        (2, 1, 'Noir'),
        (3, 2, 'Crime');
        

What we want to achieve is migrate the movies into basic Drupal Article nodes and the genres into taxonomy terms of the Tags vocabulary (which the Article nodes reference via a field). Naturally, we also want the migration to mirror the association between the movies and the genres.

Genres

Let us first take care of the genre migration because in Drupal, the movies will depend on them (they will reference them).

Inside the config/install folder of our module, we need the following configuration entity file called migrate.migration.genres.yml:

id: genres
label: Genres
migration_group: demo
source:
  plugin: genres
  key: default
destination:
  plugin: entity:taxonomy_term
process:
  vid:
    plugin: default_value
    default_value: tags
  name: name

The first three elements of this configuration are the migration ID, label and group it belongs to. The following three keys are responsible for the three main parts of the migration pipeline mentioned earlier. Let’s talk about the latter three separately.

Source

Under the source key we specify which plugin the migration should use to represent and deliver the source data (in our case the plugin with the ID of genres). The key key is used to specify which database should our source query use (that is where our data is).

So in the Plugin/migrate/source folder of our module, let’s create our SQL based source plugin for our genres:

Genres.php

namespace Drupal\demo\Plugin\migrate\source;

use Drupal\migrate\Plugin\migrate\source\SqlBase;


class Genres extends SqlBase {

  
  public function query() {
    $query = $this->select('movies_genres', 'g')
      ->fields('g', ['id', 'movie_id', 'name']);
    return $query;
  }

  
  public function fields() {
    $fields = [
      'id' => $this->t('Genre ID'),
      'movie_id' => $this->t('Movie ID'),
      'name' => $this->t('Genre name'),
    ];

    return $fields;
  }

  
  public function getIds() {
    return [
      'id' => [
        'type' => 'integer',
        'alias' => 'g',
      ],
    ];
  }
}

Since we are using a SQL source, we need to have our own source plugin class to provide some information as to how the data needs to be read. It’s not enough to use an existing one from core. The query() method creates the query for the genre data, the fields() method defines each individual row field and the getIds() method specifies the source row field that acts as the unique ID. Nothing complicated happening here.

We are extending from SqlBase which, among other things, looks for the plugin configuration element named key to learn which database it should run the query on. In our case, the default one, as we detailed in the migration configuration entity.

And this is all we need for our simple source plugin.

Destination

For the migration destination, we use the default core taxonomy term destination plugin which handles everything for us. No need to specify anything more.

Process

Under the process key of the migration, we list each destination field we want populated and one or more process plugins that transform the source row fields into data for the destination fields. Since we want the genres to be all terms of the Tags vocabulary, for the vid field we use the default_value plugin which accepts a default_value key that indicates the value each record will have. Since all will be in the same vocabulary, this works well for us.

Lastly, for the term name field we can simply specify the source row field name without an explicit plugin name. This will, under the hood, use the get plugin that simply takes the data from the source and copies it over unaltered to the destination.

For more information on how you can chain multiple process plugins in the pipeline or what other such plugins you have available from core, I recommend you check out the documentation.

Movies

Now that our genres are importable, let’s take a look at the movies migration configuration that resides in the same folder as the previous (config/install):

migrate.migration.movies.yml

id: movies
label: Movies
migration_group: demo
source:
  plugin: movies
  key: default
destination:
  plugin: entity:node
process:
  type:
    plugin: default_value
    default_value: article
  title: name
  body: description
  field_tags:
    plugin: migration
    migration: genres
    source: genres
migration_dependencies:
  required:
    - genres

We notice the same metadata as before, the three main parts of the migration (source, process and destination) but also the explicit dependency which needs to be met before this migration can be successfully run.

Source

Like before, let’s take a look at the movies source plugin, located in the same place as the genres source plugin (Plugin/migrate/source ):

Movies.php:

namespace Drupal\demo\Plugin\migrate\source;

use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Row;


class Movies extends SqlBase {

  
  public function query() {
    $query = $this->select('movies', 'd')
      ->fields('d', ['id', 'name', 'description']);
    return $query;
  }

  
  public function fields() {
    $fields = [
      'id' => $this->t('Movie ID'),
      'name' => $this->t('Movie Name'),
      'description' => $this->t('Movie Description'),
      'genres' => $this->t('Movie Genres'),
    ];

    return $fields;
  }

  
  public function getIds() {
    return [
      'id' => [
        'type' => 'integer',
        'alias' => 'd',
      ],
    ];
  }

  
  public function prepareRow(Row $row) {
    $genres = $this->select('movies_genres', 'g')
      ->fields('g', ['id'])
      ->condition('movie_id', $row->getSourceProperty('id'))
      ->execute()
      ->fetchCol();
    $row->setSourceProperty('genres', $genres);
    return parent::prepareRow($row);
  }
}

We have the same three required methods as before, that do the same thing: query for and define the data. However, here we also use the prepareRow() method in order to alter the row data and available fields. The purpose is to select the ID of the movie genre that matches the current row (movie). That value is populated into a new source field called genres, which we will see in a minute how it’s used to save the Tags taxonomy term reference.

Destination

In this case, we use the node entity destination plugin and we need nothing more.

Process

There are four fields on the Article node we want populated with movie data. First, for the node type we use the same technique as before for the taxonomy vocabulary and set article to be the default value. Second and third, for the title and body fields we map the movie name and description source fields unaltered.

Lastly, for the tags field we use the migration process plugin that allows us to translate the ID of the genre (that we added earlier to the genres source row field) into the ID of its corresponding taxonomy term. This plugin does this for us by checking the migration mapping of the genres and reading these IDs. And this is why the genres migration is also marked as a dependency for the movies import.

Activating and Running the Migration

Now that we have our two migration configuration entities and all the relevant plugins, it’s time to enable our module for the first time and have the configuration imported by Drupal. If your module was already enabled, uninstall it and then enable it again. This will make the config import happen.

Additionally, in order to run the migrations via Drush (which is the recommended way of doing it), install the Migrate Tools module. Then all that’s left to do is to use the commands to migrate or rollback the movies and genres.

To see the available migrations and their status:

drush migrate-status

To import all migrations:

drush migrate-import --all

To roll all migrations back:

drush migrate-rollback --all

Conclusion

And there we have it – a simple migration to illustrate how we can now import, track and roll back migrations in Drupal 8. We’ve seen how the plugin system is used to represent all these different components of functionality, and how the migration definition has been turned into configuration that brings these elements together.

There is much more to learn, though. You can use different source plugins, such as for data in CSV or JSON, complex process plugins (or write your own), or even custom destination plugins for whatever data structure you may have. Good luck!

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Feb 18 2016
Feb 18

Drupal 8 logo

The recommended approach to getting started with Drupal 8 is now via Composer. An official project template has been created for this. We will create our project directly using the template, which is also available on Packagist.

To create a new project based on this template we can run the following Composer command:

composer create-project drupal-composer/drupal-project:8.x-dev my_project --stability dev --no-interaction

This Composer command will pull in the template from Packagist and run a few Drupal specific scripts to prepare our project for installation. The only thing left to do is point our browser to the web/ directory (since that is where the index.php file is) and run the installer as usual.

This template comes with a /web folder that contains, among other things, the main folders of a Drupal installation that are no longer considered part of Drupal core (such as the index.php file or the modules and themes folders). Additionally, it comes with an autoload.php file used by Drupal that simply points to the Composer vendor/ directory, one folder up. So all PHP libraries are now handled from one single place.

The template’s composer.json file requires the latest stable Drupal core + some additional helper tools such as Drush and the Drupal Console. Additionally, it adds the Drupal specific Packagist repository from where we can install Drupal contributed modules, themes and profiles (that get automatically installed in the right place).

If we want to add a Drupal contributed module, we need to find it on the Drupal Packagist and require it in our project via Composer:

composer require drupal/ctools

This will add the Ctools module directly to our web/modules/ directory and update our composer.json file.

The project template also comes with a .gitignore file that keeps Drupal core and all the contributed packages outside of Git, similar to the regular vendor/ packages. So based on an updated composer.json file, we can maintain a smaller Git repository and recreate our project any time. A lot of the benefits of Drush Make have now been incorporated into a Composer flow.

Conclusion

Drupal 8 has come a long way in catching up with other major PHP software. The possibility of fully managing it via Composer, either as a main project or even just as part of a bigger set of applications, is a testament to the community effort that went in.

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Jan 19 2016
Jan 19

In an earlier tutorial, we looked at the Drupal 8 plugin system and how to create our very own custom plugin type. We’ve seen that much of the functionality declared via _info hooks in Drupal 7 has been replaced by these plugins. Our use case was very basic and it allowed each instance of such functionality to be declared manually via a new plugin class and associated form.

Drupal 8 logo

But what if we needed such instances declared dynamically depending on some factors external to our little subsystem? For example, when declaring _info hooks in Drupal 7, we can get a list of something, loop over it and declare a new item in the returned array for each individual something. The menu system does this in order to provide a new block for each menu that either comes with Drupal core or is later created through the UI.

So what about Drupal 8? We’ve seen that for each plugin of a certain type we need to declare a different PHP class. To create a new block, we need a new class. To create another block, we need another class. So where would that looping we see in Drupal 7 take place? The short answer to this is: within a plugin derivative.

In this article we will explore the long answer to that and learn what derivates are and how we can use them. For the latter, we will build an example inside the demo module that can be found in this git repository and which should hopefully help us better understand what’s going on. For a slightly more complex example, the Menu system is great as it provides an individual block for each of its menus (similar to Drupal 7 but using plugins).

What we are going to do is actually very simple. We are going to implement basic Node Block functionality by which for all the article nodes on our site we will have a block. Ridiculous? Sure. Should we be doing this for all the nodes on our site? Definitely not! But it’s a very basic implementation meant to keep things short and demonstrate the use of the plugin derivatives.

Plugin Derivatives

Plugin derivatives are the way through which a plugin of a certain type can be represented in the system as multiple instances of itself. In other words, a plugin can reference a deriver class which is responsible for providing a list of plugin definitions that are based on the initial plugin (start from the same base definition) but have slightly different configuration or definition data. The SystemMenuBlock we referred to above is a great example. It’s a single plugin which has as many derivatives as there are menus on the site.

To go a bit deeper, when a list of all the plugins of a certain type is requested, the plugin manager uses its discovery mechanism to load all the plugins of this type. If that mechanism is decorated with the DerivativeDiscoveryDecorator, the manager will be able to also retrieve derivatives. In order to do this, the derivative discovery looks for a deriver class on each plugin and, if it finds one, asks it for this list.

Plugin type managers that extend the DefaultPluginManager base class should normally have the derivative discovery mechanism decorating the default discovery (annotations). This is the most common pattern in the Drupal core plugin system: annotated discovery wrapped by derivatives.

The Derivative Class

Now that we know what the role of plugin derivatives is, let’s create our first deriver class that will be used by our block plugin (which we will create in a minute).

Inside src/Plugin/Derivative/NodeBlock.php of the demo module we have the following:

<?php

/**
 * @file
 * Contains \Drupal\demo\Plugin\Derivative\NodeBlock.
 */

namespace Drupal\demo\Plugin\Derivative;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides block plugin definitions for nodes.
 *
 * @see \Drupal\demo\Plugin\Block\NodeBlock
 */
class NodeBlock extends DeriverBase implements ContainerDeriverInterface {

  /**
   * The node storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $nodeStorage;

  /**
   * Constructs new NodeBlock.
   *
   * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
   *   The node storage.
   */
  public function __construct(EntityStorageInterface $node_storage) {
    $this->nodeStorage = $node_storage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, $base_plugin_id) {
    return new static(
      $container->get('entity.manager')->getStorage('node')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getDerivativeDefinitions($base_plugin_definition) {
    $nodes = $this->nodeStorage->loadByProperties(['type' => 'article']);
    foreach ($nodes as $node) {
      $this->derivatives[$node->id()] = $base_plugin_definition;
      $this->derivatives[$node->id()]['admin_label'] = t('Node block: ') . $node->label();
    }
    return $this->derivatives;
  }
}

All our class needs to implement is the DeriverInterface and implement its two methods. We use the ContainerDeriverInterface instead because we want to make our deriver container aware. Why? Because we use dependency injection to load Drupal’s entity manager so that we can access the Node storage (this is what the constructor and the create() method do). Additionally, our deriver class extends from the DeriverBase class because that already takes care of one of the required methods (getDerivativeDefinition()).

Finally, getDerivativeDefinitions() is the method responsible for providing an array of plugin definitions that derive from the plugin which uses this class. It receives the $base_plugin_definition as an argument (the definition of the actual plugin which uses this deriver) and we use that to build up our derivative definitions. In our case, we indiscriminately load all the Article nodes and, for each of them, create a separate definition which differs only by having a different admin_label (this is a property on the Drupal\Core\Block\Annotation\Block annotation class). The array of derivatives is keyed by the ID of the derivative (in our case the Node ID which we will use later).

A very important point we need to make here is that loading all the nodes and creating plugins out of them is never a good idea. What would be maybe interesting is to implement functionality by which individual nodes can be exposed as blocks via a checkbox or something like that.

The Block Plugin

Now that we have our deriver class, let’s create a simple block plugin that uses it to generate multiple instances of itself (one for each Article node).

Inside src/Plugin/Block/NodeBlock.php:

<?php

namespace Drupal\demo\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a 'NodeBlock' block plugin.
 *
 * @Block(
 *   id = "node_block",
 *   admin_label = @Translation("Node block"),
 *   deriver = "Drupal\demo\Plugin\Derivative\NodeBlock"
 * )
 */

class NodeBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * @var EntityViewBuilderInterface.
   */
  private $viewBuilder;

  /**
   * @var NodeInterface.
   */
  private $node;

  /**
   * Creates a NodeBlock instance.
   *
   * @param array $configuration
   * @param string $plugin_id
   * @param array $plugin_definition
   * @param EntityManagerInterface $entity_manager
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->viewBuilder = $entity_manager->getViewBuilder('node');
    $this->nodeStorage = $entity_manager->getStorage('node');
    $this->node = $entity_manager->getStorage('node')->load($this->getDerivativeId());
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity.manager')
    );
  }
  
  /**
   * {@inheritdoc}
   */
  public function build() {
    if (!$this->node instanceof NodeInterface) {
      return;
    }
    $build = $this->viewBuilder->view($this->node, 'full');
    return $build;
  }
  
  /**
   * {@inheritdoc}
   */
  public function blockAccess(AccountInterface $account, $return_as_object = FALSE) {
    return $this->node->access('view', NULL, TRUE);
  }
}

The first thing we notice in this plugin’s annotation is the deriver key which points to the class we created before. And that is basically all we need to couple the two. The derivative discovery decorator handles the heavy lifting.

Much of the rest is basic block building we should be familiar with. What’s interesting is that we can use the getDerivativeId() method to retrieve the node ID we used also as the ID of the derivative being displayed and, using that, we load the node object and build the block as the actual node output. Lastly, inside the blockAccess() method we make sure that this block has the same access checks as the actual node itself. So if the current user doesn’t have access to view the current node, the block won’t even show up.

Now if we clear the caches and navigate to the Block Layout interface we should see some blocks called Node Block: [Node title]. You can place these where you want and they will render the relevant node.

Conclusion

In this article, we’ve looked at plugin derivatives and seen a simple example of how they work. The key take away on this topic is that plugin derivatives are the way we dynamically declare multiple instances of the same plugin. They usually help us transform user configured functionality (e.g. menus) into plugins (e.g. menu blocks).

To illustrate the use of derivatives, we’ve seen a very simple technique which allows us to render Article nodes as blocks. We should remember though not to try this out on a website with many Article nodes but rather implement additional functionality that limits the number of nodes that get exposed. You know, so we don’t crash our site.

Questions? Comments? Anything you’d like explained further? Let us know!

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Dec 11 2015
Dec 11

Drupal 8 logo

The Queue API in Drupal allows us to handle a number of tasks at a later stage. What this means is that we can place items into a queue which will run some time in the future and process each individual item at that point and at least once. Usually, this happens on CRON runs, and Drupal 8 allows for a quick set up for cronjob based queues. It doesn’t necessarily have to be CRON, however.

In this article, we will look at using the Queue API in Drupal 8 by exploring two simple examples. The first will see the queue triggered by Cron while the second will allow us to manually do so ourselves. However, the actual processing will be handled by a similar worker. If you want to follow along, clone this git repository where you can find the npq module we will write in this article.

The module we’ll work with is called Node Publisher Queue and it automatically adds newly created nodes that are saved unpublished to a queue to be published later on. We will see how later on can be the next CRON run or a manual action triggered by the site’s administrator. First, let’s understand some basic concepts about queues in Drupal 8.

The theory

There are a few components that make up the Queue API in Drupal 8.

The most important role in this API is played by the QueueInterface implementation which represents the queue. The default queue type Drupal 8 ships with is currently the DatabaseQueue which is a type of reliable queue that makes sure all its items are processed at least once and in their original order (FIFO). This is in contrast to unreliable queues which only do their best to achieve this (something for which valid use cases do exist).

The typical role of the queue object is to create items, later claim them from the queue and delete them when they have been processed. In addition, it can release items if processing is either not finished or another worker needs to process them again before deletion.

The QueueInterface implementation is instantiated with the help of a general QueueFactory. In the case of the DatabaseQueue, the former uses the DatabaseQueueFactory as well. Queues also need to be created before they can be used. However, the DatabaseQueue is already created when Drupal is first installed so no additional setup is required.

The Queue Workers are responsible for processing queue items as they receive them. In Drupal 8 these are QueueWorker plugins that implement the QueueWorkerInterface. Using the QueueWorkerManager, we create instances of these plugins and process the items whenever the queue needs to be run.

The Node Publish Queue module

Now that we’ve covered the basic concepts of the Queue API in Drupal 8, let’s get our hands dirty and create the functionality described in the introduction. Our npq.info.yml file can be simple:

name: Node Publish Queue
description: Demo module illustrating the Queue API in Drupal 8
core: 8.x
type: module

Queue item creation

Inside the npq.module file we take care of the logic for creating queue items whenever a node is saved and not published:

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueInterface;

/**
 * Implements hook_entity_insert().
 */
function npq_entity_insert(EntityInterface $entity) {
  if ($entity->getEntityTypeId() !== 'node') {
    return;
  }

  if ($entity->isPublished()) {
    return;
  }

  /** @var QueueFactory $queue_factory */
  $queue_factory = \Drupal::service('queue');
  /** @var QueueInterface $queue */
  $queue = $queue_factory->get('cron_node_publisher');
  $item = new \stdClass();
  $item->nid = $entity->id();
  $queue->createItem($item);
}

Inside this basic hook_entity_insert() implementation we do a very simple task. We first retrieve the QueueFactoryInterface object from the service container and use it to get a queue called cron_node_publisher. If we track things down, we notice that the get() method on the DatabaseQueueFactory simply creates a new DatabaseQueue instance with the name we pass to it.

Lastly, we create a small PHP object containing the node ID and create an item in the queue with that data. Simple.

The CRON queue worker

Next, let’s create a QueueWorker plugin that will process the queue items whenever Cron is run. However, because we know that we will also need one for manual processing that does the same thing, we will add most of the logic in a base abstract class. So inside the Plugin/QueueWorker namespace of our module we can have the NodePublishBase class:

/**
 * @file
 * Contains Drupal\npq\Plugin\QueueWorker\NodePublishBase.php
 */

namespace Drupal\npq\Plugin\QueueWorker;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;


/**
 * Provides base functionality for the NodePublish Queue Workers.
 */
abstract class NodePublishBase extends QueueWorkerBase implements ContainerFactoryPluginInterface {

  /**
   * The node storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $nodeStorage;

  /**
   * Creates a new NodePublishBase object.
   *
   * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
   *   The node storage.
   */
  public function __construct(EntityStorageInterface $node_storage) {
    $this->nodeStorage = $node_storage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $container->get('entity.manager')->getStorage('node')
    );
  }

  /**
   * Publishes a node.
   *
   * @param NodeInterface $node
   * @return int
   */
  protected function publishNode($node) {
    $node->setPublished(TRUE);
    return $node->save();
  }

  /**
   * {@inheritdoc}
   */
  public function processItem($data) {
    /** @var NodeInterface $node */
    $node = $this->nodeStorage->load($data->nid);
    if (!$node->isPublished() && $node instanceof NodeInterface) {
      return $this->publishNode($node);
    }
  }
}

Right off the bat we can see that we are using dependency injection to inject the NodeStorage into our class. For more information about dependency injection and the service container, feel free to check out my article on the topic.

In this base class we have two methods: publishNode() and the obligatory processItem(). The former publishes and saves a node that is passed to it. The latter loads the node using the node ID contained in the $data object and publishes it if it’s unpublished.

Now, let’s create a CronNodePublisher plugin that will use this logic on Cron runs:

namespace Drupal\npq\Plugin\QueueWorker;

/**
 * A Node Publisher that publishes nodes on CRON run.
 *
 * @QueueWorker(
 *   id = "cron_node_publisher",
 *   title = @Translation("Cron Node Publisher"),
 *   cron = {"time" = 10}
 * )
 */
class CronNodePublisher extends NodePublishBase {}

And that is all. We don’t need any other logic than what already is in our base class. Notice that, in the annotation, we are telling Drupal that this worker needs to be used by Cron to process as many items as it can within 10 seconds. How does this happen?

Whenever Cron runs, it uses the QueueWorkerManager to load all its plugin definitions. Then, if any of them have the cron key in their annotation, a Queue with the same name as the ID of the worker is loaded for processing. Lastly, each item in the queue is claimed and processed by the worker until the specified time has elapsed.

If we now save an unpublished node, it will most likely become published at the next Cron run.

The manual worker

Let’s create also the possibility for the Queue to be processed manually. First, let’s adapt the hook_entity_insert() implementation from before and change this line:

$queue = $queue_factory->get('cron_node_publisher');

to this:

$queue = $queue_factory->get('manual_node_publisher');

You can of course provide an admin screen for configuring which type of node publisher the application should use.

Second, let’s create our ManualNodePublisher plugin:

namespace Drupal\npq\Plugin\QueueWorker;

/**
 * A Node Publisher that publishes nodes via a manual action triggered by an admin.
 *
 * @QueueWorker(
 *   id = "manual_node_publisher",
 *   title = @Translation("Manual Node Publisher"),
 * )
 */
class ManualNodePublisher extends NodePublishBase {}

This is almost the same as with the CRON example but without the cron key.

Third, let’s create a form where we can see how many items are in the manual_node_publisher queue and process them all by the press of a button. Inside npq.routing.yml in the module root folder:

demo.form:
  path: '/npq'
  defaults:
    _form: '\Drupal\npq\Form\NodePublisherQueueForm'
    _title: 'Node Publisher'
  requirements:
    _permission: 'administer site configuration'

We define a path at /npq which should use the specified form that lives in that namespace and that we can define as such:

/**
 * @file
 * Contains \Drupal\npq\Form\NodePublisherQueueForm.
 */

namespace Drupal\npq\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueInterface;
use Drupal\Core\Queue\QueueWorkerInterface;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\Core\Queue\SuspendQueueException;
use Symfony\Component\DependencyInjection\ContainerInterface;

class NodePublisherQueueForm extends FormBase {

  /**
   * @var QueueFactory
   */
  protected $queueFactory;

  /**
   * @var QueueWorkerManagerInterface
   */
  protected $queueManager;


  /**
   * {@inheritdoc}
   */
  public function __construct(QueueFactory $queue, QueueWorkerManagerInterface $queue_manager) {
    $this->queueFactory = $queue;
    $this->queueManager = $queue_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('queue'),
      $container->get('plugin.manager.queue_worker')
    );
  }
  
  /**
   * {@inheritdoc}.
   */
  public function getFormId() {
    return 'demo_form';
  }
  
  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    /** @var QueueInterface $queue */
    $queue = $this->queueFactory->get('node_publisher');

    $form['help'] = array(
      '#type' => 'markup',
      '#markup' => $this->t('Submitting this form will process the Manual Queue which contains @number items.', array('@number' => $queue->numberOfItems())),
    );
    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => $this->t('Process queue'),
      '#button_type' => 'primary',
    );
    
    return $form;
  }
  
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    /** @var QueueInterface $queue */
    $queue = $this->queueFactory->get('manual_node_publisher');
    /** @var QueueWorkerInterface $queue_worker */
    $queue_worker = $this->queueManager->createInstance('manual_node_publisher');

    while($item = $queue->claimItem()) {
      try {
        $queue_worker->processItem($item->data);
        $queue->deleteItem($item);
      }
      catch (SuspendQueueException $e) {
        $queue->releaseItem($item);
        break;
      }
      catch (\Exception $e) {
        watchdog_exception('npq', $e);
      }
    }
  }
}


We are again using dependency injection to inject the QueueFactory and the manager for QueueWorker plugins. Inside buildForm() we are creating a basic form structure and using the numberOfItems() method on the queue to tell the user how many items they are about to process. And finally, inside the submitForm() method we take care of the processing. But how do we do that?

First, we load the Queue and instantiate a Queue worker (in both cases we use the manual_node_publisher id). Then we run a while loop until all the items have been processed. The claimItem() method is responsible for blocking a queue item from being claimed by another queue and returning it for processing. After it gets processed by the worker, we delete it. In the next iteration, the next item is returned and on like this until no items are left.

Although we have not used it, the SuspendQueueException is meant to indicate that during the processing of the item, the worker found a problem that would most likely make all other items in the queue fail as well. And for this reason it is pointless to continue to the next item so we break out of the loop. However, we also release the item so that when we try again later, the item is available. Other exceptions are also caught and logged to the watchdog.

Now if we create a couple of nodes and don’t publish them, we’ll see their count inside the message if we navigate to /npq. By clicking the submit button we process (publish) them all one by one.

This has been a demonstration example only. It’s always important to take into account the potential load of processing a large number of items and either limit that so your request doesn’t time out or use the Batch API to split them into multiple requests.

Conclusion

In this article we’ve looked at the Queue API in Drupal 8. We’ve learned some basic concepts about how it is built and how it works, but we’ve also seen some examples of how we can work with it. Namely, we’ve played with two use cases by which we can publish unpublished nodes either during Cron runs or manually via an action executed by the user.

Have you tried out the Queue API in Drupal 8? Let us know how it went!

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Oct 14 2015
Oct 14

Without question, Display Suite is one of the most popular modules in Drupal’s contributed modules history. It allows the creation of layouts, fields and exposes all sorts of other powerful tools we use to build the presentation layer of our Drupal sites.

Drupal 8 logo

One of the more powerful features of Display Suite (DS) is the ability to create custom fields that can be displayed inside DS layouts alongside the actual core field values. In Drupal 7, this has been a very popular way of building layouts and showing dynamic data that is not strictly related to the output of any Field API field on the node (or other) entity.

Display Suite has been ported and is being maintained for Drupal 8. Depending on another contributed module called Layout Plugin, the D8 version offers much of what we have available in Drupal 7 and probably even more.

In this article, we are going to look at how we can create our own Display Suite field in Drupal 8 using the new OOP architecture and plugin system. To demonstrate this, we are going to create a DS field available only on the Article nodes that can be used to display a list of taxonomy terms from a certain vocabulary. And we’re going to make it so that the latter can be configured from the UI, namely admins will be able to specify which vocabulary’s terms should be listed. Not much usefulness in this example, I know, but it will allow you to understand how things work.

If you are following along, the code we write is available in this repository inside the Demo module. So feel free to check that out.

Drupal 8 plugins

Much of the functionality that used to be declared using an _info hook in Drupal 7 is now declared using plugins in Drupal 8. For more information on using plugins and creating your own plugin types, make sure you check out a previous Sitepoint article that talks about just that.

Display Suite also uses the new plugin system to allow other modules to define DS fields. It exposes a DsField plugin type which allows us to write and maintain all the necessary logic for such a field inside a single plugin class (+ any services we might inject into it). So we no longer implement hook_ds_field_info() and return an array of field information per entity type, but create a plugin class with data straight in its annotation and the relevant logic inside its methods.

VocabularyTerms class

Let us start by creating our plugin class called VocabularyTerms inside the src/plugins/DsField folder of our custom module and annotating it for our purposes:

namespace Drupal\demo\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Plugin that renders the terms from a chosen taxonomy vocabulary.
 *
 * @DsField(
 *   id = "vocabulary_terms",
 *   title = @Translation("Vocabulary Terms"),
 *   entity_type = "node",
 *   provider = "demo",
 *   ui_limit = {"article|*"}
 * )
 */
class VocabularyTerms extends DsFieldBase {
}

This class alone will hold all of our logic for our very simple DsField plugin. But here are a couple of remarks about what we have so far:

  • The annotation is quite self explanatory: it provides meta information about the plugin.
  • The class extends DsFieldBase which provides base functionality for all the plugins of this type.
  • At the time of writing, the ui_limit annotation has just been committed to HEAD so it might not be available in the release you are using. Limiting the availability of the field on content types and view modes can be done by overriding the isAllowed() method of the base class and performing the logic there.

Default configuration

We want our field to be configurable: the ability to select from a list of existing vocabularies. So let’s start off by providing some defaults to this configuration so that if the user selects nothing, the Tags vocabulary which comes with core will be used. For this, we have to implement the defaultConfiguration() method:

/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {

  $configuration = array(
    'vocabulary' => 'tags',
  );

  return $configuration;
}

And since we only have one configuration option, we return an array with one element keyed by the configuration name. That’s about it.

Formatters

We also want to have the ability to specify from the UI if the list of taxonomy terms is a series of links to their term pages or formatter as plain text. We could implement this within the configuration realm but let’s do so using formatters instead. And it’s very simple: we implement the formatters() method and return an array of available formatters:

/**
 * {@inheritdoc}
 */
public function formatters() {
  return array('linked' => 'Linked', 'unlinked' => 'Unlinked');
}

DS Field Formatter Options

These will be available for selection in the UI under the Field heading of the Manage Display page of the content type. And we’ll be able to see the choice when we are building the actual field for display. But more on that in a second.

Configuration summary

It’s also recommended that if we are using UI defined settings, we have a summary of what has been selected as a simple string that describes it. This gets printed under the Widget heading of the Manage Display page of the content type.

DS Configuration Summary

To do this, we need to implement the settingsSummary() method and return said text:

/**
 * {@inheritdoc}
 */
public function settingsSummary($settings) {
  $config = $this->getConfiguration();
  $no_selection = array('No vocabulary selected.');

  if (isset($config['vocabulary']) && $config['vocabulary']) {
    $vocabulary = Vocabulary::load($config['vocabulary']);
    return $vocabulary ? array('Vocabulary: ' . $vocabulary->label()) : $no_selection;
  }

  return $no_selection;
}

Here we start getting more intimate with the actual configuration that was stored with the field, available by calling the getConfiguration() method on our plugin class. What we do above, then, is check if the vocabulary setting has been set, we load it based on its machine name using the Vocabulary class and return an array of strings that need to be printed.

Since we are referencing the Vocabulary class, we also need to use it at the top:

use Drupal\taxonomy\Entity\Vocabulary;

Important to note: I am using Vocabulary statically here to load an entity for the sake of brevity. It is highly recommended you inject the relevant storage using dependency injection and use that to load entities. The same goes for most classes you’ll see me referencing statically below.

Settings form

Now that we display which configuration has been chosen from the UI, it’s time to provide the actual form which will allow the user to do so. This will be made available by clicking the cogwheel under the Operations heading of the Manage Display page of the content type.

DS Field Settings Form

/**
 * {@inheritdoc}
 */
public function settingsForm($form, FormStateInterface $form_state) {
  $config = $this->getConfiguration();

  $names = taxonomy_vocabulary_get_names();
  $vocabularies = Vocabulary::loadMultiple($names); 
  $options = array();
  foreach ($vocabularies as $vocabulary) {
    $options[$vocabulary->id()] = $vocabulary->label();
  }
  $settings['vocabulary'] = array(
    '#type' => 'select',
    '#title' => t('Vocabulary'),
    '#default_value' => $config['vocabulary'],
    '#options' => $options,
  );

  return $settings;
}

Like before, we need to implement a method for this. And what we do inside is load all the taxonomy vocabulary names and prepare an array of options to be used with a Form API select list. The latter is the only element we need for this form.

Rendering the field

The last thing left to do is implement the build() method responsible for rendering the contents of our field:

/**
 * {@inheritdoc}
 */
public function build() {
  $config = $this->getConfiguration();
  if (!isset($config['vocabulary']) || !$config['vocabulary']) {
    return;
  }

  $query = \Drupal::entityQuery('taxonomy_term')
    ->condition('vid', $config['vocabulary']);

  $tids = $query->execute();
  if (!$tids) {
    return;
  }

  $terms = Term::loadMultiple($tids);
  if (!$terms) {
    return;
   }

  return array(
    '#theme' => 'item_list',
    '#items' => $this->buildTermList($terms),
  );
}

So what do we do here? First, we access the chosen vocabulary from the configuration. Then we run an EntityQuery to find all the terms in this vocabulary. Next, we load all these terms and finally we return a render array that uses the item_list theme to print our terms.

Although we don’t need it here, in most cases you’ll need to access the node entity that is currently being rendered. That is available inside the configuration array under the entity key. Moreover, under the build key you have the actual render array of the node being built. So keep this in mind and do inspect the other elements of the configuration array on your own for more information.

I would like to mention a few more things before we take a look at the actual buildTermList() method. First, for brevity, we used the EntityQuery service statically. In your project, you should inject it. Second, we used the Term class statically to load the taxonomy term entities. Again, you should inject its storage and use that for this purpose. And lastly, we should import the Term class at the top with use:

use Drupal\taxonomy\Entity\Term;

Now that this is clear, let’s take a look at our own buildTermList() method:

private function buildTermList(array $terms) {
  $config = $this->getConfiguration();
  $formatter = isset($config['field']['formatter']) && $config['field']['formatter'] ? $config['field']['formatter'] : 'unlinked';
  $items = array();
  foreach ($terms as $term) {
    $items[] = $this->buildTermListItem($term, $formatter);
  }

  return $items;
}

This method is responsible for getting the field formatter, looping through the term entities and building an array of term information that can be printed using the item_list theme. As you can see, though, the individual term entity and formatter are passed to yet another helper method to keep things nice and tidy:

private function buildTermListItem(Term $term, $formatter) {
  if ($formatter === 'linked') {
    $link_url = Url::fromRoute('entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
    return \Drupal::l($term->label(), $link_url);
  }

  return SafeMarkup::checkPlain($term->label());
}

Finally, in the buildTermListItem() method we either return the sanitized title of the term or a link to it depending on the formatter.

Again we see classes which should be injected but were used statically to save some space. With the risk of sounding like a broken record, keep in mind that you should inject these. For now, we must use them at the top:

use Drupal\Core\Url;
use Drupal\Component\Utility\SafeMarkup;

Conclusion

And there we have it, our very own DsField plugin in Drupal 8. Clearing the caches would now make this field available on all view modes of the Article content type.

Moreover, it can be configured to choose among multiple vocabularies, the terms of which it will then display. And finally, we can even specify a formatter to print these terms either linked or as plain text.

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Oct 02 2015
Oct 02

cTools is one of those critical Drupal 7 modules many others depend on. It provides a lot of APIs and functionality that makes life easier when developing modules. Views and Panels are just two examples of such powerhouses that depend on it.

cTools makes available different kinds of functionality. Object caching, configuration exportability, form wizards, dialogs and plugins are but a few. A lot of the credit you would normally attribute to Views or Panels is actually owed to cTools.

Drupal logo

In this article, we are going to take a look at cTools plugins, especially how we can create our very own. After a brief introduction, we will immediately go hands on with a custom module that will use the cTools plugins to make defining Drupal blocks nicer (more in tune to how we define them in Drupal 8).

Introduction

cTools plugins in Drupal 7 (conceptually not so dissimilar to the plugin system in Drupal 8) are meant for easily defining reusable bits of functionality. That is to say, for the ability to define isolated business logic that is used in some context. The goal is to set up that context and plugin type once, and allow other modules to then define plugins that can be used in that context automatically.

If you’ve been developing Drupal sites for more than a year you’ve probably encountered cTools plugins in one shape or form. I think the first plugin type we usually deal with is the content_type plugin which allows us to create our own custom panel panes that display dynamic content. And that is awesome. Some of the others you may have encountered in the same realm of Panels are probably context and access (visibility rules). Maybe even relationships and arguments. These are all provided by cTools. Panels adds to this list by introducing layouts and styles that we normally use for creating Panels layouts and individual pane styles. These are I think the more common ones.

However, all of the above are to a certain extent a black box to many. All we know is that we need to define a hook to specify a directory and then provide an include file with some definition and logic code and the rest happens by magic. Going forward, I would like us to look into how a plugin type is defined so that if the case arises, we can create our own plugins to represent some reusable bits of functionality. To demonstrate this, we will create a module that turns the pesky hook system of defining custom Drupal blocks into a plugin based approach similar to what Drupal 8 is using.

The final code (+ a bit more) can be found in this repository if you want to follow along. And I do expect you are familiar with the steps necessary for defining custom Drupal blocks.

The block_plugin module

As I mentioned, I would like to illustrate the power of cTools plugins with a custom plugin type that makes defining Drupal 7 blocks saner. Instead of implementing the 2 main hooks (hook_block_info() and hook_block_view()) necessary to define a block, we’ll be able to have separate plugin files each responsible for all the logic related to their own block. No more switch cases and changing the hook implementation every time we need a new block. So how do we do this?

First, let’s create our block_plugin.info file to get started with our module:

name = Block Plugin
description = Using cTools plugins to define Drupal core blocks
core = 7.x
dependencies[] = ctools

Simple enough.

The plugin type

In order to define our news plugin type, inside the block_plugin.module file we need to implement hook_ctools_plugin_type() which is responsible for defining new plugin types cTools will recognize:

function block_plugin_ctools_plugin_type() {
  return array(
    'block' => array(
      'label' => 'Block',
      'use hooks' => FALSE,
      'process' => 'block_plugin_process_plugin'
    )
  );
}

In this hook we need to return an associative array of all the plugin type definitions we need keyed by the machine name of the plugin type name. Today we are only creating one called block. For more information on all the options available here, feel free to consult the plugins-creating.html help file within the cTools module. No use repeating all that information here.

The process key defines a function name that gets triggered every time cTools loads for us a plugin and is responsible for shaping or massaging the plugin data before we use it. It’s sort of a helper function that prepares the plugin for us each time so we don’t have to bother. So let’s see what we can do inside that function:

function block_plugin_process_plugin(&$plugin, $info) {
  // Add a block admin title
  if (!isset($plugin['admin title'])) {
    $exploded = explode('_', $plugin['name']);
    $name = '';
    foreach ($exploded as $part) {
      $name .= ucfirst($part) . ' ';
    }
    $plugin['admin title'] = $name;
  }

  // By default we also show a block title but this can be overwritten
  if (!isset($plugin['show title'])) {
    $plugin['show title'] = TRUE;
  }

  // Add a block view function
  if (!isset($plugin['view'])) {
    $plugin['view'] = $plugin['module'] . '_' . $plugin['name'] . '_view';
  }

  // Add a block form function
  if (!isset($plugin['configure'])) {
    $plugin['configure'] = $plugin['module'] . '_' . $plugin['name'] . '_configure';
  }

  // Add a block save function
  if (!isset($plugin['save'])) {
    $plugin['save'] = $plugin['module'] . '_' . $plugin['name'] . '_save';
  }
}

This callback receives the plugin array as a reference and some information about the plugin type. The task at hand is to either change or add data to the plugin dynamically. So what do we achieve above?

First, if the developer hasn’t defined an admin title for the block plugin, we generate one automatically based on the machine name of the plugin. This is so that we always have an admin title in the Drupal block interface.

Second, we choose to always display the title of the block so we mark the show title key of the plugin array as TRUE. When defining the block plugin, the developer has the option of setting this to FALSE in which case we won’t show a block title (subject).

Third, fourth and fifth, we generate a callback function for the block view, save and configure actions (if they haven’t already been set by the developer for a given plugin). These callbacks will be used when implementing hook_block_view(), hook_block_configure() and hook_block_save(), respectively. We won’t be covering the latter two in this article but feel free to check out the repository to see what these can look like.

And that’s pretty much all we need for defining our custom plugin type. We should, however, also implement hook_ctools_plugin_directory() which, as you may know, is responsible for telling cTools where a plugin of a certain type can be found in the current module:

function block_plugin_ctools_plugin_directory($module, $plugin) {
  if ($module == 'block_plugin' && in_array($plugin, array_keys(block_plugin_ctools_plugin_type())) ) {
    return 'plugins/' . $plugin;
  }
}

This will need to be implemented also by any other module that wants to define block plugins.

Drupal blocks

Now that we have the plugin type, let’s write the code which turns any defined block plugin into a Drupal block. Will start with the hook_block_info() implementation:

function block_plugin_block_info() {
  $blocks = array();

  $plugins = block_plugin_get_all_plugins();
  foreach ($plugins as $plugin) {
    $blocks[DELTA_PREFIX . $plugin['name']] = array(
      'info' => $plugin['admin title'],
    );
  }

  return $blocks;
}

Here we load all of the plugins using a helper function and define the minimum required information for the block. Here you can add also more information but we are keeping it simple for brevity.

We know each plugin will have a machine name (the name of the include file basically) and an admin title because we generate one in the processing phase if one doesn’t exist. The DELTA_PREFIX is a simple constant in which we define the prefix we want for the block machine name because we need to reuse it and should be able to easily change it if we want to:

define('DELTA_PREFIX', 'block_plugin_');

Our helper function we saw earlier looks like this:

function block_plugin_get_all_plugins() {
  return ctools_get_plugins('block_plugin', 'block');
}

It’s a simple wrapper around the respective cTools function. And for that matter, we also have the following function responsible for loading a single plugin by its machine name:

function block_plugin_get_plugin($name) {
  return ctools_get_plugins('block_plugin', 'block', $name);
}

This is very similar to the one before.

In order to make our Drupal block definitions complete, we need to implement hook_block_view():

function block_plugin_block_view($delta = '') {
  $plugin = block_plugin_plugin_from_delta($delta);
  if (!$plugin) {
    return;
  }

  $block = array();

  // Optional title
  if (isset($plugin['title']) && $plugin['show title'] !== FALSE) {
    $block['subject'] = $plugin['title'];
  }

  // Block content
  $block['content'] = $plugin['view']($delta);

  return $block;
}

So what’s happening here?

First, we use another helper function to try to load a plugin based on the delta of the current block and do nothing if we are not dealing with a plugin block.

Second, we build the block. If the user specified a title key on the plugin and the show title key is not false, we set the subject of the block (its title basically) as the former’s value. As for the actual block content, we simply call the view callback defined in the plugin. And that’s it.

Let us quickly see also the helper function responsible for loading a plugin based on a block delta:

function block_plugin_plugin_from_delta($delta) {
  $prefix_length = strlen(DELTA_PREFIX);
  $name = substr($delta, $prefix_length);
  $plugin = block_plugin_get_plugin($name);
  return $plugin ? $plugin : FALSE;
}

Nothing complicated going on here.

Defining block plugins

Since we told cTools that it can find block plugins inside the plugins/block folder of our module, let’s go ahead and create that folder. In it, we can add our first block inside a file with the .inc extension, for example my_block.inc:

<?php

$plugin = array(
  'title' => t('This is my block'),
);

/**
 * Returns a renderable array that represents the block content
 */
function block_plugin_my_block_view($delta) {
  return array(
    '#type' => 'markup',
    '#markup' => 'Yo block!'
  );
}

Like we do with all other plugins (content_type, context, etc), the plugin definition is in the form of an array inside a variable called $plugin. And for our case all we need at this point is a title (and not even that since without it the block simply won’t show a title).

Below it, we defined our callback function to display the block. The naming of this function is important. It matches the pattern we used for it during the processing phase (module_name_plugin_name_view). If we want to name it differently, all we have to do is reference the function name in the view key of the $plugin and it will use that one instead.

And that is basically it. We can now clear our caches and go to the Block administration screen where we can find our block and add it to a region. Showing that block on the page should trigger the view callback for that block plugin and render the contents.

Conclusion

In this article we’ve talked a bit about cTools plugins and saw how we can define our own type of plugin. We used the latter for transforming the Drupal block system into a rudimentary plugin system. This can be extended further to allow also for the block related configuration hooks to be replaced by callbacks inside the plugin include file. Additionally, as mentioned earlier, you can also make sure all the data available in hook_block_info() can be defined inside the plugin. I leave these tasks up to you.

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Sep 14 2015
Sep 14

In the first installment of this series we started our journey towards creating some simple but powerful functionality. The goal we set was to have the possibility to load a form on each node page and to be able to choose which form type should be used on the different node bundles.

Drupal 8 logo

The first step is done. We created a custom plugin type called ReusableForm already featured with a base plugin class that new plugins can extend. Additionally, we saw that each plugin will interact with a form class that is defined in their annotation. And like with the plugins, we also created a base class new forms can extend.

It follows to see how we can configure the core node types to use one of the plugins defined on the site and how to render the relevant form when viewing the node. But first, in order to have something to work with, let’s create our first ReusableForm plugin that uses a very simple form.

Our first plugin

Inside the src/Form folder of our module, create a class called BasicForm.php (or whatever you want to call it). Inside, we can have this simple form definition:

<?php

namespace Drupal\reusable_forms\Form;

use Drupal\Core\Form\FormStateInterface;

/**
 * Defines the BasicForm class.
 */
class BasicForm extends ReusableFormBase {

  /**
   * {@inheritdoc}.
   */
  public function getFormId() {
    return 'basic_form';
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form = parent::buildForm($form, $form_state);

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Handle form submission.
  }
}

This form class extends our form base and implements all the required methods. For more information on forms and how they work in Drupal 8, you can check out one of my previous articles on Sitepoint.com. But as you can see, we are calling the parent buildForm() method so that the logic we defined in the base class takes place. The rest is implementation detail (and here we don’t do much). But you can perform whatever logic you want and handle the submissions in any way you need.

This form can now be used with a plugin of our type and it will have 3 form elements inherited from the form base class.

Next, let’s create our first plugin. Inside the src/Plugin/ReusableForm folder of our module, we can have a file called BasicForm.php with the following:

<?php

namespace Drupal\reusable_forms\Plugin\ReusableForm;

use Drupal\reusable_forms\ReusableFormPluginBase;

/**
 * Provides a basic form.
 *
 * @ReusableForm(
 *   id = "basic_form",
 *   name = @Translation("Basic Form"),
 *   form = "Drupal\reusable_forms\Form\BasicForm"
 * )
 */
class BasicForm extends ReusableFormPluginBase {}

As you can see, all we need for our purposes is in the annotation: the id, name and form class to be used with this plugin. You can add your own methods, override existing ones and handle way more complex logic if you want, though. But for us this is ok as the ReusableFormPluginBase class handles the form building already.

And that’s it. This is all it takes to now define new ReusableForm plugins. We create a form class with all the fields and logic it needs and then reference it inside a simple plugin class.

Node type configuration

Now that we have a ReusableForm plugin, we can proceed with configuring the node type entities to make use of it. For this, we’ll need to first turn our plugin manager into a service so that we can access it and load plugins.

Inside the reusable_forms.services.yml file of our module we can have this:

services:
    plugin.manager.reusable_forms:
        class: Drupal\reusable_forms\ReusableFormsManager
        parent: default_plugin_manager

Now we’ll be able to access the plugin manager using the plugin.manager.reusable_forms service id. And by using the parent key in the definition, we specified that all its dependencies can be looked up from the parent. That’s cool!

Next, let’s turn to our .module file and do a couple of things in there. First, we want to alter the node type edit form and add some extra information about our form plugins. This is so that when users edit a node bundle they can select which form plugin should be used with the nodes of that type:

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function reusable_forms_form_node_type_form_alter(&$form, FormStateInterface $form_state) {

  $form['reusable_forms'] = array(
    '#type' => 'details',
    '#title' => t('Reusable forms'),
    '#group' => 'additional_settings',
  );

  // Load the current node type configuration entity.
  $node_type = $form_state->getFormObject()->getEntity();

  $form['reusable_forms']['reusable_forms_enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable reusable forms'),
    '#description' => t('Check this box if you would like a reusable form on this node type.'),
    '#default_value' => $node_type->getThirdPartySetting('reusable_forms', 'enabled', 0),
  );

  $form_plugins = \Drupal::service('plugin.manager.reusable_forms')->getDefinitions();
  $options = [];
  foreach ($form_plugins as $name => $plugin) {
    $options[$name] = $plugin['name'];
  }

  $form['reusable_forms']['reusable_forms_enabled'] = array(
    '#type' => 'radios',
    '#title' => t('Available forms'),
    '#default_value' => $node_type->getThirdPartySetting('reusable_forms', 'plugin', 'basic_form'),
    '#options' => $options,
    '#description' => t('The available forms you can choose from for this node type.'),
    '#states' => array(
      'visible' => array(
        ':input[name="reusable_forms_enabled"]' => array('checked' => TRUE),
      ),
    ),
  );

  $form['#entity_builders'][] = 'reusable_forms_form_node_type_form_builder';
}

Implementing hook_form_BASE_FORM_ID_alter will do the trick perfectly for this. Though we mustn’t forget to use the FormStateInterface class at the top:

use Drupal\Core\Form\FormStateInterface;

So what happens here? We are creating a new fieldset to group two form fields relevant for our purpose: a checkbox to enable the reusable forms and a select list to choose from the existing plugins. As options to the latter we are building an array of all the plugin names after we load our plugin manager and request from it all the available definitions. And using the #states magic we make sure that this latter field is only visible if the checkbox to enable the forms is checked.

Right at the end we are adding an extra callback function to the #entity_builders group that will be triggered when the entity is saved and that has the purpose of mapping values to an entity. So let’s see that function now:

/**
 * Entity form builder for the node type form to map some values to third party
 * settings
 */
function reusable_forms_form_node_type_form_builder($entity_type, NodeTypeInterface $type, &$form, FormStateInterface $form_state) {
  if ($form_state->getValue('reusable_forms_enabled') === 1) {
    $type->setThirdPartySetting('reusable_forms', 'enabled', 1);
    $type->setThirdPartySetting('reusable_forms', 'plugin', $form_state->getValue('reusable_forms_enabled'));
    return;
  }

  $type->unsetThirdPartySetting('reusable_forms', 'enabled');
  $type->unsetThirdPartySetting('reusable_forms', 'plugin');
}

And again, let’s make use of the NodeTypeInterface at the top:

use Drupal\node\NodeTypeInterface;

In this function we are doing something simple but awesome. If the admin has enabled the use of reusable forms on this bundle, we make use of the configuration entity’s third party settings space to store our data. Otherwise we simply unset it if it exists.

I purposefully ignored this last point when talking about the form alter we implemented before. Now you can see how the form element default values are populated by making a request to the third party settings object on the node type configuration entity.

Configuration schema

Before moving on to actually displaying the forms, we need to also add the schema definition for the new configuration we are storing. Inside the config/schema/reusable_forms.schema.yml file of our module we can add the following:

node.type.*.third_party.reusable_forms:
  type: mapping
  label: 'Reusable Forms'
  mapping:
    reusable_forms_enabled:
      type: boolean
      label: 'Whether to enable the reusable forms on this node type'
    reusable_forms_enabled:
      type: sequence
      label: 'Available forms'
      sequence:
        type: string
        label: 'Plugin name'     

Node view

Now that we are storing the user preferences on the node type config entity, let’s see how we can render our chosen form plugins on the node pages of the enabled types.

The first thing we’re going to do is define a content entity pseudo field that will be configurable from the node Manage display interface. Still inside our .module file we can have this:

/**
 * Implements hook_entity_extra_field_info().
 */
function reusable_forms_entity_extra_field_info() {
  $extra = array();

  $bundles = NodeType::loadMultiple();
  $bundles = array_filter($bundles, function($bundle){
    return $bundle->getThirdPartySetting('reusable_forms', 'enabled') === 1;
  });

  foreach ($bundles as $bundle) {
    $extra['node'][$bundle->Id()]['display']['reusable_form'] = array(
      'label' => t('Reusable form'),
      'description' => t('Reusable form'),
      'weight' => 100,
      'visible' => TRUE,
    );
  }

  return $extra;
}

And again we have to use NodeType at the top:

use Drupal\node\Entity\NodeType;

What happens here is simple. We load all the node bundles and filter out the ones for which the reusable forms are not enabled. Then for each one we define an extra display component in a very self-explanatory way.

By clearing the cache will be able to already see the new pseudo field in place on the configuration page – though it won’t yet do anything. Which brings us to the last piece of the puzzle, rendering the relevant form.

To make the pseudo field we just defined useful, we need to implement hook_entity_view (or a variant of it) and render its content:

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function reusable_forms_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
  if ($display->getComponent('reusable_form')) {
    $plugin_manager = \Drupal::service('plugin.manager.reusable_forms');
    $node_type = NodeType::load($entity->bundle());
    $plugin = $node_type->getThirdPartySetting('reusable_forms', 'plugin');
    if (!$plugin) {
      return;
    }
    $build['reusable_form'] = $plugin_manager->createInstance($plugin)->buildForm($entity);
  }
}

And let’s not forget the use statements at the top:

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;

First, we check if the reusable_form component exists on this node’s display (whether it is made visible in the UI). If it is, we add the reusable_form key to the render array that is building this node view.

We start by loading the plugin manager and the node type configuration object of the current node entity. Then we use the former to instantiate a plugin with the ID defined in the third party settings of the latter. And in doing so, as you remember, we are passing as argument the current entity object so that the form being built can be aware of the entity it is showing up with (though we are not really taking advantage of this in our example).

And that’s it. What’s left is to install the module from scratch (since we added the config schema), edit a node type and select our default plugin (or another one if you created it). Then you can configure the display of this node type to show the reusable form which will then do so when viewing a node of that type. Neat.

Conclusion

As we’ve seen in this article series, Drupal 8 provides us with the tools to do some powerful stuff. With the plugin system we set up the basis for our reusable functionality. And then we hooked into various points of Drupal core to make use of this logic. We’ve seen how any configuration entity that implements the ThirdPartySettingInterface can be extended to include new data. And lastly, we’ve displayed relevant data when viewing content entities with the help of pseudo fields.

But this is just the beginning. You can take what we did here and extend it to your needs. For instance, you can create a new content entity type that will model form submissions. The possibilities are endless.

Daniel Sipos

Meet the author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Sep 11 2015
Sep 11

Reusable Forms in Drupal 8

  • Drupal 8 Custom Plugin Types

Drupal 8 comes with a great addition to the backend developer toolkit in the form of the plugin system. Completely new, specific to Drupal and evolved from serving only a few specific purposes, plugins have become the go-to system for reusable functionality in Drupal 8.

Drupal 8 logo

In this article series of two parts, we will use this system to build a feature that allows the use of custom forms together with node entities. After we’re done, we’ll be able to do the following:

  • configure node bundles to use one of multiple form types to be displayed together with the node display
  • easily define new form types by extending from a sensible base class

Because the topic is very well covered elsewhere, I will not go into the details of how plugins work. But do feel free to brush up on the theory before diving into the crux of it here. And if you want to take a look at the end result, the code we write in both articles can be found in this repository.

We will get started by creating our custom plugin type. To this end, we will have 2 interfaces and 6 classes. It sounds like much, but I assure you they are rather boilerplate and quick to set up. Then, in the next installment of this series, we will see how to use it for our reusable forms attached to nodes.

Plugin manager

Responsible for discovering and loading plugins, the most important part in any plugin type is the manager. However, it’s very simple to create one because Drupal already provides us with a sensible default base to extend. So in our module’s /src folder we can have this class inside a ReusableFormManager.php file (the de facto name of our plugin type becoming ReusableForm):

<?php
namespace Drupal\reusable_forms;

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

class ReusableFormsManager extends DefaultPluginManager {

public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
    parent::__construct('Plugin/ReusableForm', $namespaces, $module_handler, 'Drupal\reusable_forms\ReusableFormPluginInterface', 'Drupal\reusable_forms\Annotation\ReusableForm');
    $this->alterInfo('reusable_forms_info');
    $this->setCacheBackend($cache_backend, 'reusable_forms');
  }
}

As I mentioned, our manager extends the DefaultPluginManager class and just overrides the constructor to call the parent one with some important information about our plugin type:

  • Plugin/ReusableForm – the subdirectory where plugins of this type will be found within any module
  • Drupal\reusable_forms\ReusableFormPluginInterface – the interface each of our plugins will need to implement
  • Drupal\reusable_forms\Annotation\ReusableForm – the annotation class that will define our plugin properties (such as ID, name, etc.)

Additionally, we create an alter hook which can be implemented by various modules to alter the plugin definitions and we set a key for our plugins in the cache backend. For more information about plugin managers, what they do and how they are set up, you should consult the documentation page available on Drupal.org.

Plugin interface

Next, let’s create that interface the manager expects all our plugins to implement. Inside a file called ReusableFormPluginInterface.php located in the src/ folder of our module, we can have this:

<?php

namespace Drupal\reusable_forms;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;

interface ReusableFormPluginInterface extends PluginInspectionInterface, ContainerFactoryPluginInterface {

  /**
   * Return the name of the reusable form plugin.
   *
   * @return string
   */
  public function getName();

  /**
   * Builds the associated form.
   *
   * @param $entity EntityInterface.
   *   The entity this plugin is associated with.
   *
   * @return array().
   *   Render array of form that implements \Drupal\reusable_forms\Form\ReusableFormInterface
   */
  public function buildForm($entity);
}

This is a very simple interface that enforces only two methods: getName() and buildForm(). The first will return the name of the plugin while the latter is expected to be passed an entity object and to return a render array of a form definition that implements \Drupal\reusable_forms\Form\ReusableFormInterface (the interface we will set up for our actual forms). You’ll also notice that we are extending two other interfaces. Those provide us with some extra helpful methods and allow us to inject dependencies from the container.

Plugin annotation

As defined in the manager, let’s also set up our annotation class inside src/Annotation/ReusableForm.php:

<?php

namespace Drupal\reusable_forms\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
 * Defines a reusable form plugin annotation object.
 *
 * @Annotation
 */
class ReusableForm extends Plugin {

  /**
   * The plugin ID.
   *
   * @var string
   */
  public $id;

  /**
   * The name of the form plugin.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */
  public $name;

  /**
   * The form class associated with this plugin
   *
   * It must implement \Drupal\reusable_forms\Form\ReusableFormInterface.
   *
   * @var string
   */
  public $form;
}

Here we simply extend the default Plugin annotation class and define three properties (id, name and form). These will be the three keys found in the annotation of our individual plugins (we’ll see an example in part two of this series).

Plugin base

So far we have the core of what we need for our plugin type: a plugin manager that can discover new plugins using the annotation we defined and instantiate them with its default factory, i.e. the Container Factory.

Let us now lay the ground work for the plugins themselves by creating a base class all the plugins can/should/will extend. Inside the src/ folder of our module we can create a new file called ReusableFormPluginBase.php with the following abstract class inside:

<?php

namespace Drupal\reusable_forms;

use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Form\FormBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class ReusableFormPluginBase extends PluginBase implements ReusableFormPluginInterface {

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilder.
   */
  protected $formBuilder;

  /**
   * Constructs a ReusableFormPluginBase object.
   *
   * @param array $configuration
   * @param string $plugin_id
   * @param mixed $plugin_definition
   * @param FormBuilder $form_builder
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilder $form_builder) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->formBuilder = $form_builder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('form_builder')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->pluginDefinition['name'];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm($entity) {
    return $this->formBuilder->getForm($this->pluginDefinition['form'], $entity);
  }
}

There are a few things to note here. First, we are extending from the plugin base class provided by Drupal so that we get some useful functionality (the methods of PluginInspectionInterface are already implemented with sensible defaults). Second, we are implementing the interface we defined earlier. All of our plugins need to implement it so might as well take care of it here. Third, we are using dependency injection to load from the container the form_builder service we will need to build our forms. This is possible because our interface extends from the ContainerFactoryPluginInterface.

For more information about the service container and dependency injection in Drupal 8, check out one of my previous articles on Sitepoint.com.

As our interface dictates, we already take care of implementing the two methods right here in our base class. The getName() method will simply return the name of the plugin as defined in the plugin annotation. The buildForm() method, on the other hand, will use the form builder to build a form. For this it will use the class provided in the plugin annotation’s form key (which needs to be the fully qualified name of a class that implements our Form interface which we haven’t defined yet). In doing so, we also pass the $entity argument to the form (whatever that may be as long as it implements EntityInterface). This is so that the form that is being rendered on the node page becomes aware of the node it is being rendered with.

Form Interface

Our plugin type is pretty much finished. We can now provide some sensible defaults to the forms that will be used by these plugins. Inside src/Form/ReusableFormInterface.php we can have this simple interface:

<?php

namespace Drupal\reusable_forms\Form;

use Drupal\Core\Form\FormInterface;

interface ReusableFormInterface extends FormInterface {}

We are doing nothing here except for extending from the default Drupal FormInterface. We have it so that we can add whatever methods we need our forms to implement (currently none) and are able to identify forms that implement this interface as compatible with our plugins.

More from this author

Form base

Now that we have a form interface to implement, let’s also create a form base class that the rest of the forms can extend. Inside src/Form/ReusableFormBase.php we can have the following abstract class:

<?php

namespace Drupal\reusable_forms\Form;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Defines the ReusableFormBase abstract class
 */
abstract class ReusableFormBase extends FormBase implements ReusableFormInterface {

  /**
   * @var EntityInterface.
   */
  protected $entity;

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $build_info = $form_state->getBuildInfo();
    if ($build_info['args'] && $build_info['args'][0] instanceof EntityInterface) {
      $this->entity = $build_info['args'][0];
    }

    $form['first_name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('First name'),
    );

    $form['last_name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Last name'),
    );

    $form['email'] = array(
      '#type' => 'email',
      '#title' => $this->t('Email'),
    );

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#button_type' => 'primary',
    );

    return $form;
  }
}

As you can see, we are implementing the interface but we are also extending from the default Drupal FormBase class. And in this example we only have the buildForm method that returns a simple form with three fields and a submit button. You can do whatever you want here in terms of what you consider a good base form. Additionally though, at the beginning of this method, we are checking if an EntityInterface object has been passed as an argument when this form is built and setting it as a protected property. The classes extending this will be able to make use of this entity as long as they call the parent::buildForm() method first.

Conclusion

In the first part of this article series we focused on setting up our custom plugin type and getting it ready for use. In doing so we quickly went through this process and saw how our plugins are expected to work with the actual form classes. In the next part we will work on making it possible to display them together with the nodes. This means adding extra configuration to the node type entities and displaying the forms using pseudo fields managed as part of the regular content view modes. Stay tuned!

Aug 17 2015
Aug 17

In the first article on Drupal 8 module development we looked a bit at the routing aspect of this process. We’ve seen that creating pages with paths is now a matter of declaring routes that match up with controllers. The latter, as we’ve seen, can return a render array that gets interpreted into markup and displayed in the main content area of that page. However, did you know that under the hood, Drupal actually transforms that array into a Response object according to the dictates of Symfony’s HTTPKernelInterface?

drupal8wide

In this article, I would like us to go deeper into the internals of Drupal 8 (and Symfony2) and look at what actually happens (and can happen) from the moment a request is made by a user to the one in which they see something returned in response. The example I mentioned above is just one direction this process can go in, and today we are also going to see other possibilities. The goal is to understand the flexibility of the system which in turn can help us build awesome applications.

Before going into it, I strongly recommend you check out this diagram which does an amazing job at synthesizing what is often referred to as the render pipeline. Though in my opinion it represents more than the name implies because the render system is only part of what’s depicted, albeit a big one.

Front controller (index.php)

It’s no secret that Symfony2 is now an important part of Drupal. The latter uses many of Symfony’s components, most importantly for this article the HTTPKernel and HTTPFoundation ones. Together they are responsible for encapsulating a user request, passing it to the application and then returning whatever comes back to the user in a consistent and OO way.

The HTTPKernelInterface (something you probably heard about also from other contexts) is what glues all of this together by taking in a Request object and always returning a Response one. A very simple but powerful concept.

This process is initiated inside the index.php file which starts by generating said Request object and passing it to the HTTPKernel::handle() method. The latter is then responsible for returning a Response object. At a high level, this is what happens both in a Drupal application as well as in a Symfony one (or any other that leverages the HTTPKernel component).

HTTPKernel and events

HTTPKernel is the heart of any Symfony based application. Its handle() method, as we saw, has a great deal of responsibility in preparing a response and it does so in a workflow driven by events. This makes for a very flexible application where the heavy lifting is always delegated to listeners of these events.

If you look at the diagram from earlier, you can see this workflow depicted in the second column, and it essentially represents the glue between Symfony and the Drupal side of things.

It starts with the first event called kernel.request. Subscribers to this event handle various tasks. But two very important ones in Drupal 8 are the format negotiation and routing. The first determines the type of response that needs to be returned (html, json, image, pdf, etc) while the second determines what the code responsible for handling this is (the _controller key of a route definition inside the routing.yml file). But like in most steps in this event workflow, if a listener returns a response object, the process skips most of the further steps (stops propagation) and goes straight to kernel.response.

The second event is kernel.controller which is called after the application knows which controller is responsible for handling the request. At this point, listeners can still perform some overriding operations on it. Closely following this step, the Kernel is responsible for resolving the arguments that get passed to the controller. One such operation in Drupal is loading objects based on IDs found in the request (for example nodes) and directly providing the controller with them. Then finally the controller gets called with the respective parameters.

The controller is responsible for returning a response of some kind. If it returns a Response object, the process skips to the kernel.response event. Listeners to the latter can perform last minute modifications on the object such as modifying headers or the content itself. And after getting it from the handle() method, the front controller uses the send() method on the Response object to send it back to the user and terminates the process.

symfony event workflow

Going deeper with render arrays

If the controller does not return a Response object, the Kernel fires one last event: kernel.view. Its subscribers are responsible for turning the result of the controller into an actual Response object. So this means that you have the option of returning from your controller any kind of object as long as you couple it with a VIEW event subscriber that turns that into a proper Response.

However, as we’ve seen in the example, most of the time controllers will return a render array. Usually this represents the page’s main content (similar to page callbacks in Drupal 7).

To handle this, Drupal 8 has a MainContentViewSubscriber responsible for transforming this array into proper Response objects. It does so by using a particular MainContentRenderer chosen during the format negotiation phase we’ve talked about before. And although there are a few such renderers already available, the default one used is the HtmlRenderer.

HTMLRenderer

Since this is the most commonly used type of main content renderer, let’s go in a bit deeper and see how this builds the page.

One of the cool things about this step in the process is the concept of page variants.
This means that HTMLRenderer dispatches an event responsible for finding out which type of page is to be used to wrap the main content render array: RenderEvents::SELECT_PAGE_DISPLAY_VARIANT. By default, the SimplePageVariant is used unless the Block module is enabled. In that case the BlockPageVariant kicks in and allows the placement of the blocks in the regions around the main content. If you want, you can subscribe to this event in your own module and provide your own variant.

All of this happens within the prepare() method of the HTMLRenderer which supplies the renderResponse() method with a #type => 'page' render array that wraps the main content one. The latter two get in turn wrapped into a #type => 'html' render array which gets finally rendered using the Renderer class (the equivalent of drupal_render() in Drupal 7). The resulting HTML string gets added to the Response object and gets returned to the front controller.

Although this is a very high level overview of the process, this is basically what happens. Now we have a Response object which means the Kernel can dispatch its kernel.response event. And right after this, the front controller can send the Response back to the user and terminate the process.

Conclusion

In this article we’ve taken a journey into Drupal 8 (and Symfony2) internals by following the pipeline from a user request to the response the server returns. We’ve seen how Drupal 8 leverages the HTTPKernel and HTTPFoundation Symfony2 components and how it basically lives on top of them. Additionally, we’ve seen how the glue between them is made up of the events the Kernel dispatches to which Drupal subscribes for all of its functionality. Finally, we’ve seen how HTML pages are built and returned to the user with the help of the render pipeline.

I believe that understanding what is going on under the hood in a Drupal 8 application will allow you to create awesome applications by knowing exactly which entry points you have into this flow. And I believe that if you take away only one thing from this article, it should be the word flexibility. Because the flexibility for building exactly what we need in Drupal 8 far surpasses anything in Drupal 7. It has truly become modern.

Aug 10 2015
Aug 10

If you are a Drupal developer who has dabbled in theming older versions of Drupal (5, 6, 7) you understand why frustration is the trusty companion of any Drupal themer. Luckily, though, Drupal 8 promises so many improvements that even the Angry Themer is happy for a change. It is only natural we jump in and start looking at what these improvement are.

Drupal 8 logo

In this article, we will look at some of the more important changes to theming in Drupal 8. Although we will keep things simple and start from the basics, I do assume you have at least a bit of experience with theming in Drupal 7. And since theming is a big subject and this is just an introduction, you’ll find all sorts of links to more information that can help you out.

Starting up

As with custom modules, a new theme always starts with a folder and the obligatory .info.yml file inside (as opposed to the old .info file). These go in the root themes folder of the Drupal codebase (as opposed to the old sites/all/themes directory) and they should both have the same name (e.g. my_theme.info.yml within the my_theme folder).

Inside the .info.yml file we provide a few required keys:

name: Theme name
description: Theme description
type: theme
core: 8.x

All the rest are optional and come as needed. With this in place, you can already navigate to admin/appearance and enable the new theme or set it as default. You’ll notice that from the good ‘ol Bartik you now have a really naked theme in place. But the big difference from Drupal 7 is that you can easily start theming every aspect of your website.

A major improvement is that we now have an intermediary core theme called Classy which bridges the gap between the data output by the Drupal backend (usually the system module) and the actual themes. So what all the other available core themes do (and what you can as well) is use Classy as their base theme and override its templates:

base theme: classy

Alternatively, you can also not do this but copy all of its template files into your theme and you’ll end up at the same place. But you probably won’t need all those files (some might not need changing) so in my opinion it’s just better to use Classy as a base theme and just override what you need.

Although I won’t go into details, region definition is also quite an important aspect of a theme’s info.yml file:

regions:
  header: 'Header'
  content: 'Content'
  footer: 'Footer'

With this in place, inside your page.html.twig template file you can print these regions out like so:

{{ page.header }}

This is the Twig templating syntax you should start getting familiar with if you haven’t already.

Twig

By now I think everybody knows that Twig is the templating language used in Drupal 8. I won’t go into it too much because there are many resources out there with plenty of information about how Twig syntax can make themers forget all about PHPTemplate in no time.

But one important thing to keep in mind is that there are no more theme functions in Drupal 8. This means that all themable output is run through an html.twig file. We can still use hook_theme() to define reusable theme implementations, but they will now all use Twig files. And the cool thing is that these Twig templates are extensible. This means they can define only the necessary bits related to them and inherit the rest from their parent. Check out the Twig extends documentation for more information on what I mean.

Templates

I mentioned before how in Drupal 8 we are in full control over the markup of our site due to everything being neatly organised in template files within the Classy theme. So one of the next steps in building your theme would be overriding the html.html.twig and/or page.html.twig files to provide markup for your pages. Doing so, you get to play with semantic HTML5 markup because that is what Drupal 8 outputs by default.

All of these template files have documentation at the top with information about what variables you have available. Additionally, you can make use of various Twig filters and functions to manipulate this data straight from the template files. It is in fact recommended, for instance, to build translatable strings or urls straight in there rather than the preprocessor in order to maybe avoid unnecessary function calls (if these don’t actually end up being printed).

And speaking of preprocess functions, they do still exist. However, there is no longer a template.php file to put them in but rather a .theme PHP file (my_theme.theme) inside the root theme folder.

An interesting note about preprocessors is that in Drupal 8 we have to try to always prepare render arrays for the template file variables rather than final markup as we often do in Drupal 7. Of course, this is only if the data needs to be rendered and is not already just a simple string or something primitive like that. The point is to no longer call drupal_render() within our preprocessor functions but let the Twig magic handle that for us.

Debugging

There are many improvements made also to debugging of theme information. By turning on Twig debugging (debug: true) inside the sites/defaults/services.yml file, you get a bunch of helpful HTML comments printed in the page source.

Theme debugging in Drupal 8

These allow you to see which template file is responsible for a particular piece of markup, where it is located and what theme suggestions you can use to override it. No more spending hours trying to figure out what to override. This is a great win!

Additionally, Twig comes with the dump() function which allows you to print out a particular variable to the page from Twig. However, if that is not enough for you, the Devel module comes with the kint() function that provides a better (traversable) variable inspection tool. This is the new Krumo.

Assets and libraries

The last thing we are going to cover here is the topic of assets (CSS and JavaScript files). A notable change is that Drupal 8 ships with jQuery 2.x alongside other modern frontend libraries like Modernizr, Backbone.js and Underscore.js. And if you ever had to work with jQuery in Drupal 7 you’ll understand why this is no small gain. Plus this means that IE8 and lower are no longer supported!

Additionally, Drupal 8 has adopted a SMACSS based CSS file organization and we have some [good architecture and best practices] (https://www.drupal.org/node/1887918#best-practices) in place as well. No more excuses for messy CSS in our theme!

One thing that trips people up in the beginning is that, for performance reasons, assets are no longer added indiscriminately to every page. So if that Ajax functionality you’re trying out doesn’t work, make sure Drupal has loaded the necessary scripts for it. You can do so by declaring them as dependencies to your own.

What’s great is that we now have a unified way of doing all of this across the entire development spectrum. We use libraries that contain both javascript and css files, which can have dependencies on other assets and which get #attached to render arrays. If you want them loaded on all pages, you can also add them to your theme’s info.yml file or implement hook_page_attachments() and add them like that. However, it is recommended to always attach libraries to render arrays to make sure your assets don’t get loaded unless they are really needed and that they are cached properly with the data they serve.

Conclusion

In this article we’ve looked at some of the more prominent changes to theming in Drupal 8. We’ve done so by taking a look at the starting point from which we create new themes and moved through some of the main topics related to this process. By no means, however, has this been a complete rundown of all the changes. I recommend keeping up to date with the documentation on Drupal.org (which is also constantly updating) and jump in the code yourself. It should be fun!

Jul 06 2015
Jul 06

In this article, we are going to look at building a multistep form in Drupal 8. For brevity, the form will have only two steps in the shape of two completely separate forms. To persist values across these steps, we will use functionality provided by Drupal’s core for storing temporary and private data across multiple requests.

Drupal 8 logo

In Drupal 7, a similar approach can be achieved using the cTools object cache. Alternatively, there is the option of persisting data through the $form_state array as illustrated in this tutorial.

The code we write in this article can be found in this repository alongside much of the Drupal 8 work we’ve been doing so far. We will be dealing with forms quite a lot so I do recommend checking out one of the previous articles on Drupal 8 in which we talk about forms.

The plan

As I mentioned above, our multistep form will consist of two independent forms with two simple elements each. Users will be able to fill in the first one and move to the second form where they can either go back to the previous step or fill it in and press submit. While navigating between the different steps, the previously submitted values are stored and used to pre-populate the form fields. If the last form is submitted, however, the data gets processed (not covered in this article) and cleared from the temporary storage.

Technically, both of these forms will inherit common functionality from an abstract form class we will call MultistepFormBase. This class will be in charge of injecting the necessary dependencies, scaffolding the form, processing the end result and anything else that is needed and is common to both.

We will group all the form classes together and place them inside a new folder called Multistep located within the Form plugin directory of our demo module (next to the old DemoForm). This is purely for having a clean structure and being able to quickly tell which forms are part of our multistep form process.

The code

We will start with the form base class. I will explain what is going on here after we see the code.

MultistepFormBase.php:

/**
 * @file
 * Contains \Drupal\demo\Form\Multistep\MultistepFormBase.
 */

namespace Drupal\demo\Form\Multistep;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\SessionManagerInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class MultistepFormBase extends FormBase {

  /**
   * @var \Drupal\user\PrivateTempStoreFactory
   */
  protected $tempStoreFactory;

  /**
   * @var \Drupal\Core\Session\SessionManagerInterface
   */
  private $sessionManager;

  /**
   * @var \Drupal\Core\Session\AccountInterface
   */
  private $currentUser;

  /**
   * @var \Drupal\user\PrivateTempStore
   */
  protected $store;

  /**
   * Constructs a \Drupal\demo\Form\Multistep\MultistepFormBase.
   *
   * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
   * @param \Drupal\Core\Session\AccountInterface $current_user
   */
  public function __construct(PrivateTempStoreFactory $temp_store_factory, SessionManagerInterface $session_manager, AccountInterface $current_user) {
    $this->tempStoreFactory = $temp_store_factory;
    $this->sessionManager = $session_manager;
    $this->currentUser = $current_user;

    $this->store = $this->tempStoreFactory->get('multistep_data');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('user.private_tempstore'),
      $container->get('session_manager'),
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Start a manual session for anonymous users.
    if ($this->currentUser->isAnonymous() && !isset($_SESSION['multistep_form_holds_session'])) {
      $_SESSION['multistep_form_holds_session'] = true;
      $this->sessionManager->start();
    }

    $form = array();
    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#button_type' => 'primary',
      '#weight' => 10,
    );

    return $form;
  }

  /**
   * Saves the data from the multistep form.
   */
  protected function saveData() {
    // Logic for saving data goes here...
    $this->deleteStore();
    drupal_set_message($this->t('The form has been saved.'));

  }

  /**
   * Helper method that removes all the keys from the store collection used for
   * the multistep form.
   */
  protected function deleteStore() {
    $keys = ['name', 'email', 'age', 'location'];
    foreach ($keys as $key) {
      $this->store->delete($key);
    }
  }
}

Our abstract form class extends from the default Drupal FormBase class so that we can use some of the functionality made available by it and the traits it uses. We are using dependency injection to inject some of the needed services:

  • PrivateTempStoreFactory gives us a temporary store that is private to the current user (PrivateTempStore). We will keep all the submitted data from the form steps in this store. In the constructor, we are also immediately saving the store attribute which contains a reference to the multistep_data key/value collection we will use for this process. The get() method on the factory either creates the store if it doesn’t exist or retrieves it from the storage.
  • The SessionManager allows us to start a session for anonymous users.
  • The CurrentUser allows us to check if the current user is anonymous.

Inside the buildForm() method we do two main things. First, we start a session for anonymous users if one does’t already exist. This is because without a session we cannot pass around temporary data across multiple requests. We use the session manager for this. Second, we create a base submit action button that will be present on all the implementing forms.

The saveData() method is going to be called from one or more of the implementing forms and is responsible with persisting the data from the temporary storage once the multistep process is completed. We won’t be going into the details of this implementation because it depends entirely on your use case (e.g. you can create a configuration entity from each submission). We do, however, handle the removal of all the items in the store once the data has been persisted. Keep in mind though that these types of logic checks should not be performed in the base class. You should defer to a dedicated service class as usual, or use a similar approach.

Now it’s time for the actual forms that will represent steps in the process. We start with the first class inside a file called MultistepOneForm.php:

/**
 * @file
 * Contains \Drupal\demo\Form\Multistep\MultistepOneForm.
 */

namespace Drupal\demo\Form\Multistep;

use Drupal\Core\Form\FormStateInterface;

class MultistepOneForm extends MultistepFormBase {

  /**
   * {@inheritdoc}.
   */
  public function getFormId() {
    return 'multistep_form_one';
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form = parent::buildForm($form, $form_state);

    $form['name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Your name'),
      '#default_value' => $this->store->get('name') ? $this->store->get('name') : '',
    );

    $form['email'] = array(
      '#type' => 'email',
      '#title' => $this->t('Your email address'),
      '#default_value' => $this->store->get('email') ? $this->store->get('email') : '',
    );

    $form['actions']['submit']['#value'] = $this->t('Next');
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->store->set('email', $form_state->getValue('email'));
    $this->store->set('name', $form_state->getValue('name'));
    $form_state->setRedirect('demo.multistep_two');
  }
}

This form will look something like this:

Drupal 8 Multistep Forms 1

In the buildForm() method we are defining our two dummy form elements. Do notice that we are retrieving the existing form definition from the parent class first. The default values for these fields are set as the values found in the store for those keys (so that users can see the values they filled in at this step if they come back to it). Finally, we are changing the value of the action button to Next (to indicate that this form is not the final one).

In the submitForm() method we save the submitted values to the store and then redirect to the second form (which can be found at the route demo.multistep_two). Keep in mind that we are not doing any sort of validation here to keep the code light. But most use cases will call for some input validation.

Since we’ve touched upon the issue of routes, let’s update the route file in our demo module and create two new routes for our forms:

demo.routing.yml:

demo.multistep_one:
  path: '/demo/multistep-one'
  defaults:
    _form: '\Drupal\demo\Form\Multistep\MultistepOneForm'
    _title: 'First form'
  requirements:
    _permission: 'access content'
demo.multistep_two:
  path: '/demo/multistep-two'
  defaults:
    _form: '\Drupal\demo\Form\Multistep\MultistepTwoForm'
    _title: 'Second form'
  requirements:
    _permission: 'access content'

For more information about what is going on in this file you can read one of the previous Drupal 8 articles which explain routes as well.

Finally, we can create our second form (inside a file called MultistepTwoForm):

/**
 * @file
 * Contains \Drupal\demo\Form\Multistep\MultistepTwoForm.
 */

namespace Drupal\demo\Form\Multistep;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;

class MultistepTwoForm extends MultistepFormBase {

  /**
   * {@inheritdoc}.
   */
  public function getFormId() {
    return 'multistep_form_two';
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form = parent::buildForm($form, $form_state);

    $form['age'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Your age'),
      '#default_value' => $this->store->get('age') ? $this->store->get('age') : '',
    );

    $form['location'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Your location'),
      '#default_value' => $this->store->get('location') ? $this->store->get('location') : '',
    );

    $form['actions']['previous'] = array(
      '#type' => 'link',
      '#title' => $this->t('Previous'),
      '#attributes' => array(
        'class' => array('button'),
      ),
      '#weight' => 0,
      '#url' => Url::fromRoute('demo.multistep_one'),
    );

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->store->set('age', $form_state->getValue('age'));
    $this->store->set('location', $form_state->getValue('location'));

    // Save the data
    parent::saveData();
    $form_state->setRedirect('some_route');
  }
}

This one will look like this, again very simple:

Drupal 8 Multistep Forms 1

Again, we are extending from our base class like we did with the first form. This time, however, we have different form elements and we are adding a new action link next to the submit button. This will allow users to navigate back to the first step of the form process.

Inside the submitForm() method we again save the values to the store and defer to the parent class to persist this data in any way it sees fit. We then redirect to whatever page we want (the route we use here is a dummy one).

And that is pretty much it. We should now have a working multistep form that uses the PrivateTempStore to keep data available across multiple requests. If we need more steps, all we have to do is create some more forms, add them in between the existing ones and make a couple of adjustments. Of course you can make this much more flexible by not hardcoding the route names in the links and redirects, but I leave that part up to you.

Conclusion

In this article, we looked at a simple way to create a multistep form in Drupal 8. You can, of course, build on this approach and create highly complex and dynamic processes that involve not just forms but also other kinds of steps that leverage cross-request data. Thus, the purpose of this article has been as much about multistep forms as it has been about illustrating the power of the PrivateTempStore. And if you, like me, think that the cTools object cache is very powerful in Drupal 7, you’ll be very invested in its Drupal 8 counterpart.

Jun 10 2015
Jun 10

One of the things that makes Drupal great is its flexible user permission system. The out of the box permissions grid we are all familiar with covers most uses cases of controlling what users can and cannot do. It is also very easy for module developers to create new permissions and roles that restrict the logic they implement.

Drupal logo

Nevertheless, I have encountered a practical use case where the default configuration options are not enough. Namely, if you need to have multiple users with access to edit a particular node of a given type but without them necessarily having access to edit others of the same type. In other words, the next great article should be editable by Laura and Glenn but not by their colleagues. However, out of the box, users of a particular role can be masters either of their own content or of all content of a certain type. So this is not immediately possible.

In this article I am going to show you my solution to this problem in the form of a simple custom module called editor_list. Article nodes will have a field where you can select users and only these users (or those who have full access) will be able to edit that particular node. You can find the module already in this git repository and you can install it on your site for a quick start. Do keep in mind that it has a dependency on the Entity Reference module as we will see in a minute.

I will keep the code comments to a minimum to save space but you can find them in the repository if you want. Basic knowledge of Drupal 7 is assumed in the remainder of this tutorial.

Scaffolding

We first need the editor_list.info file for our module to get us going:

name = Editor List
description = Module illustrating a custom solution for having multiple editors on a node.
core = 7.x
dependencies[] = entityreference

Next, we need our editor_list.module file where most of our business logic will be located. So go ahead and create it and we will populate it as we go on.

Finally, though not covered here, we can have an editor_list.install file where we can implement hook_install() and hook_update hooks to create fields and/or deploy configuration. In the repository, you’ll find that I provided an install hook that already creates an entity reference field called field_editors and attaches it to the Article content type. If you are following along but not using the code in the repository, you should go ahead and create the field manually through the UI. It’s a simple field that references User entities and allows for unlimited selections. Nothing major.

Node access

Going back to our .module file, it’s time to implement our access logic. First though, to make things as flexible and reusable as possible, let’s have a simple function that returns an array of node types to which we apply our access logic:

function editor_list_node_types() {
  return array('article');
}

Since we are only targeting articles, this will suffice. But we will use this function in multiple places so in case we need to target other types as well, we just have to update this array.

Next, let’s write another helpful function that returns all the user IDs set in the editors field of a given node. We will also use this in multiple places:

function editor_list_uids_from_list($node) {
  $users = field_get_items('node', $node, 'field_editors');

  $allowed_uids = array();
  if ($users) {
    $allowed_uids = array_map(function($user) {
      return $user['target_id'];
    }, $users);
  }

  return $allowed_uids;
}

I believe the function is quite self explanatory so I won’t go into details here. Instead, we can turn to our hook_node_access() implementation that gets called by Drupal whenever a user tries to do something with a node (view, edit or delete):

/**
 * Implements hook_node_access().
 */
function editor_list_node_access($node, $op, $account) {
  $node_types = editor_list_node_types();

  if ( ! is_object($node) || ! in_array($node->type, $node_types) || $op !== 'update') {
    return NODE_ACCESS_IGNORE;
  }

  $allowed_uids = editor_list_uids_from_list($node);

  if (empty($allowed_uids)) {
    return NODE_ACCESS_IGNORE;
  }

  if (in_array($account->uid, $allowed_uids)) {
    return NODE_ACCESS_ALLOW;
  }
}

So what’s happening here?

First, we use our previously declared helper function to get the list of node types we want to target, and we basically ignore the situation and return if the node type of the currently accessed node is not within our list or if the operation the user is attempting is not of the type “update”. Then we use our other helper function to check if there are any users in the editor list for this node and again ignore the situation if there aren’t. However, if there are, and our accessing user is among them, we return the NODE_ACCESS_ALLOW constant which basically gives the user access to perform the attempted operation. And that’s it.

You can check out the documentation for more information about how this hook works.

Let’s say you have admin users who can create and edit any type of content and regular authenticated users who cannot edit articles (apart from maybe the ones they created themselves). Adding one of these latter users to a node’s editor list would give them access to that particular node. And another great thing is that since this is all nicely integrated, contextual filters and tabs also take these dynamic permissions into account.

Field access

We now have a working module that does what I initially set out for it to do. But let’s say that your admin users are the only ones responsible for adding users to the editor lists. In other words, you are afraid that if your editors can edit their nodes and remove themselves from the list, they’ll get locked out of the node they are supposed to work on.

To account for this situation, we need to implement a field access check and remove the possibility that editors tamper with that field. Implementing hook_field_access should do the trick nicely. And if you are wondering, this hook is similar to hook_node_access() but is responsible for individual fields rather than the entire node (+ a couple of other small differences).

/**
 * Implements hook_field_access().
 */
function editor_list_field_access($op, $field, $entity_type, $entity, $account) {
  $node_types = editor_list_node_types();
  if ($entity_type === 'node' && is_object($entity) && in_array($entity->type, $node_types)) {
    return editor_list_control_field_access($op, $field, $entity_type, $entity, $account);
  }
}

And here we have it. There are a few more parameters because this hook gets called for all entities, not just nodes. But again, we check if the currently accessed node is one of those we defined earlier (and that the entity is in fact a node) and this time delegate to another function to keep things tidier:

function editor_list_control_field_access($op, $field, $entity_type, $entity, $account) {
  if ($op !== 'edit') {
    return;
  }

  $uids = editor_list_uids_from_list($entity);
  if (!in_array($account->uid, $uids)) {
    return;
  }

  $deny = array('field_editors');
  if (in_array($field['field_name'], $deny)) {
    return false;
  }
}}

Since we only care if the user is trying to update a particular field, we return nothing if this is not the case. Keep in mind that the op string here is edit and not update as it was in the other hook. This is just one of those Drupal quirks of inconsistency we all came to love so much. And like before, we ignore the situation if the current user is not part of the editor list.

Then, we define an array of field names we want to deny access to (in our case only one but we can add to it depending on the use case). Finally, we return false if the currently accessed field is part of our $deny array. Yet another difference here in that we have to return a boolean instead of a constant like we did before.

Now the editors in the list of a given node cannot remove themselves or add anybody else to the list. But then again, in some cases you may want this functionality and in others not. It’s up to you.

Tidying up

The last thing I am going to show you here relates to organization and maybe a bit of user experience. With our current implementation, the editor list field on the Article nodes is present somewhere on the form (wherever you dragged-and-dropped it when editing the field settings). However, wouldn’t it be nice if it were automatically part of the Authoring information group at the bottom of the page? Something like this:

Drupal 7 multiple editors per node

I think so. Let’s see how we can do that.

First, we need to implement hook_form_alter or one of its variations. I prefer the most targeted one to avoid unnecessary calls to it and a bunch of conditional checks:

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function editor_list_form_article_node_form_alter(&$form, &$form_state, $form_id) {
  $form['#after_build'][] = 'editor_list_node_form_after_build';
}

We went with the BASE_FORM_ID of the article nodes here so if we extend our application to other types we would do the same for those as well. Inside, we just define an #after_build function to be triggered when the form has finished building. This is to ensure all the form alterations have been already done by contributed modules. All that is left to be done is to write the function responsible for making changes to the form:

function editor_list_node_form_after_build($form, &$form_state) {
  $field = field_info_field('field_editors');
  if ( ! field_access('edit', $field, 'node', $form['#entity'])) {
    return $form;
  }

  if ($form['author']['#access'] === 0) {
    return $form;
  }

  $field_editors = $form['field_editors'];
  $field_editors['#weight'] = 0;
  $form['author']['additional_authors'] = $field_editors;
  $form['field_editors'] = array();

  return $form;
}

This looks complicated but it really isn’t. We begin by loading the field definition of our editor list field. This is so that we can run the field_access check on it and just return the form array unchanged if the current user doesn’t have access to the field. Next, we do the same if the current user does not have access to the author group on the form (this is the Authoring information group we want to place the field into). And lastly, we make a copy of the field definition, change its weight and place it into the group, followed by unsetting the original definition to avoid having duplicates.

And that is pretty much it. Now the editors list field should be tucked in with the rest of the information related to authorship.

Conclusion

In this article, we created a solution to a content editing problem that Drupal 7 could not fix out of the box. However, it did provide us with the development tools necessary to make this an easy task inside of a custom module.

We now have an editor list field on the article node form by which we can specify exactly which users have access to that particular node. Though do keep in mind that in order for this to be of any use, the users you add to these lists must not have a role that allows them to edit all article nodes. Otherwise you won’t see much of a difference.

Jun 02 2015
Jun 02

In this article we are going to look at how we can create and use pseudo-fields in Drupal 8.

What are pseudo-fields?

Pseudo-fields are simple display fields that you can control from the display settings of a particular entity type. An example of such a field is the core Links field on the node or comment entities.

Why are they useful?

Pseudo-fields are great if you have some content or data that you need to render together with that of a particular entity. And since you don't want to be hacky and hardcode all of this inside a template or preprocessor, you can use pseudo-fields to expose some control over this to the UI. This means that you can use the core drag-and-drop functionality to control their visibility and position relative to regular entity fields.

Pseudo-fields are a great way to display data that is tightly coupled to the entities but is not part of them or their fields.

So how do they work?

There are 2 main steps we need to take in order to create a pseudo-field. First, we need to implement hook_entity_extra_field_info() which is similar to Drupal 7.

/**
 * Implements hook_entity_extra_field_info().
 */
function my_module_entity_extra_field_info() {
  $extra = array();

  foreach (NodeType::loadMultiple() as $bundle) {
    $extra['node'][$bundle->Id()]['display']['my_own_pseudo_field'] = array(
      'label' => t('My own field'),
      'description' => t('This is my own pseudo-field'),
      'weight' => 100,
      'visible' => TRUE,
    );
  }

  return $extra;
}

We mustn't forget to use the NodeType class at the top of the file:

use Drupal\node\Entity\NodeType;

With this implementation we are creating one pseudo-field called My own field that will show up on the display settings of all the node bundles. After clearing the cache, you can already see it if you go to the display settings of any node bundle you have.

drupal 8 pseudo fields

The second step is making this field actually render something when the node is being viewed. For this we need to implement hook_entity_view() or any of its variants:

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function my_module_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
  if ($display->getComponent('my_own_pseudo_field')) {
      $build['my_own_pseudo_field'] = [
        '#type' => 'markup',
        '#markup' => 'This is my custom content', 
    ];
  }
}

And again, let's use the necessary classes at the top:

use \Drupal\Core\Entity\EntityInterface;
use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;

Above we went with the hook_ENTITY_TYPE_view() variant that applies to the node entity. Inside, we are being passed an EntityViewDisplayInterface display object which we can use to check whether or not our own component (the one we defined earlier) exists in this display. If it does, we add our custom data to the $build render array that is passed by reference. This check allows the user interface to determine the visibility of this component on each view mode, as well as the weight (position relative to the other components which can be either entity fields or other such pseudo-fields).

And that's about it. You can save, edit the display settings of your nodes, create view modes and specify exactly for which one and where this custom content should show up.

Hope this helps.

May 25 2015
May 25

I was working on a big website with many contrib and custom modules. And I had to debug a very annoying redirect that started happening sometime in the recent past, not sure when. Some pages simply just redirected to other URLs.

I figured out that the problem was one of a 301 redirect. My browser told me that at least. But good luck figuring out where in the code I can find the culprit. Xdebug breakpoints everywhere but to no avail. A search for drupal_goto in the custom modules directory didn't help either, and God be with anyone trying to search through a contrib folder of that size.

Then it hit me. Isn't there a hook invoked inside drupal_goto? At this point I was assuming (and hoping really) that the redirect was happening somehow with a drupal_goto. And it turns out there is one: hook_drupal_goto_alter.

Armed with a new dose of hope, I implemented the hook and cleared the cache. Inside, I added the obligatory $test = ''; statement and put a breakpoint on it. Let's see what happens. After loading one of the offending pages, the breakpoint halted the execution and the Xdebug call stack in my PHPStorm immediately pointed out the problem: Global Redirect. There was some URL rewriting happening on the site so GR got a bit confused and was redirecting back to the original path. The details of the issue are however not important.

My point is that using this hook, I could see exactly who and why was calling drupal_goto. I didn't use it for anything else, apart from learning why the redirect is happening which in turn allowed me to write some code that prevented that.

Awesome. I learned about a new hook. And maybe now you as well.

May 18 2015
May 18

In a previous article I've shown you how you can add new html elements to the <head> of your Drupal 7 site. Recently, however, I was working on a Drupal 8 project and encountered the need to do this in D8. And it took me a while to figure it out so I thought I'd share the process with you.

As you know, in Drupal 7 we use drupal_add_html_head() from anywhere in the code to add a rendered element into the <head>. This is done by passing a render array and most of the time you'll use the type #tag. In Drupal 8, however, we no longer have this procedural function so it can be a bit tricky to find out how this is done.

Although existing in Drupal 7 as well, the #attached key in render arrays really becomes important in D8. We can no longer add any scripts or stylesheets to any page without such proper attachment to render arrays. In my last article I've shown you how to add core scripts to pages in case they were missing (which can happen for anonymous users). In essence, it is all about libraries now that get attached to render arrays. So that is most of what you'll hear about.

But libraries are not the only thing you can attach to render arrays. You can also add elements to the head of the page in a similar way you'd attach libraries. So if we wanted to add a description meta tag to all of the pages on our site, we could implement hook_page_attachments() like so:

/**
 * Implements hook_page_attachments().
 */
function module_name_page_attachments(array &$page) {
  $description = [
    '#tag' => 'meta',
    '#attributes' => [
      'name' => 'description',
      'content' => 'This is my website.',
    ],
  ];
  $page['#attached']['html_head'][] = [$description, 'description'];
}

In the example above we are just adding a dummy description meta tag to all the pages. You probably won't want to apply that to all the pages though and rather have the content of the description tag read the title of the current node. In this case you can implement hook_entity_view() like so:

/**
 * Implements hook_entity_view().
 */
function demo_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) {
  if ($entity->getEntityTypeId() !== 'node') {
    return;
  }

  $description = [
    '#tag' => 'meta',
    '#attributes' => [
      'name' => 'description',
      'content' => \Drupal\Component\Utility\SafeMarkup::checkPlain($entity->title->value),
    ],
  ];
  $build['#attached']['html_head'][] = [$description, 'description'];
}

Now you targeting the node entities and using their titles as the content for the description meta tag. And that is pretty much it.

Hope this helps.

May 15 2015
May 15

In this article, I am going to show you a clean way of using the Drupal 8 Ajax API without writing one line of JavaScript code. To this end, we will go back to the first custom form we built for Drupal 8 in a previous article and Ajaxify some of its behaviour to make it more user friendly.

Drupal 8 logo

An updated version of this form can be found in this repository under the name DemoForm (the demo module). The code we write in this article can also be found there but in a separate branch called ajax. I recommend you clone the repo and install the module in your development environment if you want to follow along.

DemoForm

Although poorly named, the DemoForm was very helpful in illustrating the basics of writing a custom form in Drupal 8. It handles validation, configuration and exemplifies the use of the Form API in general. Of course, it focuses on the basics and has nothing spectacular going on.

If you remember, or check the code, you’ll see that the form presents a single textfield responsible for collecting an email address to be saved as configuration. The form validation is in charge of making sure that the submitted email has a .com ending (a poor attempt at that but enough to illustrate the principle of form validation). So when a user submits the form, they are saving a new email address to the configuration and get a confirmation message printed to the screen.

In this article, we will move the email validation logic to an Ajax callback so that after the user has finished typing the email address, the validation gets automagically triggered and a message printed without submitting the form. Again, there is nothing spectacular about this behaviour and you will see it quite often in forms in the wild (typically to validate usernames). But it’s a good exercise for looking at Ajax in Drupal 8.

Ajax form

The first thing we need to do is move the email validation logic from the general validateForm() to a method that handles only this aspect:

/**
 * Validates that the email field is correct.
 */
protected function validateEmail(array &$form, FormStateInterface $form_state) {
  if (substr($form_state->getValue('email'), -4) !== '.com') {
    return FALSE;
  }
  return TRUE;
}

As you can notice, we’ve also changed the logic a bit to make sure the email address ends with a .com.

Then, we can defer to this logic from the main validation method to make sure our existing behaviour still works:

/**
 * {@inheritdoc}
 */
public function validateForm(array &$form, FormStateInterface $form_state) {
  // Validate email.
  if (!$this->validateEmail($form, $form_state)) {
    $form_state->setErrorByName('email', $this->t('This is not a .com email address.'));
  }
}

This way even if our form gets somehow submitted (programatically or otherwise), the validation will still be run.

Next, we need to turn to our form definition, specifically the email field, and make it trigger ajax requests based on a user interaction. This will be the act of a user changing the value of the field and removing focus from it:

$form['email'] = array(
  '#type' => 'email',
  '#title' => $this->t('Your .com email address.'),
  '#default_value' => $config->get('demo.email_address'),
  '#ajax' => [
    'callback' => array($this, 'validateEmailAjax'),
    'event' => 'change',
    'progress' => array(
      'type' => 'throbber',
      'message' => t('Verifying email...'),
    ),
  ],
  '#suffix' => '<span class="email-valid-message"></span>'
);

What we did new here is add the #ajax key to the array with some of the relevant keys. Additionally, we added a little markup after the form element as a wrapper for a short message regarding the validity of the email.

The callback inside the #ajax array points to a method inside our form class (validateEmailAjax()) while the event adds a javascript binding to this form element for the jQuery change event. Alternatively, you can also specify a path key instead of a callback, but in our case it would mean having to also set up a route and a controller which seems redundant. And we don’t want the wrapper key because we do not intend to fill up an area with returned content but want to fine grain the actions that result from the callback. For that, we will use Ajax commands.

To learn more about all of this, I encourage you to consult the Ajax API page or the Form API entry for Ajax. There are a handful of other options you can use to further customize the Ajax behavior of your form elements.

Now it’s time to write the callback method inside of our form class. This receives the $form array and $form_state object as arguments coming from the form that triggered the Ajax request:

/**
 * Ajax callback to validate the email field.
 */
public function validateEmailAjax(array &$form, FormStateInterface $form_state) {
  $valid = $this->validateEmail($form, $form_state);
  $response = new AjaxResponse();
  if ($valid) {
    $css = ['border' => '1px solid green'];
    $message = $this->t('Email ok.');
  }
  else {
    $css = ['border' => '1px solid red'];
    $message = $this->t('Email not valid.');
  }
  $response->addCommand(new CssCommand('#edit-email', $css));
  $response->addCommand(new HtmlCommand('.email-valid-message', $message));
  return $response;
}

Simply put, in this method, we perform the validation and return an Ajax response with multiple commands that differ depending on the validation result. With the CssCommand we apply some css directly to the email form element while with the HtmlCommand we replace the contents of the specified selector (remember the suffix from our form element?).

These commands pretty much map to jQuery functions so they are quite easy to grasp. You can find a list of all available commands on this page. And since we are using three new classes inside this method, we must remember to also use them at the top:

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CssCommand;
use Drupal\Core\Ajax\HtmlCommand;

And that is pretty much it. If you clear the cache and reload your form, typing into the email field and removing focus will trigger the callback to validate the email address. You’ll notice the little throbber icon there (which can be changed in the definition) and the short message we defined as well. A correct email address should highlight the field in green and print the OK message while on the contrary the color red is used with an opposite message.

If we had specified a wrapper in the form element definition, we could have returned some content (or render array) which would have been placed inside that selector. So you have the option of choosing between returning content or Ajax commands but I recommend the latter for most cases because they offer a more flexible (and consistent) behavior.

Conclusion

In this article we’ve seen an example of using Ajax to improve our form and make it more friendly to end users. And we have written exactly zero lines of javascript to accomplish this.

In our case, it really is a matter of preference or fancification. But if you are dealing with a 20 field form which has validation on multiple fields similar to this, using Ajax really makes sense. It doesn’t annoy users with having to submit the form only to realize their input is invalid.

Although forms are the main area where you’ll see Ajax in Drupal 8, there are a couple of other ways you can leverage it without writing JavaScript.

Once nice way is to add the use-ajax class on any link. This will have Drupal make an Ajax request to the URL in the href attribute whenever the link is clicked. From the callback you can return Ajax commands and perform various actions as needed. But do keep in mind that jQuery and other core scripts are not loaded on all pages for anonymous users (hence Ajax will gracefully degrade to regular link behaviour). So make sure you include these scripts for anonymous users if you need this behavior.

May 01 2015
May 01

In this article we are going to look at automated testing in Drupal 8. More specifically, we are going to write a few integration tests for some of the business logic we wrote in the previous Sitepoint articles on Drupal 8 module development. You can find the latest version of that code in this repository along with the tests we write today.

Drupal 8 logo

But before doing that, we will talk a bit about what kinds of tests we can write in Drupal 8 and how they actually work.

Simpletest (Testing)

Simpletest is the Drupal specific testing framework. For Drupal 6 it was a contributed module but since Drupal 7 it has been part of the core package. Simpletest is now an integral part of Drupal core development, allowing for safe API modifications due to an extensive codebase test coverage.

Right off the bat I will mention the authoritative documentation page for Drupal testing with Simpletest. There you can find a hub of information related to how Simpletest works, how you can write tests for it, what API methods you can use, etc.

By default, the Simpletest module that comes with Drupal core is not enabled so we will have to do that ourselves if we want to run tests. It can be found on the Extend page named as Testing.

Once that is done, we can head to admin/config/development/testing and see all the tests currently available for the site. These include both core and contrib module tests. At the very bottom, there is also the Clean environment button that we can use if any of our tests quit unexpectedly and there are some remaining test tables in your database.

How does Simpletest work?

When we run a test written for Simpletest, the latter uses the existing codebase and instructions found in the test to create a separate Drupal environment in which the test can run. This means adding additional tables to the database (prefixed by simpletest_) and test data that are used to replicate the site instance.

Depending on the type of test we are running and what it contains, the nature of this replication can differ. In other words, the environment can have different data and core functionality depending on the test itself.

What kinds of tests are there in Drupal 8?

There are two main types of tests that we can write for Drupal 8: unit tests using PHPUnit (which is in core now) and functional tests (using Simpletest). However, the latter can also be split into two different kinds: web tests (which require web output) and kernel tests (which do not require web output). In this article we will practically cover only web tests because most of the functionality we wrote in the previous articles is manifested through output so that’s how we need to test it as well.

Writing any type of test starts by implementing a specific class and placing it inside the src/Tests folder of the module it tests. I also encourage you to read this documentation page that contains some more information on this topic as I do not want to duplicate it here.

Our tests

As I mentioned, in this article we will focus on providing test coverage for some of the business logic we created in the series on Drupal 8 module development. Although there is nothing complicated happening there, the demo module we built offers a good example for starting out our testing process as well. So let’s get started by first determining what we will test.

By looking at the demo module, we can delineate the following aspects we can test:

That’s pretty much it. The custom menu link we defined inside the demo.links.menu.yml could also be tested but that should already work out of the box so I prefer not to.

For the sake of brevity and the fact that we don’t have too much we need to test, I will include all of our testing methods into one single class. However, you should probably group yours into multiple classes depending on what they are actually responsible for.

Inside a file called DemoTest.php located in the src/Tests/ folder, we can start by adding the following:

<?php

namespace Drupal\demo\Tests;

use Drupal\simpletest\WebTestBase;

/**
 * Tests the Drupal 8 demo module functionality
 *
 * @group demo
 */
class DemoTest extends WebTestBase {

  /**
   * Modules to install.
   *
   * @var array
   */
  public static $modules = array('demo', 'node', 'block');

  /**
   * A simple user with 'access content' permission
   */
  private $user;

  /**
   * Perform any initial set up tasks that run before every test method
   */
  public function setUp() {
    parent::setUp();
    $this->user = $this->drupalCreateUser(array('access content'));
  }
}

Here we have a simple test class which for every test it runs, will enable the modules in the $modules property and create a new user stored inside the $user property (by virtue of running the setUp() method).

For our purposes, we need to enable the demo module because that is what we are testing, the block module because we have a custom block plugin we need to test and the node module because our logic uses the access content permission defined by this module. Additionally, the user is created just so we can make sure this permission is respected.

For the three bullet points we identified above, we will now create three test methods. Keep in mind that each needs to start with the prefix test in order for Simpletest to run them automatically.

Testing the page

We can start by testing the custom page callback:

/**
 * Tests that the 'demo/' path returns the right content
 */
public function testCustomPageExists() {
  $this->drupalLogin($this->user);

  $this->drupalGet('demo');
  $this->assertResponse(200);

  $demo_service = \Drupal::service('demo.demo_service');
  $this->assertText(sprintf('Hello %s!', $demo_service->getDemoValue()), 'Correct message is shown.');
}

And here is the code that does it.

First, we log in with the user we created in the setUp() method and then navigate to the demo path. Simpletest handles this navigation using its own internal browser. Next, we assert that the response of the last accessed page is 200. This validates that the page exists. However, this is not enough because we need to make sure the text rendered on the page is the one loaded from our service.

For this, we statically access the \Drupal class and load our service. Then we assert that the page outputs the hello message composed of the hardcoded string and the return value of the service’s getDemoValue() method. It’s probably a good idea to write a unit test for whatever logic happens inside the service but for our case this would be quite redundant.

And that’s it with the page related logic. We can go to the testing page on our site, find the newly created DemoTest and run it. If all is well, we should have all green and no fails.

drupal 8 automatated tests

Testing the form

For the form we have another method, albeit more meaty, that tests all the necessary logic:

/**
 * Tests the custom form
 */
public function testCustomFormWorks() {
  $this->drupalLogin($this->user);
  $this->drupalGet('demo/form');
  $this->assertResponse(200);

  $config = $this->config('demo.settings');
  $this->assertFieldByName('email', $config->get('demo.email_address'), 'The field was found with the correct value.');

  $this->drupalPostForm(NULL, array(
    'email' => '[email protected]'
  ), t('Save configuration'));
  $this->assertText('The configuration options have been saved.', 'The form was saved correctly.');

  $this->drupalGet('demo/form');
  $this->assertResponse(200);
  $this->assertFieldByName('email', '[email protected]', 'The field was found with the correct value.');

  $this->drupalPostForm('demo/form', array(
    'email' => '[email protected]'
  ), t('Save configuration'));
  $this->assertText('This is not a .com email address.', 'The form validation correctly failed.');

  $this->drupalGet('demo/form');
  $this->assertResponse(200);
  $this->assertNoFieldByName('email', '[email protected]', 'The field was found with the correct value.');
}

The first step is like before. We go to the form page and assert a successful response. Next, we want to test that the email form element exists and that its default value is the value found inside the default module configuration. For this we use the assertFieldByName() assertion.

Another aspect we need to test is that saving the form with a correct email address does what it is supposed to: save the email to configuration. So we use the drupalPostForm() method on the parent class to submit the form with a correct email and assert that a successful status message is printed on the page as a result. This proves that the form saved successfully but not necessarily that the new email was saved. So we redo the step we did earlier but this time assert that the default value of the email field is the new email address.

Finally, we need to also test that the form doesn’t submit with an incorrect email address. We do so again in two steps: test a form validation failure when submitting the form and that loading the form again will not have the incorrect email as the default value of the email field.

Testing the block

/**
 * Tests the functionality of the Demo block
 */
public function testDemoBlock() {
  $user = $this->drupalCreateUser(array('access content', 'administer blocks'));
  $this->drupalLogin($user);

  $block = array();
  $block['id'] = 'demo_block';
  $block['settings[label]'] = $this->randomMachineName(8);
  $block['theme'] = $this->config('system.theme')->get('default');
  $block['region'] = 'header';
  $edit = array(
    'settings[label]' => $block['settings[label]'],
    'id' => $block['id'],
    'region' => $block['region']
  );
  $this->drupalPostForm('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], $edit, t('Save block'));
  $this->assertText(t('The block configuration has been saved.'), 'Demo block created.');

  $this->drupalGet('');
  $this->assertText('Hello to no one', 'Default text is printed by the block.');

  $edit = array('settings[demo_block_settings]' => 'Test name');
  $this->drupalPostForm('admin/structure/block/manage/' . $block['id'], $edit, t('Save block'));
  $this->assertText(t('The block configuration has been saved.'), 'Demo block saved.');

  $this->drupalGet('');
  $this->assertText('Hello Test name!', 'Configured text is printed by the block.');
}

For this test we need another user that also has the permission to administer blocks. Then we create a new instance of our custom demo_block with no value inside the Who field and assert that a successful confirmation message is printed as a result. Next, we navigate to the front page and assert that our block shows up and displays the correct text: Hello to no one.

Lastly, we edit the block and specify a Test name inside the Who field and assert that saving the block configuration resulted in the presence of a successful confirmation message. And we close off by navigating back to the home page to assert that the block renders the correct text.

Conclusion

In this article, we’ve seen how simple it is to write some basic integration tests for our Drupal 8 business logic. It involves creating one or multiple class files which simply make use of a large collection of API methods and assertions to test the correct behavior of our code. I strongly recommend you give this a try and start testing your custom code early as possible in order to make it more stable and less prone to being broken later on when changes are made.

Additionally, don’t let yourself get discouraged by the slow process of writing tests. This is mostly only in the beginning until you are used to the APIs and you become as fluent as you are with the actual logic you are testing. I feel it’s important to also mention that this article presented a very high level overview of the testing ecosystem in Drupal 8 as well as kept the tests quite simple. I recommend a more in depth look into the topic going forward.

Apr 20 2015
Apr 20

Drupal 8 comes with many performance improvements, one of which being that javascript is no longer indiscriminately loaded on every page. This means that for anonymous users, there are many pages where there is no jQuery or even javascript loaded.

Although this is a good thing, sometimes you do need jQuery (for example to use Ajax, in which case you'd also need other scripts). So how do you load these files? One way is to use hook_page_attachments() to #attach your own library to the page. However, this is only recommended if the assets you need to attach do not apply to something specific, but to the entire page. The recommended way is to attach the library to a render array because then it will only get loaded if necessary.

And if we look at the documentation page for assets, we'll see how we can add our own library. We need to create a my_module.libraries.yml or my_theme.libraries.yml file. And inside, we can add the following:

my_scripts:
  version: VERSION
  js:
    js/scripts.js: {}
  dependencies:
    - core/jquery
    - core/drupal.ajax
    - core/drupal
    - core/drupalSettings
    - core/jquery.once

Where my_scripts will be the name of the library we will reference when attaching.

As you can see, we are not including any javascript or css of our own, we are just making use of the dependency scripts provided by core. So then what is left to do is to attach this library to a render array (or to the entire page if that is your use case):

$render_array['#attached']['library'][] = 'my_module/my_scripts';

Hope this helps.

Apr 10 2015
Apr 10

A Silex and Elasticsearch app powered by Drupal 7 for content management

In the previous article I started exploring the integration between Drupal 7 and the Elasticsearch engine. The goal was to see how we can combine these open source technologies to achieve a high performance application that uses the best of both worlds. If you’re just now joining us, you should check out this repository which contains relevant code for these articles.

esdrupalsilex

We’ll now create a small Silex application that reads data straight from Elasticsearch and returns it to the user.

Silex app

Silex is a great PHP micro framework developed by the same people that are behind the Symfony project. It is in fact using mainly Symfony components but at a more simplified level. Let’s see how we can get started really quickly with a Silex app.

There is more than one way. You can add it as a dependency to an existent composer based project:

"silex/silex": "~1.2",

Or you can even create a new project using a nice little skeleton provided by the creator:

composer.phar create-project fabpot/silex-skeleton

Regardless of how your project is set up, in order to access Elasticsearch we’ll need to use its PHP SDK. That needs to be added to Composer:

"elasticsearch/elasticsearch": "~1.0",

And if we want to use Twig to output data, we’ll need this as well (if not already there of course):

"symfony/twig-bridge": "~2.3"

In order to use the SDK, we can expose it as a service to Pimple, the tiny Silex dependency injection container (much easier than it sounds). Depending on how our project is set up, we can do this in a number of places (see the repository for an example). But basically, after we instantiate the new Silex application, we can add the following:

$app['elasticsearch'] = function() {
  return new Client(array());
};

This creates a new service called elasticsearch on our app that instantiates an object of the Elasticsearch Client class. And don’t forget we need to use that class at the top:

use Elasticsearch\Client;

Now, wherever we want, we can get the Elasticsearch client by simply referring to that property in the $app object:

$client = $app['elasticsearch'];

Connecting to Elasticsearch

In the previous article we’ve managed to get our node data into the node index with each node type giving the name of an Elasticsearch document type. So for instance, this will return all the article node types:

http://localhost:9200/node/article/_search

We’ve also seen how to instantiate a client for our Elasticsearch SDK. Now it’s time to use it somehow. One way is to create a controller:

<?php

namespace Controller;

use Silex\Application;
use Symfony\Component\HttpFoundation\Response;

class NodeController {

  /**
   * Shows a listing of nodes.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   */
  public function index() {
    return new Response('Here there should be a listing of nodes...');
  }
  
  /**
   * Shows one node
   *
   * @param $nid
   * @param \Silex\Application $app
   * @return mixed
   */
  public function show($nid, Application $app) {
    $client = $app['elasticsearch'];
    $params = array(
      'index' => 'node',
      'body' => array(
        'query' => array(
          'match' => array(
            'nid' => $nid,
          ),
        ),
      )
    );
    
    $result = $client->search($params);
    if ($result && $result['hits']['total'] === 0) {
      $app->abort(404, sprintf('Node %s does not exist.', $nid));
    }
    
    if ($result['hits']['total'] === 1) {
      $node = $result['hits']['hits'];
      return $app['twig']->render('node.html.twig', array('node' => reset($node)));
    }
  }
}

Depending on how you organise your Silex application, there are a number of places this controller class can go. In my case it resides inside the src/Controller folder and it’s autoloaded by Composer.

We also need to create a route that maps to this Controller though. Again, there are a couple of different ways to handle this but in my example I have a routes.php file located inside the src/ folder and required inside index.php:

<?php

use Symfony\Component\HttpFoundation\Response;

/**
 * Error handler
 */
$app->error(function (\Exception $e, $code) {
  switch ($code) {
    case 404:
      $message = $e->getMessage();
      break;
    default:
      $message = 'We are sorry, but something went terribly wrong. ' . $e->getMessage();
  }

  return new Response($message);
});

/**
 * Route for /node
 */
$app->get("/node", "Controller\\NodeController::index");

/**
 * Route /node/{nid} where {nid} is a node id
 */
$app->get("/node/{nid}", "Controller\\NodeController::show");

So what happens in my example above? First, I defined an error handler for the application, just so I can see the exceptions being caught and print them on the screen. Not a big deal. Next, I defined two routes that map to my two controller methods defined before. But for the sake of brevity, I only exemplified what the prospective show() method might do:

  • Get the Elasticsearch client
  • Build the Elasticsearch query parameters (similar to what we did in the Drupal environment)
  • Perform the query
  • Check for the results and if a node was found, render it with a Twig template and pass the node data to it.
  • If no results are found, abort the process with a 404 that calls our error handler for this HTTP code declared above.

If you want to follow this example, keep in mind that to use Twig you’ll need to register it with your application. It’s not so difficult if you have it already in your vendor folder through composer.

After you instantiate the Silex app, you can register the provider:

$app->register(new TwigServiceProvider());

Make sure you use the class at the top:

use Silex\Provider\TwigServiceProvider;

And add it as a service with some basic configuration:

$app['twig'] = $app->share($app->extend('twig', function ($twig, $app) {
  return $twig;
}));
$app['twig.path'] = array(__DIR__.'/../templates');

Now you can create template files inside the templates/ folder of your application. For learning more about setting up a Silex application, I do encourage you to read this introduction to the framework.

To continue with our controller example though, here we have a couple of template files that output the node data.

Inside a page.html.twig file:

        <!DOCTYPE html>
        <html>
        <head>
            {% block head %}
                <title>{% block title %}{% endblock %} - My Elasticsearch Site</title>
            {% endblock %}
        </head>
        <body>
        <div id="content">{% block content %}{% endblock %}</div>
        </body>
        </html>

And inside the node.html.twig file we used in the controller for rendering:

{% extends "page.html.twig" %}

{% block title %}{{ node._source.title }}{% endblock %}

{% block content %}

    <article>
        <h1>{{ node._source.title }}</h1>

        <div id="content">

            {% if node._source.field_image %}
                <div class="field-image">
                    {% for image in node._source.field_image %}
                        <img src="http://www.sitepoint.com/integrate-elasticsearch-silex//{{ image.url }}" alt="img.alt"/>
                    {% endfor %}
                </div>
            {% endif %}

            {% if node._source.body %}
                <div class="field-body">
                    {% for body in node._source.body %}
                        {{ body.value|striptags('<p><div><br><img><a>')|raw }}
                    {% endfor %}
                </div>
            {% endif %}
        </div>
    </article>


{% endblock %}

This is just some basic templating for getting our node data printed in the browser (not so fun otherwise). We have a base file and one that extends it and outputs the node title, images and body text to the screen.

Alternatively, you can also return a JSON response from your controller with the help of the JsonResponse class:

use Symfony\Component\HttpFoundation\JsonResponse;

And from your controller simply return a new instance with the values passed to it:

return new JsonResponse($node);

You can easily build an API like this. But for now, this should already work. By pointing your browser to http://localhost/node/5 you should see data from Drupal’s node 5 (if you have it). With one big difference: it is much much faster. There is no bootstrapping, theming layer, database query etc. On the other hand, you don’t have anything useful either out of the box except for what you build yourself using Silex/Symfony components. This can be a good thing or a bad thing depending on the type of project you are working on. But the point is you have the option of drawing some lines for this integration and decide its extent.

One end of the spectrum could be building your entire front end with Twig or even Angular.js with Silex as the API backend. The other would be to use Silex/Elasticsearch for one Drupal page and use it only for better content search. Somewhere in the middle would probably be using such a solution for an entire section of a Drupal site that is dedicated to interacting with heavy data (like a video store or something). It’s up to you.

Conclusion

We’ve seen in this article how we can quickly set up a Silex app and use it to return some data from Elasticsearch. The goal was not so much to learn how any of these technologies work, but more of exploring the options for integrating them. The starting point was the Drupal website which can act as a perfect content management system that scales highly if built properly. Data managed there can be dumped into a high performance data store powered by Elasticsearch and retrieved again for the end users with the help of Silex, a lean and fast PHP framework.

Apr 03 2015
Apr 03

A Silex and Elasticsearch app powered by Drupal 7 for content management

In this tutorial I am going to look at the possibility of using Drupal 7 as a content management system that powers another high performance application. To illustrate the latter, I will use the Silex PHP microframework and Elasticsearch as the data source. The goal is to create a proof of concept, demonstrating using these three technologies together.

esdrupalsilex

The article comes with a git repository that you should check out, which contains more complete code than can be presented in the tutorial itself. Additionally, if you are unfamiliar with either of the three open source projects being used, I recommend following the links above and also checking out the documentation on their respective websites.

The tutorial will be split into two pieces, because there is quite a lot of ground to cover.

In this part, we’ll set up Elasticsearch on the server and integrate it with Drupal by creating a small, custom module that will insert, update, and delete Drupal nodes into Elasticsearch.

In the second part, we’ll create a small Silex app that fetches and displays the node data directly from Elasticsearch, completely bypassing the Drupal installation.

Elasticsearch

The first step is to install Elasticsearch on the server. Assuming you are using Linux, you can follow this guide and set it up to run when the server starts. There are a number of configuration options you can set here.

A very important thing to remember is that Elasticsearch has no access control so, once it is running on your server, it is publicly accessible through the (default) 9200 port. To avoid having problems, make sure that in the configuration file you uncomment this line:

network.bind_host: localhost

And add the following one:

script.disable_dynamic: true

These options make sure that Elasticsearch is not accessible from the outside, nor are dynamic scripts allowed. These are recommended security measures you need to take.

Drupal

The next step is to set up the Drupal site on the same server. Using the Elasticsearch Connector Drupal module, you can get some integration with the Elasticsearch instance: it comes with the PHP SDK for Elasticsearch, some statistics about the Elasticsearch instance and some other helpful submodules. I’ll leave it up to you to explore those at your leisure.

Once the connector module is enabled, in your custom module you can retrieve the Elasticsearch client object wrapper to access data:

$client = elastic_connector_get_client_by_id('my_cluster_id');

Here, my_cluster_id is the Drupal machine name that you gave to the Elasticsearch cluster (at admin/config/elasticsearch-connector/clusters). The $client object will now allow you to perform all sorts of operations, as illustrated in the docs I referenced above.

Inserting data

The first thing we need to do is make sure we insert some Drupal data into Elasticsearch. Sticking to nodes for now, we can write a hook_node_insert() implementation that will save every new node to Elasticsearch. Here’s an example, inside a custom module called elastic:

/**
 * Implements hook_node_insert().
 */
function elastic_node_insert($node) {
  $client = elasticsearch_connector_get_client_by_id('my_cluster_id');
  $params = _elastic_prepare_node($node);

  if ( ! $params) {
    drupal_set_message(t('There was a problem saving this node to Elasticsearch.'));
    return;
  }

  $result = $client->index($params);
  if ($result && $result['created'] === false) {
    drupal_set_message(t('There was a problem saving this node to Elasticsearch.'));
    return;
  }

  drupal_set_message(t('The node has been saved to Elasticsearch.'));
}

As you can see, we instantiate a client object that we use to index the data from the node. You may be wondering what _elastic_prepare_node() is:

/**
 * Prepares a node to be added to Elasticsearch
 *
 * @param $node
 * @return array
 */
function _elastic_prepare_node($node) {

  if ( ! is_object($node)) {
    return;
  }

  $params = array(
    'index' => 'node',
    'type' => $node->type,
    'body' => array(),
  );

  // Add the simple properties
  $wanted = array('vid', 'uid', 'title', 'log', 'status', 'comment', 'promote', 'sticky', 'nid', 'type', 'language', 'created', 'changed', 'revision_timestamp', 'revision_uid');
  $exist = array_filter($wanted, function($property) use($node) {
    return property_exists($node, $property);
  });
  foreach ($exist as $field) {
    $params['body'][$field] = $node->{$field};
  }

  // Add the body field if exists
  $body_field = isset($node->body) ? field_get_items('node', $node, 'body') : false;
  if ($body_field) {
    $params['body']['body'] = $body_field;
  }

  // Add the image field if exists
  $image_field = isset($node->field_image) ? field_get_items('node', $node, 'field_image') : false;
  if ($image_field) {
    $params['body']['field_image'] = array_map(function($img) {
      $img = file_load($img['fid']);
      $img->url = file_create_url($img->uri);
      return $img;
    }, $image_field);
  }

  return $params;
}

It is just a helper function I wrote, which is responsible for “serializing” the node data and getting it ready for insertion into Elasticsearch. This is just an example and definitely not a complete or fully scalable one. It is also assuming that the respective image field name is field_image. An important point to note is that we are inserting the nodes into the node index with a type = $node->type.

Updating data

Inserting is not enough, we need to make sure that node changes get reflected in Elasticsearch as well. We can do this with a hook_node_update() implementation:

/**
 * Implements hook_node_update().
 */
function elastic_node_update($node) {
  if ($node->is_new !== false) {
    return;
  }

  $client = elasticsearch_connector_get_client_by_id('my_cluster_id');
  $params = _elastic_prepare_node($node);

  if ( ! $params) {
    drupal_set_message(t('There was a problem updating this node in Elasticsearch.'));
    return;
  }

  $result = _elastic_perform_node_search_by_id($client, $node);
  if ($result && $result['hits']['total'] !== 1) {
    drupal_set_message(t('There was a problem updating this node in Elasticsearch.'));
    return;
  }

  $params['id'] = $result['hits']['hits'][0]['_id'];
  $version = $result['hits']['hits'][0]['_version'];
  $index = $client->index($params);

  if ($index['_version'] !== $version + 1) {
    drupal_set_message(t('There was a problem updating this node in Elasticsearch.'));
    return;
  }
  
  drupal_set_message(t('The node has been updated in Elasticsearch.'));
}

We again use the helper function to prepare our node for insertion, but this time we also search for the node in Elasticsearch to make sure we are updating and not creating a new one. This happens using another helper function I wrote as an example:

/**
 * Helper function that returns a node from Elasticsearch by its nid.
 *
 * @param $client
 * @param $node
 * @return mixed
 */
function _elastic_perform_node_search_by_id($client, $node) {
  $search = array(
    'index' => 'node',
    'type' => $node->type,
    'version' => true,
    'body' => array(
      'query' => array(
        'match' => array(
          'nid' => $node->nid,
        ),
      ),
    ),
  );

  return $client->search($search);
}

You’ll notice that I am asking Elasticsearch to return the document version as well. This is so that I can check if a document has been updated with my request.

Deleting data

The last (for now) feature we need is the ability to remove the data from Elasticsearch when a node gets deleted. hook_node_delete() can help us with that:

/**
 * Implements hook_node_delete().
 */
function elastic_node_delete($node) {
  $client = elasticsearch_connector_get_client_by_id('my_cluster_id');

  // If the node is in Elasticsearch, remove it
  $result = _elastic_perform_node_search_by_id($client, $node);
  if ($result && $result['hits']['total'] !== 1) {
    drupal_set_message(t('There was a problem deleting this node in Elasticsearch.'));
    return;
  }

  $params = array(
    'index' => 'node',
    'type' => $node->type,
    'id' => $result['hits']['hits'][0]['_id'],
  );

  $result = $client->delete($params);
  if ($result && $result['found'] !== true) {
    drupal_set_message(t('There was a problem deleting this node in Elasticsearch.'));
    return;
  }

  drupal_set_message(t('The node has been deleted in Elasticsearch.'));
}

Again, we search for the node in Elasticsearch and use the returned ID as a marker to delete the document.

Please keep in mind though that using early returns such as illustrated above is not ideal inside Drupal hook implementations unless this is more or less all the functionality that needs to go in them. I recommend splitting the logic into helper functions if you need to perform other unrelated tasks inside these hooks.

This is enough to get us started using Elasticsearch as a very simple data source on top of Drupal. With this basic code in place, you can navigate to your Drupal site and start creating some nodes, updating them and deleting them.

One way to check if Elasticsearch actually gets populated, is to disable the remote access restriction I mentioned above you need to enable. Make sure you only do this on your local, development, environment. This way, you can perform HTTP requests directly from the browser and get JSON data back from Elasticsearch.

You can do a quick search for all the nodes in Elasticsearch by navigating to this URL:

http://localhost:9200/node/_search

…where localhost points to your local server and 9200 is the default Elasticsearch port.

For article nodes only:

http://localhost:9200/node/article/_search

And for individual articles, by the auto generated Elasticsearch ids:

http://localhost:9200/node/article/AUnJgdPGGE7A1g9FtqdV

Go ahead and check out the Elasticsearch documentation for all the amazing ways you can interact with it.

Conclusion

We’ve seen in this article how we can start working to integrate Elasticsearch with Drupal. Obviously, there is far more we can do based on even the small things we’ve accomplished. We can extend the integration to other entities and even Drupal configuration if needed. In any case, we now have some Drupal data in Elasticsearch, ready to be used from an external application.

That external application will be the task for the second part of this tutorial. We’ll be setting up a small Silex app that, using the Elasticsearch PHP SDK, will read the Drupal data directly from Elasticsearch. As with part 1, above, we won’t be going through a step-by-step tutorial on accomplishing a given task, but instead will explore one of the ways that you can start building this integration. See you there.

Mar 23 2015
Mar 23

Have you ever used hook_node_access() to control access to various node operations on your site? Have you found this hook unbelievably awesome? Thought to yourself, boy, I'm unstoppable now? Well, I sure did.

The problem is that Drupal 7 entities do not start and end with nodes and you may want to control access to other entities in the same way. However, there is no hook_taxonomy_term_access() or hook_fieldable_panels_pane_access() that you can use to apply this tactic. So what can you do in these cases?

The first thing is to understand that each entity implementation is different. Some have a centralised access callback for all the operations while others simply define permissions or access callbacks to be used directly inside the hook_menu() definition. And I'm sure there are also other ways of handling this but these I think are the most common.

In this article we are going to look at two entity examples: taxonomy terms (from core) and fieldable panels panes (from contrib). We will trace the access pipeline from request to response and see what we can do to intervene in this process and add our own logic. So let's begin.

Taxonomy terms

The first entity type we are going to look at is the regular taxonomy term introduced by core. It's actually pretty easy to understand how this entity type is built and what we can do in order to hook into the access pipeline.

If we take a look at taxonomy_menu(), we can quickly figure out which paths it creates to add and edit terms and what access callback and arguments are defined:

Adding terms to a vocabulary

$items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add'] = array(
   'title' => 'Add term',
   'page callback' => 'drupal_get_form',
   'page arguments' => array('taxonomy_form_term', array(), 3),
   'access arguments' => array('administer taxonomy'),
   'type' => MENU_LOCAL_ACTION,
   'file' => 'taxonomy.admin.inc',
);

Editing a particular term

$items['taxonomy/term/%taxonomy_term/edit'] = array(
   'title' => 'Edit',
   'page callback' => 'drupal_get_form',
   // Pass a NULL argument to ensure that additional path components are not
   // passed to taxonomy_form_term() as the vocabulary machine name argument.
   'page arguments' => array('taxonomy_form_term', 2, NULL),
   'access callback' => 'taxonomy_term_edit_access',
   'access arguments' => array(2),
   'type' => MENU_LOCAL_TASK,
   'weight' => 10,
   'file' => 'taxonomy.admin.inc',
);

By nature, viewing taxonomy term pages has to do with node access since they show a list of nodes so we will skip this aspect here and stick with just the add/edit operations.

So let's change the access callbacks and potential passed arguments inside a hook_menu_alter() implementation.

/**
 * Implements hook_menu_alter().
 */
function demo_menu_alter(&$items) {
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add']['access callback'] = 'demo_taxonomy_term_access';
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add']['access arguments'] = array(3, 'add');

  $items['taxonomy/term/%taxonomy_term/edit']['access callback'] = 'demo_taxonomy_term_access';
  $items['taxonomy/term/%taxonomy_term/edit']['access arguments'][] = 'edit';
}

So what happens here? We are simply replacing the access callback definition for these two menu items with our own custom function demo_taxonomy_term_access(). Additionally, we standardise a bit the arguments this function gets: $entity (either term or vocabulary object) and $op (add or edit). This covers both cases.

Now let's write our callback function:

/**
 * Access callback for taxonomy term add/edit operations
 *
 * @param null $entity
 * @param null $op
 * @return bool
 */
function demo_taxonomy_term_access($entity = null, $op = null) {
  if ($op === 'add') {
    return user_access('administer taxonomy');
  }

  if ($op === 'edit') {
    return taxonomy_term_edit_access($entity);
  }
}

Inside the function we run a check on $op and perform the access logic we want. In doing so, we make use of the $entity object that can be either the term being edited or the vocabulary to which a new term is added. In this example we are replicating exactly the intended access checks of the Taxonomy module. To add a term, a user needs to have the administer taxonomy permission while to edit one it needs either the same permission or a vocabulary specific permission (as seen inside taxonomy_term_edit_access()). Now it's up to you to include inside this logic whatever else you need. And you end up with something not so dissimilar to hook_node_access() but for taxonomy terms.

Fieldable Panels Panes (FPP)

FPP is a contributed module that creates an entity type that is primarily used inside a Panels context. Regardless of any of this though, it too exposes CRUD operations on the entities of this type. And consequently, there are access implications. So let's see how we can hook into this pipeline by starting where we did with the Taxonomy module: at fieldable_panels_panes_menu().

By looking inside this hook implementation we can find many defined paths which relate to these CRUD operations. And we also see that many of them have fieldable_panels_panes_access() as the access callback with some specific arguments passed to it.

But what does this function actually do? Nothing but taking the parameters and deferring to the controller class responsible for this entity type and its access() method. And by checking fieldable_panels_panes_entity_info(), the principle function responsible for defining the entity type, we find that this is the PanelsPaneController class. In there, we find all the logic for determining access rights for various operations.

Now that we know all this, what can we do to hook into this pipeline? We could do like before and override the hook_menu() implementation. But since there are so many menu items and the FPP class controller is already doing such a nice job, that may be counter productive. So let's instead override the entity definition and replace the class controller with one of ours that extends PanelsPaneController. In there, we then do what we want.

First, we implement hook_entity_info_alter():

/**
 * Implements hook_entity_info_alter().
 */
function demo_entity_info_alter(&$entity_info) {
  $entity_info['fieldable_panels_pane']['controller class'] = 'DemoPanelsPaneController';
}

Right after this, we create a file inside our module called DemoPanelsPaneController.inc which contains the following to start with:

<?php

/**
 * Overrides fieldable panels panes controller functionality
 */
class DemoPanelsPaneController extends PanelsPaneController {

}

Finally, we edit the module's .info file and make sure it loads this file:

files[] = DemoPanelsPaneController.inc

Then we clear the cache. If all went well, nothing really has changed on the site in terms of functionality. However, the DemoPanelsPaneController class is being used for controlling the fieldable_panels_pane entity type. And since this one extends PanelsPaneController, all previous functionality remains. It follows to now override the access() method and include our own logic to it:

public function access($op, $entity = NULL, $account = NULL) {
   // $account not always full (defaults to current user)
   return parent::access($op, $entity, $account);
}

In the example above, nothing is really changed because the logic is deferred back to the parent class. But you could add some logic in addition or instead of that depending on various contextual factors.

However, I strongly recommend/warn you to stick to the minimum amount of deviation from default is needed, and for all rest, defer back to the original logic. This is to prevent the opening of any security holes. For example, if your access() method looks like this:

public function access($op, $entity = NULL, $account = NULL) {
   if ($op === 'update') {
     return false;
   }   
}

You are indeed denying access to the edit form but now anybody can create and delete entities because there is no more check on those operations. So make sure you understand this and do not leave any loopholes. The fix in this case would be:

public function access($op, $entity = NULL, $account = NULL) {
   if ($op === 'update') {
     return false;
   }   
   return parent::access($op, $entity, $account);
}

If the operation is edit, the access is denied for everybody (probably not a good idea but suitable for this demo purpose). However, if the operation is delete, create or view, we defer to the logic of the parent class to handle those cases. In which case the default FPP permissions will be used.

Conclusion

In this article we've seen two ways we can hook into the access checking pipeline of entities in Drupal 7. We've learned that there is more than just one way of going about it depending on how the entity type in question has been defined. The purpose was to illustrate how you can approach the matter and where you need to look in order to find a solution. Hope this helps.

Mar 11 2015
Mar 11

Please be aware that due to the development process Drupal 8 has been undergoing at the time of writing, some parts of the code might be outdated. Take a look at this repository in which I try to update the example code and make it work with the latest Drupal 8 release.

With the introduction of annotated plugins, a lot has changed in Drupal 8. We have a more streamlined approach to describing and discovering pieces of functionality that extend the core. Along with many other components, the former Field API (part of the larger and consolidated Entity API) is now based on plugins.

In this tutorial we will go through defining a custom field formatter for an existing field (image). What we want to achieve is to make it possible to display an image with a small caption below it. This caption will be the title value assigned to the image if one exists.

The code we write here can be found in this repository as the image_title_caption module. But let’s see how we can get to that final result.

The Drupal module

Let us start by creating a new custom module (image_title_caption) with only one file:

image_title_caption.info.yml:

name: Image title caption
type: module
description: Uses the image title field as a caption
core: 8.x
dependencies:
  - image

Nothing out of the ordinary here. We can even enable the module already if we want.

The plugin

In Drupal 8, field formatters (like field types and widgets themselves) are plugins. Core ones are defined either by core modules or can be found inside the Drupal\Core\Field\Plugin\Field\FieldFormatter namespace. And like we’ve seen in a previous article in which we looked at custom blocks, plugins go inside the src/Plugin/ folder of our module. In the case of field formatters, this will be src/Plugin/Field/FieldFormatter directory.

Below you can see our own formatter class:

src/Plugin/Field/FieldFormatter/ImageTitleCaption.php:

<?php

/**
 * @file
 * Contains \Drupal\image_title_caption\Plugin\Field\FieldFormatter\ImageTitleCaption.
 */

namespace Drupal\image_title_caption\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatter;

/**
 * Plugin implementation of the 'image_title_caption' formatter.
 *
 * @FieldFormatter(
 *   id = "image_title_caption",
 *   label = @Translation("Image with caption from title"),
 *   field_types = {
 *     "image"
 *   }
 * )
 */
class ImageTitleCaption extends ImageFormatter {

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items) {
    $elements = parent::viewElements($items);
    foreach ($elements as &$element) {
      $element['#theme'] = 'image_title_caption_formatter';
    }

    return $elements;
  }

}

This is our plugin. Nothing else to it. Above the class declaration we have the @FieldFormatter annotation through which the plugin gets discovered. We specify a plugin ID (image_title_caption), label and an array of field types this formatter can be used with. In our case, the latter only contains the default image field type but we could support more if we wanted to, even custom field types. The values that go in that array are plugin IDs so if you look at the image field type plugin, you’ll see that its ID is image.

The class looks simple because we are extending from the default ImageFormatter plugin defined by the core Image module. For our purpose, all we need to override is the viewElements() method which is responsible for returning a render array of our field data. The latter can be found inside the $items list and can be used and prepared for rendering.

The first thing we do in this method is make sure we call the parent class method on the items and store that in a variable. That will already prepare the image to be rendered just as if it would normally.

By default, the ImageFormatter plugin (the parent) uses the image_formatter theme inside the render array to output the image field values. What we do here is that for each item, we replace this theme with our own: image_title_caption_formatter. Then we return the elements (render array) just like the parent does.

You’ll notice this a lot in Drupal 8: we get a very good indication on what we need to do from the parent classes we extend. And if you ask me, that is much better than figuring out what some magic hook or function does.

The theme

Since the image_title_caption_formatter theme we specified above is so far imaginary, we’ll need to create it. Inside the .module file of our module we need to implement hook_theme:

image_title_caption.module:

/**
 * Implements hook_theme().
 */
function image_title_caption_theme() {
  return array(
    'image_title_caption_formatter' => array(
      'variables' => array('item' => NULL, 'item_attributes' => NULL, 'url' => NULL, 'image_style' => NULL),
    ),
  );
}

This should look familiar as it is very similar to Drupal 7. Please take note of the variables we pass to this theme. We intend to override the default image_formatter theme so we should have the same variables passed here as well. Additionally, since the image_formatter theme is preprocessed, we’ll need to create a preprocessor for our theme as well:

/**
 * Implements template_preprocess_image_title_caption_formatter().
 */
function template_preprocess_image_title_caption_formatter(&$vars) {
  template_preprocess_image_formatter($vars);
  $vars['caption'] = String::checkPlain($vars['item']->get('title')->getValue());
}

In this preprocessor we perform two actions:

  • We make sure that the variables passed to the template file will have first been preprocessed by the default image_formatter theme preprocessor. This is so that all the variables are exactly the same and the image gets displayed as it normally would be.
  • We create a new variable called caption that will contain the sanitised value of the image title.

For sanitisation, we use the helper String class statically. We are still inside the .module file so we cannot inject it, but we need to use it at the top of the file:

use Drupal\Component\Utility\String;

Template

Lastly, we need to create a template file for our new theme:

templates/image-title-caption-formatter.html.twig:

{% if url %}
  <a href="http://www.sitepoint.com/creating-custom-field-formatters-drupal-8//{{ url }}">{{ image }}</a>
{% else %}
  {{ image }}
{% endif %}
{% if caption %}
  <div class="image-caption">{{ caption }}</div>
{% endif %}

Similar to Drupal 7, the name of this file is important as it mirrors the theme name. As for the contents, they are almost the same as the template used by the image_formatter theme except for the caption printed at the bottom.

Does it work?

Now that we’ve written the code, we need enable the module and clear all the caches if we had made code changes after enabling. It’s time to test it out.

For example, go to the article content type field display settings at admin/structure/types/manage/article/display. For the Image field, under the Format heading, you should be able to select the Image with caption from title format. Save the form and go to admin/structure/types/manage/article/fields/node.article.field_image and make sure the image field title is enabled.

Finally, you can edit an article, upload an image and specify a title. That title will continue to behave as such, but additionally, it will be displayed below the image as a caption. Of course, you can still style it as you wish etc.

Conclusion

In this article we’ve seen how easy it is to create a field formatter and extend default behaviour in Drupal 8. We’ve only touched on overriding the viewElements() of this plugin but we can do much more to further customise things. You are also not required to extend the ImageFormatter. There are plenty of existing plugins you can either extend from or use as an example.

Additionally, you can easily create new field types and widgets as well. It’s a similar process but you’ll need to take some schema information into account, use different annotation classes and write some more code. But the point is you are very flexible in doing so.

Mar 02 2015
Mar 02

In a previous article I showed you how to use the usort() and uasort() PHP functions to sort some more complex data using a comparator callback function. How cool was that?

Although powerful, this technique is quite restricting though because all the logic for the sorting happens inside the comparator function. In other words, you cannot pass parameters to this callback except for the actual default values that are being compared. So what's the problem with this?

Say you have an array of objects that have a name property that can be retrieved by a getName() getter method. And let's say they also have an address property retrieved by getAddress(). And you have a listing of this data and you need to allow for sorting by any of these properties both ASC and DESC. And forget for one second about the possibility of ordering them as they come out of your data store.

Implementing this with just the default usort() function means you will need 4 different comparer functions (one for each combination of property and sort direction). And what if you want to add more columns to the listing? Oh no..

As you can imagine, the solution to this problem is having a dynamic sorting function (or method in a class) to which you can pass the items to be sorted, the property by which to sort and the direction of sort. And then not have to worry about creating all these ridiculous comparer functions. So how might this look like?

function sortByObjectProps(&$items, $method, $order) {
  if ( ! is_array($items)) {
    return false;
  }

  return usort($items, function($a, $b) use ($method, $order){
    $cmp = strcmp($a->$method(), $b->$method());
    return $order === 'asc' ? $cmp : -$cmp;
  });
}

So what happens here? First of all, $items is passed by reference so we don't need to return it. The return value will just be a boolean indicating the success or failure of the sort. Additionally, we pass the method name that retrieves the property value ($method) and the direction of the sort ($order).

Then we run usort() on the items but - and here is the kicker - with an anonymous function that can use the passed in method and order values. Then it's just a matter of comparing the return values of the getter methods and negate the integer if the order is DESC. Pretty cool no?

So now you can have as many columns as you need and get them sorted in both directions. And obviously even more complex stuff.

Hope this helps.

Feb 25 2015
Feb 25

Drupal 8 comes with many improvements over its predecessor we have grown to both love and hate. Next to prominent systems such as Views in core, configuration management or a useful translation service, there are also less known changes but that are equally important to know and use. One such improvement has been the cache API that solves many performance problems we have in Drupal 7.

drupal8wide

In this article, I want to shine a bit of light over the new cache API. To this end, we are going to look at how we can use it in our custom modules as we are encouraged to do so much more in Drupal 8.

Additionally, I have prepared a little demonstration in the shape of a module you can install for testing the impact of the cache API. It’s a simple page that in its rendering logic makes an external API call (to a dummy JSON endpoint) and caches its results. The page then displays the actual time it takes for this to happen, contrasting the external call time vs. the cached version time.

The new cache API

Cache concept.

Bins

The new cache API (with the default DatabaseBackend storage) is stored in multiple bins which map to tables that start with the prefix cache_. When interacting with the cache, we always start by requesting a cache bin:

$cache = \Drupal::cache();

Where $cache will be an instance of the DatabaseBackend object that represents the default bin (cache_default). To request a particular bin we pass in the name in the constructor:

$render_cache = \Drupal::cache('render');

Where $render_cache will represent the render cache bin (which is new in Drupal 8 and is supposed to improve render performance across the board).

As you can see, we are requesting the cache service statically using the \Drupal class. If we are working inside classes, it is best practice to inject the service from the container. You can do so by specifying as an argument to your service the relevant cache bin service (such as cache.default). Here you can get a list of all core services including the ones related to cache.

But for the sake of brevity, we will use it statically here.

Retrieving cached items

Once we know which bin we want to work with (for custom modules this will usually be the default bin), we can retrieve and store cache items.

$cache = \Drupal::cache()->get('my_value');

It’s that simple. $cache will be a stdClass object containing some metadata about the cache item plus the actual data available under the $cache->data property. The my_value parameter is the cache ID.

An important thing to keep in mind is that using the get() method without a second parameter will not return the cache item if it has been invalidated (either programatically or through expiration). Passing the boolean true as a second parameter will force it to return the data.

Storing cache items

Although storing new items in the cache is just as easy as retrieving them, we have more options when doing so. To store an item we use the set() method (instead of get() like before), a method that takes 2 mandatory parameters and 2 optional ones:

  • the cache ID (the string by which we can later reference the item)
  • the data (a PHP value such as a string, array or object that gets serialised automatically and stored in the table – should not be over 1MB in size)
  • the expiration time (a timestamp in the future when this cache item will automatically become invalid or -1 which basically means this item never expires. It is best practice to use the Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT constant to represent this value)
  • tags (an array of cache tags this item can be later identified by)

As an example:

Drupal::cache()->set('my_value', $my_object, CacheBackendInterface::CACHE_PERMANENT, array('my_first_tag', 'my_second_tag'));

This will set a permanent cache item tagged with 2 tags and store a serialised version of $my_object as the data.

Cache invalidation and removal

Cache invalidation means that the respective items are no longer fresh and are unreliable in terms of what data they hold. They will be removed at the next garbage collection which can also be called using the garbageCollection() method on the CacheBackend object.

As mentioned above, when storing a cache item we can specify an expiration time. When this time lapses, the cache item becomes invalid but still exists in the bin and can be retrieved. However, we can also invalidate items manually using the invalidate(), invalidateMultiple() or invalidateAll() methods on the CacheBackend object.

Removing items altogether can be done using the delete(), deleteMultiple() or deleteAll() methods. These actions also happen only on the bin the CacheBackend is wrapping and completely remove the respective table records.

Cache tags

Another cool new feature of the Cache API in Drupal 8 are the cache tags (the fourth parameter in the setter method). The role of the tags is to identify cache items across multiple bins for proper invalidation. The purpose is the ability to accurately target multiple cache items that contain data about the same object, page, etc. For example, nodes can appear both on a page and in a view (stored in different cache items in different bins but both tagged with the same node:nid formatted tag). This allows invalidating both cache items when changes happen to that node without having to know the cache ids.

To manually invalidate caches using the tags, we can use the invalidateTags() method statically on the \Drupal\Core\Cache\Cache class:

\Drupal\Core\Cache\Cache::invalidateTags(array('node:5', 'my_tag'));

This will call the cache invalidator service and invalidate all the cache items tagged with node:5 and my_tag.

Additionally, for Drupal entities we don’t have to create our own tags but can retrieve them from the entity system:

  • \Drupal\Core\Entity\EntityInterface::getCacheTags()
  • \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags()

This keeps the tags for Drupal entities consistent across the board.

Demonstrating the cache API

As I mentioned before, I created a small module that allows us to see the benefits of caching data. You can find the module in this git repository but here is the crux of it:

Please note that in this example I access the cache backend service statically to save some space. For a dependency injection approach (the correct approach), take a look at the repository code.

A route file that adds a new route to the /cache-demo path:

cache_demo_page:
  path: 'cache-demo'
  defaults:
    _controller: '\Drupal\cache_demo\Controller\CacheDemoController::index'
    _title: 'Cache demo'
  requirements:
    _permission: 'access content'

And the controller class that returns the page inside src/Controller/CacheDemoController.php:

<?php

/**
 * @file
 * Contains \Drupal\cache_demo\Controller\CacheDemoController.
 */

namespace Drupal\cache_demo\Controller;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use \GuzzleHttp\Client;

/**
 * Cache demo main page.
 */
class CacheDemoController extends ControllerBase {

  public function index(Request $request) {
    $output = array();

    $clear = $request->query->get('clear');
    if ($clear) {
      $this->clearPosts();
    }

    if (!$clear) {
      $start_time = microtime(TRUE);
      $data = $this->loadPosts();
      $end_time = microtime(TRUE);

      $duration = $end_time - $start_time;
      $reload = $data['means'] == 'API' ? 'Reload the page to retrieve the posts from cache and see the difference.' : '';
      $output['duration'] = array(
        '#type' => 'markup',
        '#prefix' => '<div>',
        '#suffix' => '</div>',
        '#markup' => t('The duration for loading the posts has been @duration ms using the @means. @reload',
          array(
            '@duration' => number_format($duration * 1000, 2),
            '@means' => $data['means'],
            '@reload' => $reload
          )),
      );
    }

    if ($cache = \Drupal::cache()->get('cache_demo_posts') && $data['means'] == 'cache') {
      $url = new Url('cache_demo_page', array(), array('query' => array('clear' => true)));
      $output['clear'] = array(
        '#type' => 'markup',
        '#markup' => $this->l('Clear the cache and try again', $url),
      );
    }

    if (!$cache = \Drupal::cache()->get('cache_demo_posts')) {
      $url = new Url('cache_demo_page');
      $output['populate'] = array(
        '#type' => 'markup',
        '#markup' => $this->l('Try loading again to query the API and re-populate the cache', $url),
      );
    }

    return $output;
  }

  /**
   * Loads a bunch of dummy posts from cache or API
   * @return array
   */
  private function loadPosts() {
    if ($cache = \Drupal::cache()->get('cache_demo_posts')) {
      return array(
        'data' => $cache->data,
        'means' => 'cache',
      );
    }
    else {
      $guzzle = new Client();
      $response = $guzzle->get('http://jsonplaceholder.typicode.com/posts');
      $posts = $response->json();
      \Drupal::cache()->set('cache_demo_posts', $posts, CacheBackendInterface::CACHE_PERMANENT);
      return array(
        'data' => $posts,
        'means' => 'API',
      );
    }
  }

  /**
   * Clears the posts from the cache.
   */
  function clearPosts() {
    if ($cache = \Drupal::cache()->get('cache_demo_posts')) {
      \Drupal::cache()->delete('cache_demo_posts');
      drupal_set_message('Posts have been removed from cache.', 'status');
    }
    else {
      drupal_set_message('No posts in cache.', 'error');
    }
  }

}

Inside the index() method we do a quick check to see whether the clear query parameter is present in the url and call the clearPosts() method responsible for deleting the cache item. If there isn’t one, we calculate how long it takes for the loadPosts() method to return its value (which can be either the posts from the cache or from the API). We use Guzzle to make the API call and when we do, we also store the results directly. Then we just output the duration of the call in milliseconds and print 2 different links depending on whether there is cache stored or not (to allow us to clear the cache item and run the API call again).

When you navigate to cache-demo for the first time, the API call gets made and the 100 posts get stored in the cache. You can then reload the page to see how long it takes for those posts to be retrieved from the cache. Upon doing that, you’ll have a link to clear the cache (by a page refresh with the clear query string) followed by another link which refreshes the page without the clear query string and that in turn makes the API call again. And on like that to test the contrast in duration.

Conclusion

In this article we’ve looked at how easy it is to use the Cache API in Drupal 8. There are some very simple class methods that we can use to manage cache items and it has become too straightforward for us not to start using it in our custom modules. I encourage you to check it out, play around with the API and see for yourself how easy it is to use.

Feb 11 2015
Feb 11

In this article I am going to show you how you can integrate Pushover with your Drupal site. I will illustrate a couple of examples of how you can use Pushover to notify yourself as soon as something happens on your site.

The code I write in this article is also available in this repository so you can just clone that if you want to follow along.

What is Pushover?

Pushover is a web and mobile application that allows you to get real time notifications on your mobile device. The way it works is that you install an app on your Android or Apple device and using a handy API you can send that app notifications. The great thing about this is that it happens more or less in real time (depending on your internet connection) as Pushover uses the Google and Apple servers to send the notifications.

The price is also very affordable. At a rate of $4.99 USD per platform (Android, Apple or desktop) paid only once, you can use it on any number of devices under that platform. And you also get a 5 day trial period for free the moment you create your account.

What am I doing here?

In this article I am going to set up a Pushover application and use it from my Drupal site to notify my phone of various events. I will give you two example use cases that Pushover can be handy with:

  • Whenever an anonymous user posts a comment that awaits administrative approval, I’ll send a notification to my phone
  • Whenever the admin user 1 logs into the site, I’ll send an emergency notification to my phone (useful if you are the only user of that admin account).

Naturally, these are examples and you may not find them useful. But they only serve as illustration of the power you can have by using Pushover.

Pushover account

First, a quick look at creating your Pushover account. To follow along, go on over to Pushover and sign up if you haven’t already and you can start your 5 day free trial. Then, go to Google Play or the App Store and install the app on your device. You’ll need to log in to your account and give the device a name. Mine is called simply Nexus (hint: I don’t have an iPhone).

Go back then to the Pushover website and you can already test it out by sending yourself a test notification to one or all of your active devices. You can even specify which sound it should make.

Next, if you want to use the API, you’ll need to create an application. This will generate for you an app_token to use later on. And that should be pretty much it.

Drupal

Now that the Pushover account creation is taken care of, the device app is installed (Android in my case) and I have my Pushover application, it’s time to see how I can achieve the goal set out in the beginning. As a brief roadmap, I will do the following:

  • Create a custom Drupal module
  • Add to it the Pushover class created by Chris Schalenborgh (a handy wrapper over the curl calls to the API)
  • Implement some hooks that will trigger the notifications based on certain conditions
  • Profit

The custom module I’ll be working with in this example is called pushover and it contains a pushover.info and a pushover.module file (as required). Additionally, I will create a lib/Pushover folder in it to store the external class I will use to connect to Pushover. There are other – more recommended ways – of importing external libraries into Drupal modules (see the Libraries API), but for the sake of brevity, this will work just fine.

The first thing I want to do in my pushover.module file is to import this external class. Based on my folder structure, I can do so with this line:

require_once(DRUPAL_ROOT . '/' . drupal_get_path('module', 'pushover') . '/lib/Pushover/Pushover.php');

Next, I want to create a reusable helper function that will return a pushable object. That is an object already populated with my own defaults (such as credentials) and that takes some parameters for the more common properties. But first, I want to put my Pushover account credentials into the settings.php file because they do not belong in my git repository:

$conf['pushover_credentials'] = array(
  'user_token' => 'uCpygdjfsndfi7233sdasdo33Yv',
  'app_token' => 'aKH8Nwsdasdanl342jmsdaBWgoVe',
);

Obviously neither of these tokens are now valid but if you are following along, you should replace them with yours: the user_token you get on the main page as you log in to the Pushover website and the app_token is the one generated for your application.

Then I can continue with my helper function I mentioned earlier:

/**
 * Helper function that returns a pushable object using the Pushover class
 * 
 * @param $vars
 * @return bool|Pushover
 */
function pushover_get_pushable($vars) {
  global $conf;
  if (isset($conf['pushover_credentials'])) {
    $push = new Pushover();
    $push->setToken($conf['pushover_credentials']['app_token']);
    $push->setUser($conf['pushover_credentials']['user_token']);
    $push->setTitle($vars['title']);
    $push->setMessage($vars['message']);
    if (isset($vars['url'])) {
      $push->setUrl($vars['url']);
    }
    if (isset($vars['device'])) {
      $push->setDevice($vars['device']);
    }
    $push->setTimestamp(time());

    return $push;
  }
  else {
    return FALSE;
  }
}

In this function I instantiate a new Pushover object if there are credentials in my settings.php file. Otherwise, I fail silently by returning false. The function takes some parameters that are set on the object: title and message are mandatory whereas the url and device are not. The device is actually the name of the device to which you want to restrict this notification.

Additionally, I set the current timestamp and then return the object.

Next, it’s time to use this function inside some hooks. The first one is going to be hook_comment_insert():

/**
 * Implements hook_comment_insert().
 */
function pushover_comment_insert($comment) {

  // Send a push notification if a new comment is created by an anonymous user
  // and it is not yet published.
  if ($comment->status == 0 && $comment->is_anonymous == TRUE) {
    global $base_url;
    $vars = array(
      'title' => 'New comment on ' . variable_get('site_name') . '!',
      'message' => 'Subject: ' . $comment->subject,
      'url' => $base_url . '/node/' . $comment->nid . '#comment-' . $comment->cid,
      'device' => 'Nexus'
    );
    $pushable = pushover_get_pushable($vars);
    if ($pushable) {
      $pushed = $pushable->send();
      if ($pushed == false) {
        watchdog('Pushover', t('A comment has been created but there was an error pushing that over.'), array(), WATCHDOG_ERROR, NULL);
      }
    }
  }
}

In here I check if the commenter is anonymous and the status is 0 (to be on the safe side). If that’s the case, I build the array of parameters for my helper function with some information about the site and comment and use the send() method to send the notification. You’ll notice that I restricted this to the Nexus device.

At the end, I check whether the notification went out successfully (the send() method returns false if the Pushover service does not return the success status code of 1. If something went wrong, I quickly log it to the watchdog.

So now if an anonymous user writes a comment, I get a push notification with the site name, comment subject and URL. Nifty.

Now let’s turn to the second example in which I implement an emergency notification if my admin user logs into the site. If it’s not me, I’ll know something is wrong and I probably got hacked. I do this inside a hook_user_login() implementation:

/**
 * Implements hook_user_login().
 */
function pushover_user_login(&$edit, $account) {
  // If the admin user logs in, send a push notification.
  if ($account->uid == 1) {
    $whitelist = array('1.1.1.1');
    if (!in_array(ip_address(), $whitelist)) {
      global $base_url;
      $vars = array(
        'title' => 'Admin user sign in',
        'message' => 'Admin user has logged into this site: ' . variable_get('site_name') . '!',
        'url' => $base_url,
      );
      $pushable = pushover_get_pushable($vars);
      if ($pushable) {
        $pushable->setPriority(2);
        $pushable->setRetry(30);
        $pushable->setExpire(60);
        $pushed = $pushable->send();
        if ($pushed == false) {
          watchdog('Pushover', t('An admin user has logged into the site but there was an error pushing this over.'), array(), WATCHDOG_ERROR, NULL);
        }
      }
    }
  }
} 

In here, I first check if the user logging in is the one with the id of 1 (the main admin user). Then I create an array with whitelisted IPs to check against the user logging in. I don’t want to get notified if I log in from home or from the office (1.1.1.1 is just an example ip address).

Then just like before, I get my pushable object with the usual variables (no device this time, I want this to go out to all my devices). On top of those, I set a priority of 2 (marking it an emergency notification), a retry value of 30 seconds and an expire value of 60 seconds. The latter 2 in combination with the priority make it so that if left unacknowledged by my phone, the notification gets resent every 30 seconds for a total duration of 60 seconds. For more information about the possible options you have with Pushover, make sure you check out their API docs.

And there it is. I will now get an emergency notification if someone logs in with my admin account. Of course, not very good if many people can log in with that account, but you get the point.

Conclusion

In this tutorial I showed you how you can use Pushover from your Drupal site to send notifications to your phone when certain events occur. I covered 2 examples but I’m sure you can find more. I would like to also mention that I found a contrib Drupal module called Pushover which uses Rules in order to send out Pushover notifications. I haven’t really used it, but make sure to check it out if you want to use Pushover and your site is already making use of the Rules module. Or, you know, you hate writing code.

Feb 02 2015
Feb 02

So you are taking the plunge into learning Drupal 8 for the purpose of developing sites, modules, themes, etc. You're a great Drupal 7 developer, familiar with many drupalisms but you don't have tons of experience with modern PHP frameworks, principles and practices. Well, Drupal 8 still includes many of the old drupalisms but still attempts to keep in line with times and modernise itself.

In this article I want to outline 6 steps I believe you should take to get started learning how to develop custom modules and/or themes in Drupal 8. On top of these 6 builds everything else.

The first three are PHP related in a more general fashion, while the last three target aspects of Drupal 8 itself.

1. Learn Object Oriented Programming

One of the biggest difference between Drupal 7 and 8 for developers is the way code is written. It's still PHP but it's now much more object oriented. Global procedural functions are still in place but most of the logic happens in classes.

In case you don't have much experience with Object Oriented Programming in PHP, this is the first thing you need to learn, brush upon or revise (depending on your level). There are many resources available out there, all scattered as hyperlinks in this section. There are also courses you can take, both free and paid.

Without quite a solid OOP foundation, you won't be able to understand much of how Drupal 8 modules are built.

2. Learn how to use Composer

One of the consequences of modernizing PHP has been the introduction of the Composer package manager. Projects are no longer built without it as it does a great job of installing, updating and managing in general the external libraries and dependencies of your project. Not to mention its very helpful autoloader.

Drupal 8 uses Composer to manage external PHP libraries and dependencies (such as Symfony components, Guzzle, etc) and there is talk about the ability to handle also contrib modules. So make sure you know how Composer works and even start using it in non-Drupal projects.

3. Get familiar with Symfony

One of the main points of contention (back then) in the Drupal 8 development cycle was the introduction of Symfony components. Some people didn't really agree with this great shift from the Drupal way, but others embraced it wholeheartedly. I am in the latter group as I love Symfony and used it even before developing anything in Drupal 8.

Drupal 7 developers are often being told that knowing Symfony is not required in order to develop in Drupal 8. While technically true, you still end up learning a lot of it through the Drupal experience. That being said, I strongly recommend learning some Symfony before. It is a great modular framework and knowing its concepts will really help you out in understanding how Drupal 8 is built (for the components it uses I mean). Once you can build a small website in Symfony, you'll enjoy developing in Drupal 8 that much more because concepts will be similar a lot of the time. Not too mention that you can use Symfony to build apps on its own.

4. Routing and controllers

Just like with Drupal 7, when starting to learn Drupal 8 you need to create the obligatory hello world module (creating a page with a parameter in the URL( usually world) that displays the text Hello + parameter). This example introduces you to many important things:

  • Module folder structure
  • Routing (no more hook_menu) through routing.yml files that map to Controller methods
  • Controller classes which have various methods that can be mapped to routes
  • Access arguments for these routes
  • Rendering markup to the page inside the Controller methods
  • etc

So I really recommend giving this a go.

5. Plugins

Another important concept you'll need to get familiar with is Plugins. Admittedly, this is not the easiest to grasp, but it is super important because it's everywhere. Not to worry though, it's not rocket science.

Many old Drupal 7 implementations of various concepts have been transformed to plugins: info hooks, blocks, field formatters, views handlers, etc. Understanding plugins is therefore very important for being able to extend default Drupal functionality.

6. Dependency injection and the service container

The last step I am going to mention here is dependency injection. Drupal 8 uses the Symfony dependency injection container to manage service instantiation and injection into classes that need them. This helps decouple functionality and increases testability.

However, many people are scared of this concept, mainly because they don't grasp it. I wasn't super comfortable either before understanding it. But you should definitely learn what it means, why we use it and how we use it. Because it is a very simple concept that is used all the time in procedural code as well.

You can already find many tutorials out there on Drupal 8 that load services statically through the \Drupal class. It is much faster to write so people (me included) prefer it when writing about D8. I usually also tend to make a note that using dependency injection is preferred in theses cases.

Not understanding what the service container and dependency injection is will easily let you fall into the habit of just statically requesting services and coupling your code like it was procedural. Once you are clear on this point, this will hopefully not happen any more and the sight of a \Drupal::get('some_service') will make you think twice.

Conclusion

There you have it. What I think are the first 6 steps you should take when learning Drupal 8 for the first time. Of course there are many other important things to learn and do but I believe they build on top of this foundation. Of course, this is me writing so others may have different opinions (very much welcomed in the comments). So let's discuss.

Jan 26 2015
Jan 26

In the previous article we've seen how we can interact programatically with Views in Drupal 8 in order to create a custom field in our Views results. Today, we will be looking a bit at how we can create a custom filter you can then add to the View in the UI and influence the results based on that.

Filters in Views have to do with the query being run by Views on the base table. Every filter plugin is responsible with adding various clauses in this query in an attempt to limit the results. Some (probably most) take on configuration parameters so you can specify in the UI how the filtering should be done.

If you remember from the last article, to create our field we extended the FieldPluginBase class. Similarly, for filters, there is a FilterPluginBase class that we can extend to create our own custom filter. Luckily though, Views also provides a bunch of plugins that extend the base one and which we can use or extend to make our lives even easier. For example, there is a BooleanOperator class that provides a lot of the functionality needed for this type of filter. Similarly, there is an InOperator class, a String class, etc. You can find them all inside the views/src/Plugin/views/filter directory of the Views core module or here.

In this tutorial, we will create 2 custom filters. One will be a very simple one that won't even require creating a new class. The second one will be slightly more complex and for which we will create our own plugin.

The code we write will go in the same module we started in the previous article and that can be found in this repository.

Node type filter

The first filter we will write is very simple. We want to be able to filter our node results by the machine name of the node type. By default, we can use a filter in which we select which node types to be included. Let's say, for the sake of argument, we want a more complex one, such as the one available for a regular text value like the title. The String class will be perfect for this and will provide actually 100% of our needs.

So let's go to our hook_views_data_alter() implementation and add a new filter:

...

$data['node_field_data']['node_type_filter'] = array(
  'title' => t('Enhanced node type filter'),
  'filter' => array(
    'title' => t('Enhanced node type filter'),
    'help' => t('Provides a custom filter for nodes by their type.'),
    'field' => 'type',
    'id' => 'string'
  ),
);

...

Since the table that we are interested in altering the query for is the node_field_data table, that is what we are extending with our new filter. Under the filter key we have some basic info + the id of the plugin used to perform this task. Since our needs are very simple, we can directly use the String plugin without us having to extend it. The most important thing here though is the field key (under filter). This is where we specify that our node_type_filter field (which is obviously a non-existent table column) should be treated as being the type column on the node_field_data table. So, by default, the query alter happens on that column. And this way we don't have to worry about anything else, the String plugin will take care of everything. If we didn't specify that, we would have to extend the plugin and make sure the query happens on the right column.

And that's it. You can clear your cache, create a View with nodes of multiple types and add the Enhanced node type filter to it. In its configuration you'll have many matching options such as equals, contains, does not contain etc you can use. For example, you can use contains and specify the letters art in order to return results whose node type machine name contain these letters.

Node title filter

The second custom filter we build will allow Views UI users to filter the node results by their title from a list of possibilities. In other words, they will have a list of checkboxes which will make it possible to include/exclude various node titles from the result set.

Like before, we need to declare our filter inside the hook_views_data_alter() implementation:

...

$data['node_field_data']['nodes_titles'] = array(
  'title' => t('Node titles'),
  'filter' => array(
    'title' => t('Node titles'),
    'help' => t('Specify a list of titles a node can have.'),
    'field' => 'title',
    'id' => 'd8views_node_titles'
  ),
);

...

Since we are filtering on the title column, we are extending again on the node_field_data table but with the title column as the real field to be used. Additionally, this time we are creating a plugin to handle the filtering identified as d8views_node_titles. Now it follows to create this class:

src/Plugin/views/filter/NodeTitles.php:

<?php

/**
 * @file
 * Definition of Drupal\d8views\Plugin\views\filter\NodeTitles.
 */

namespace Drupal\d8views\Plugin\views\filter;

use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\filter\InOperator;
use Drupal\views\ViewExecutable;

/**
 * Filters by given list of node title options.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("d8views_node_titles")
 */
class NodeTitles extends InOperator {

  /**
   * {@inheritdoc}
   */
  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
    parent::init($view, $display, $options);
    $this->valueTitle = t('Allowed node titles');
    $this->definition['options callback'] = array($this, 'generateOptions');
  }

  /**
   * Override the query so that no filtering takes place if the user doesn't
   * select any options.
   */
  public function query() {
    if (!empty($this->value)) {
      parent::query();
    }
  }

  /**
   * Skip validation if no options have been chosen so we can use it as a
   * non-filter.
   */
  public function validate() {
    if (!empty($this->value)) {
      parent::validate();
    }
  }

  /**
   * Helper function that generates the options.
   * @return array
   */
  public function generateOptions() {
    // Array keys are used to compare with the table field values.
    return array(
      'my title' => 'my title',
      'another title' => 'another title',
    );
  }

}

Since we want our filter to be of a type that allows users to select from a list of options to be included in the results, we are extending from the InOperator plugin. The class is identified with the @ViewsFilter("d8views_node_titles") annotation (the id we specified in the hook_views_data_alter() implementation).

Inside our plugin, we override three methods:

Inside init(), we specify the title of the set of filter options and the callback that generates the values for options. This callback has to be a callable and in this case we opted for the generateOptions() method on this class. The latter just returns an array of options to be presented for the users, the keys of which being used in the query alteration. Alternatively, we could have also directly created the options inside the init() method by filling up the $this->valueOptions property with our available titles. Using a callback is cleaner though as you can perform various logic in there responsible for delivering the necessary node titles.

The point of overriding the query() and validate() methods was to prevent a query and validation from happening in case the user created the filter without selecting any title. This way the filter has no effect on the results rather than returning 0 results. It's a simple preference meant to illustrate how you can override various functionality to tailor your plugins to fit your needs.

And that's it. You can add the Node titles filter and check the box next to the titles you want to allow in the results.

Conclusion

In this article we've looked at how we can create custom filters in Drupal 8 Views. We've seen what are the steps to achieve this and looked at a couple of the existing plugins that are used across the framework and which you can use as is or extend from.

The best way to learn how all these work is by studying the code in those plugin classes. You will see if they are enough for what you want to build or extending them makes sense. In the next article we are going to look at some other Views plugins, so stay tuned.

Jan 19 2015
Jan 19

In this article I am going to show you how to create a custom Views field in Drupal 8. At the end of this tutorial, you will be able to add a new field to any node based View which will flag (by displaying a specific message) the nodes of a particular type (configurable in the field configuration). Although I will use nodes, you can use this example to create custom fields for other entities as well.

So let's get started by creating a small module called d8views (which you can also find in this repository):

d8views.info.yml:

name: Drupal 8 Views Demo
description: 'Demo module that illustrates working with the Drupal 8 Views API'
type: module
core: 8.x

In Drupal 7, whenever we want to create a custom field, filter, relationship, etc for Views, we need to implement hook_views_api() and declare the version of Views we are using. That is no longer necessary in Drupal 8. What we do now is create a file called module_name.views.inc in the root of our module and implement the views related hooks there.

To create a custom field for the node entity, we need to implement hook_views_data_alter():

d8views.views.inc:

/**
 * Implements hook_views_data_alter().
 */
function d8views_views_data_alter(array &$data) {
  $data['node']['node_type_flagger'] = array(
    'title' => t('Node type flagger'),
    'field' => array(
      'title' => t('Node type flagger'),
      'help' => t('Flags a specific node type.'),
      'id' => 'node_type_flagger',
    ),
  );
}

In this implementation we extend the node table definition by adding a new field called node_type_flagger. Although there are many more options you can specify here, these will be enough for our purpose. The most important thing to remember is the id key (under field) which marks the id of the views plugin that will be used to handle this field. In Drupal 7 we have instead a handler key in which we specify the class name.

In Drupal 8 we have something called plugins and many things have now been converted to plugins, including views handlers. So let's define ours inside the src/Plugin/views/field folder of our module:

src/Plugin/views/field/NodeTypeFlagger.php

<?php

/**
 * @file
 * Definition of Drupal\d8views\Plugin\views\field\NodeTypeFlagger
 */

namespace Drupal\d8views\Plugin\views\field;

use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\NodeType;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;

/**
 * Field handler to flag the node type.
 *
 * @ingroup views_field_handlers
 *
 * @ViewsField("node_type_flagger")
 */
class NodeTypeFlagger extends FieldPluginBase {

  /**
   * @{inheritdoc}
   */
  public function query() {
    // Leave empty to avoid a query on this field.
  }

  /**
   * Define the available options
   * @return array
   */
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['node_type'] = array('default' => 'article');

    return $options;
  }

  /**
   * Provide the options form.
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    $types = NodeType::loadMultiple();
    $options = [];
    foreach ($types as $key => $type) {
      $options[$key] = $type->label();
    }
    $form['node_type'] = array(
      '#title' => $this->t('Which node type should be flagged?'),
      '#type' => 'select',
      '#default_value' => $this->options['node_type'],
      '#options' => $options,
    );

    parent::buildOptionsForm($form, $form_state);
  }

  /**
   * @{inheritdoc}
   */
  public function render(ResultRow $values) {
    $node = $values->_entity;
    if ($node->bundle() == $this->options['node_type']) {
      return $this->t('Hey, I\'m of the type: @type', array('@type' => $this->options['node_type']));
    }
    else {
      return $this->t('Hey, I\'m something else.');
    }
  }
}

We are defining our NodeTypeFlagger class that extends FieldPluginBase (which is the base plugin abstract class for the views field many plugins extend from). Just above the class declaration we use the @ViewsField annotation to specify the id of this plugin (the same one we declared in the hook_views_data_alter() implementation). We also use the @ingroup annotation to mark that this is a views field handler.

In our example class, we have 4 methods (all overriding the parent class ones).

Query

First, we override the query() method but leave it empty. This is so that views does not try to include this field in the regular node table query (since the field is not backed by a table column).

DefineOptions

The second method is the defineOptions() method through which we specify what configuration options we need for this field. In our case one is enough: we need to specify the node type which we want flagged in the Views results. We set a sensible default as the article node type.

BuildOptionsForm

The third method, buildOptionsForm() is responsible for creating the form for the configuration options we declared earlier. In our case we just have a select list with which we can choose from the existing node types.

Render

Lastly, the render() method which is the most important and which deals with output. We use it to actually render the content of the field for each result. Here is where we perform some business logic based on the currently set node type option and flag with a message whether or not the current result is in fact of the type specified in the configuration.

The $resultRow object is an instance of Drupal\views\ResultRow which contains data returned for the current row by Views and the entity object at the base of the query (in our case the node). Based on this information we can perform our logic.

Keep in mind you can use depedency injection to inject all sorts of services into this class and make use of them in your logic. Additionally, you can override various other methods of the parent class in order to further customize your field.

Conclusion

There you have it. A small custom module that demonstrates how to create a custom Views field (plugin). Relationships, filters, sorters and others work in similar way. I will be covering those in later articles. Stay tuned.

Dec 15 2014
Dec 15

Angular.js is the hot new thing right now for designing applications in the client. Well, it’s not so new anymore but is sure as hell still hot, especially now that it’s being used and backed by Google. It takes the idea of a JavaScript framework to a whole new level and provides a great basis for developing rich and dynamic apps that can run in the browser or as hybrid mobile apps.

logo_drupal

In this article I am going to show you a neat little way of using some of its magic within a Drupal 7 site. A simple piece of functionality but one that is enough to demonstrate how powerful Angular.js is and the potential use cases even within heavy server-side PHP frameworks such as Drupal. So what are we doing?

We are going to create a block that lists some node titles. Big whoop. However, these node titles are going to be loaded asynchronously using Angular.js and there will be a textfield above them to filter/search for nodes (also done asyncronously). As a bonus, we will also use a small open source Angular.js module that will allow us to view some of the node info in a dialog when we click on the titles.

So let’s get started. As usual, all the code we write in the tutorial can be found in this repository.

Ingredients

In order to mock this up, we will need the following:

  • A custom Drupal module
  • A Drupal hook_menu() implementation to create an endpoint for querying nodes
  • A Drupal theme function that uses a template file to render our markup
  • A custom Drupal block to call the theme function and place the markup where we want
  • A small Angular.js app
  • For the bonus part, the ngDialog Angular module

The module

Let us get started with creating a custom module called Ang. As usual, inside the modules/custom folder create an ang.info file:

name = Ang
description = Angular.js example on a Drupal 7 site.
core = 7.x

…and an ang.module file that will contain most of our Drupal related code. Inside this file (don’t forget the opening <?php tag), we can start with the hook_menu() implementation:

/**
 * Implements hook_menu().
 */
function ang_menu() {
  $items = array();

  $items['api/node'] = array(
    'access arguments' => array('access content'),
    'page callback'     => 'ang_node_api',
    'page arguments' => array(2),
    'delivery callback' => 'drupal_json_output'
  );

  return $items;
}
/**
 * API callback to return nodes in JSON format
 *
 * @param $param
 * @return array
 */
function ang_node_api($param) {

  // If passed param is node id
  if ($param && is_numeric($param)) {
    $node = node_load($param);
    return array(
      'nid' => $param,
      'uid' => $node->uid,
      'title' => check_plain($node->title),
      'body' => $node->body[LANGUAGE_NONE][0]['value'],
    );
  }
  // If passed param is text value
  elseif ($param && !is_numeric($param)) {
    $nodes = db_query("SELECT nid, uid, title FROM {node} n JOIN {field_data_body} b ON n.nid = b.entity_id WHERE n.title LIKE :pattern ORDER BY n.created DESC LIMIT 5", array(':pattern' => '%' . db_like($param) . '%'))->fetchAll();
    return $nodes;
  }
  // If there is no passed param
  else {
    $nodes = db_query("SELECT nid, uid, title FROM {node} n JOIN {field_data_body} b ON n.nid = b.entity_id ORDER BY n.created DESC LIMIT 10")->fetchAll();
    return $nodes;
  }
}

In hook_menu() we declare a path (api/node) which can be accessed by anyone with permissions to view content and which will return JSON output created in the callback function ang_node_api(). The latter gets passed one argument, that is whatever is found in the URL after the path we declared: api/node/[some-extra-param]. We need this argument because of we want to achieve 3 things with this endpoint:

  1. return a list of 10 most recent nodes
  2. return a node with a certain id (api/node/5 for example)
  3. return all the nodes which have the passed parameter in their title (api/node/chocolate for example, where chocolate is part of one or more node titles)

And this is what happens in the second function. The parameter is being checked against three cases:

  • If it exists and it’s numeric, we load the respective node and return an array with some basic info form that node (remember, this will be in JSON format)
  • If it exists but it is not numeric, we perform a database query and return all the nodes whose titles contain that value
  • In any other case (which essentially means the lack of a parameter), we query the db and return the latest 10 nodes (just as an example)

Obviously this callback can be further improved and consolidated (error handling, etc), but for demonstration purposes, it will work just fine. Let’s now create a theme that uses a template file and a custom block that will render it:

/**
 * Implements hook_theme().
 */
function ang_theme($existing, $type, $theme, $path) {
  return array(
    'angular_listing' => array(
      'template' => 'angular-listing',
      'variables' => array()
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function ang_block_info() {

  $blocks['angular_nodes'] = array(
    'info' => t('Node listing'),
  );

  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function ang_block_view($delta = '') {

  $block = array();

  switch ($delta) {
    case 'angular_nodes':
      $block['subject'] = t('Latest nodes');
      $block['content'] = array(
        '#theme' => 'angular_listing',
        '#attached' => array(
          'js' => array(
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js',
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-resource.js',
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog.min.js',
            drupal_get_path('module', 'ang') . '/ang.js',
          ),
          'css' => array(
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog.min.css',
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog-theme-default.min.css',
          ),
        ),
      );
      break;
  }

  return $block;
}

/**
 * Implements template_preprocess_angular_listing().
 */
function ang_preprocess_angular_listing(&$vars) {
  // Can stay empty for now.
}

There are four simple functions here:

  1. Using hook_theme() we create our angular_listing theme that uses the angular-listing.tpl.php template file we will create soon.
  2. Inside the hook_block_info() we define our new block, the display of which is being controlled inside the next function.
  3. Using hook_block_view() we define the output of our block: a renderable array using the angular_listing theme and which has the respective javascript and css files attached. From the Google CDN we load the Angular.js library files, inside ang.js we will write our JavaScript logic and in the /lib/ngDialog folder we have the library for creating dialogs. It’s up to you to download the latter and place it in the module following the described structure. You can find the files either in the repository or on the library website.
  4. The last function is a template preprocessor for our template in order to make sure the variables are getting passed to it (even if we are actually not using any).

As you can see, this is standard boilerplate Drupal 7 code. Before enabling the module or trying out this code, let’s quickly create the template file so Drupal doesn’t error out. Inside a file called angular-listing.tpl.php, add the following:

        <div ng-app="nodeListing">
        
           <div ng-controller="ListController">
        
             <h3>Filter</h3>
             <input ng-model="search" ng-change="doSearch()">
        
              <ul>
                <li ng-repeat="node in nodes"><button ng-click="open(node.nid)">Open</button> {{ node.title }}</li>
              </ul>
        
             <script type="text/ng-template" id="loadedNodeTemplate">
             <h3>{{ loadedNode.title }}</h3>
             {{ loadedNode.body }}
             </script>
        
            </div>
        
        </div>

Here we have some simple HTML pimped up with Angular.js directives and expressions. Additionally, we have a <script> tag used by the ngDialog module as the template for the dialog. Before trying to explain this, let’s create also our ang.js file and add our javascript to it (since the two are so connected):

angular.module('nodeListing', ['ngResource', 'ngDialog'])

  // Factory for the ngResource service.
  .factory('Node', function($resource) {
    return $resource(Drupal.settings.basePath + 'api/node/:param', {}, {
      'search' : {method : 'GET', isArray : true}
    });
  })

  .controller('ListController', ['$scope', 'Node', 'ngDialog', function($scope, Node, ngDialog) {
    // Initial list of nodes.
    $scope.nodes = Node.query();

    // Callback for performing the search using a param from the textfield.
    $scope.doSearch = function() {
      $scope.nodes = Node.search({param: $scope.search});
    };

    // Callback to load the node info in the modal
    $scope.open = function(nid) {
      $scope.loadedNode = Node.get({param: nid});
      ngDialog.open({
        template: 'loadedNodeTemplate',
        scope: $scope
      });
    };

}]);

Alright. Now we have everything (make sure you also add the ngDialog files as requested in the #attached key of the renderable array we wrote above). You can enable the module and place the block somewhere prominent where you can see it. If all went well, you should get 10 node titles (if you have so many) and a search box above. Searching will make AJAX calls to the server to our endpoint and return other node titles. And clicking on them will open up a dialog with the node title and body on it. Sweet.

But let me explain what happens on the Angular.js side of things as well. First of all, we define an Angular.js app called nodeListing with the ngResource (the Angular.js service in charge communicating with the server) and ngDialog as its dependencies. This module is also declared in our template file as the main app, using the ng-app directive.

Inside this module, we create a factory for a new service called Node which returns a $resource. The latter is in fact a connection to our data on the server (the Drupal backend accessed through our endpoint). In addition to the default methods on it, we define another one called .search() that will make a GET request and return an array of results (we need a new one because the default .get() does not accept an array of results).

Below this factory, we define a controller called ListController (also declared in the template file using the ng-controller directive). This is our only controller and it’s scope will apply over all the template. There are a few things we do inside the controller:

  1. We load nodes from our resource using the query() method. We pass no parameters so we will get the latest 10 nodes on the site (if you remember our endpoint callback, the request will be made to /api/node). We attach the results to the scope in a variable called nodes. In our template, we loop through this array using the ng-repeat directive and list the node titles. Additionally, we create a button for each with an ng-click directive that triggers the callback open(node.nid) (more on this at point 3).
  2. Looking still at the template, above this listing, we have an input element whose value will be bound to the scope using the ng-model directive. But using the ng-change directive we call a function on the scope (doSearch()) every time a user types or removes something in that textfield. This function is defined inside the controller and is responsible for performing a search on our endpoint with the param the user has been typing in the textfield (the search variable). As the search is being performed, the results populate the template automatically.
  3. Lastly, for the the bonus part, we define the open() method which takes a node id as argument and requests the node from our endpoint. Pressing the button, this callback function opens the dialog that uses a template defined inside of the <script> tag with the id of loadedNodeTemplate and passes to it the current scope of the controller. And if we turn to the template file, we see that the dialog template simply outputs the title and the body of the node.

Conclusion

You can see for yourself the amount of code we wrote to accomplish this neat functionality. Most of it is actually boilerplate. A very fast node query block that delivers results asynchronously with all of its benefits. And if you know Angular.js, you can imagine the possibility of enhancing the Drupal experience further.

Now, are you interested to learn more about the love between Angular.js and Drupal? Would you have done anything differently? Let us know in the comments below!

Nov 24 2014
Nov 24

If you do a lot of Drupal development and need to deploy configuration I am sure that you are using update hooks to some extent at least. If you don't use Features and want to create a taxonomy vocabulary or something in code, the hook_update_N() hook is the way to go.

But have you ever needed to perform an update the size of which would exceed PHP's maximum execution time? If you need to create 1000 entities (let's just say as an example), it's not a good idea to trust that the production server will not max out and leave you hanging in the middle of a deploy. So what's the solution?

You can use the batch capability of the update hook. If you were wondering what the &$sandbox argument is for, it's just for that. You use it for two things mainly:

  • store data required for your operations across multiple passes (since it is passed by reference the values remain)
  • tell Drupal when it should stop the process by setting the $sandbox['#finished'] value to 1.

Let me show you how this works. Let's say we want to create a vocabulary and a bunch of taxonomy terms with names from a big array. We want to break this array into chunks and create the terms one chunk at the time so as to avoid the load on the server.

So here is how you do it:

/**
 * Create all the terms
 */
function my_module_update_7001(&$sandbox) {

  $names = array(
    'Fiona',
    'Jesse',
    'Michael',
    ...
    'Sam',
    'Nate',
  );

  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;
    $sandbox['limit'] = 5;
    $sandbox['max'] = count($names);

    // Create the vocabulary
    $vocab = (object) array(
      'name' => 'Names',
      'description' => 'My name vocabulary.',
      'machine_name' => 'names_vocabulary',
    );

    taxonomy_vocabulary_save($vocab);
    $sandbox['vocab'] = taxonomy_vocabulary_machine_name_load('names_vocabulary');
  }

  // Create the terms
  $chunk = array_slice($names, $sandbox['progress'], $sandbox['limit']);
  if (!empty($chunk)) {
    foreach ($chunk as $key => $name) {
      $term = (object) array(
        'name' => $name,
        'description' => 'The name is: ' . $name,
        'vid' => $sandbox['vocab']->vid,
      );

      taxonomy_term_save($term);
      $sandbox['progress']++;
    }
  }

  $sandbox['#finished'] = ($sandbox['progress'] / $sandbox['max']);


}

So what happens here? First, we are dealing with an array of names (can anybody recognise them by the way?) Then we basically see if we are at the first pass by checking if we had set already the progress key in $sandbox. If we are at the first pass, we set some defaults: a limit of 5 terms per pass out of a total of count($names). Additionally, we create the vocabulary and store it as a loaded object in the sandbox as well (because we need its id for creating the terms).

Then, regardless of the pass we are on, we take a chunk out of the names always offset by the progress of the operation. And with each term created, we increment this progress by one (so with each chunk, the progress increases by 5) and of course create the terms. At the very end, we keep setting the value of $sandbox['#finished'] to the ratio of progress per total. Meaning that with each pass, this value increases from an original of 0 to a maximum of 1 (at which point Drupal knows it needs to stop calling the hook).

And like this, we save a bunch of terms without worrying that PHP will time out or the server will be overloaded. Drupal will keep calling the hook as many times as needed. And depending on the operation, you can set your own sensible chunk sizes.

Hope this helps.

Oct 20 2014
Oct 20

How to Build a Drupal 8 Module

Please be aware that due to the development process Drupal 8 has been undergoing at the time of writing, some parts of the code might be outdated. Take a look at this repository in which I try to update the example code and make it work with the latest Drupal 8 release.

With the incorporation of many Symfony components into Drupal in its 8th version, we are seeing a shift away from many Drupalisms towards more modern PHP architectural decisions. For example, the both loved and hated hook system is getting slowly replaced. Plugins and annotations are taking away much of the need for info hooks and the Symfony Event Dispatcher component is replacing some of the invoked hooks. Although they remain strong in Drupal 8, it’s very possible that with Drupal 9 (or maybe 10) hooks will be completely removed.

In this article we are going to primarily look at how the Symfony Event Dispatcher component works in Drupal. Additionally, we will see also how to invoke and then implement a hook in Drupal 8 to achieve similar goals as with the former.

To follow along or to get quickly started, you can find all the code we work with here in this repository. You can just install the module and you are good to go. The version of Drupal 8 used is the first BETA release so it’s preferable to use that one to ensure compatibility. Alpha 15 should also work just fine. Let’s dive in.

What is the Event Dispatcher component?

A very good definition of the Event Dispatcher component can be found on the Symfony website:

The EventDispatcher component provides tools that allow your application components to communicate with each other by dispatching events and listening to them.

I recommend reading up on that documentation to better understand the principles behind the event dispatcher. You will get a good introduction to how it works in Symfony so we will not cover that here. Rather, we will see an example of how you can use it in Drupal 8.

Drupal 8 and the Event Dispatcher

For the better part of this article, we will focus on demonstrating the use of the Event Dispatcher in Drupal 8. To this end, we will create a simple demo module (event_dispatcher_demo) that has a configuration form which saves two values as configuration. Upon saving this form, we will dispatch an event that contains the config object and which will allow other parts of the application to intercept and modify it before being saved. Finally, we will do just that by demonstrating how to subscribe (or listen) to these events.

In Drupal 7, this type of modularity is only achieved with hooks. Hooks are being invoked and modules have the option to implement them and contribute with their own data. At the end of this article, we will see how to do that as well in Drupal 8. But first, let’s get on with our demo module.

If you don’t know the basics of Drupal 8 module development, I recommend checking out my previous articles in this series.

The form

The first thing we need is a simple config form with two fields. In a file called DemoForm.php located in the src/Form folder, we have the following:

<?php

/**
 * @file
 * Contains Drupal\event_dispatcher_demo\Form\DemoForm.
 */

namespace Drupal\event_dispatcher_demo\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

class DemoForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormID() {
    return 'demo_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('event_dispatcher_demo.demo_form_config');
    $form['my_name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('My name'),
      '#default_value' => $config->get('my_name'),
    ];
    $form['my_website'] = [
      '#type' => 'textfield',
      '#title' => $this->t('My website'),
      '#default_value' => $config->get('my_website'),
    ];
    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    parent::submitForm($form, $form_state);

    $config = $this->config('event_dispatcher_demo.demo_form_config');

    $config->set('my_name', $form_state->getValue('my_name'))
      ->set('my_website', $form_state->getValue('my_website'));
      
    $config->save();
  }

}

Let’s also create a route for it (in the event_dispatcher_demo.routing.yml file) so we can access the form in the browser:

event_dispatcher_demo.demo_form:
  path: 'demo-form'
  defaults:
    _form: '\Drupal\event_dispatcher_demo\Form\DemoForm'
    _title: 'Demo form'
  requirements:
    _permission: 'access administration pages'

So now if you point your browser to example.com/demo-form, you should see the form. Submitting it will create and persist a configuration object called event_dispatcher_demo.demo_form_config that contains two fields: my_name and my_website .

The event dispatcher

Now it’s time to work on the form submit handler (the formSubmit() method) and dispatch an event when the form is saved. This is what the new method will look like:

public function submitForm(array &$form, FormStateInterface $form_state) {

  parent::submitForm($form, $form_state);

  $config = $this->config('event_dispatcher_demo.demo_form_config');

  $config->set('my_name', $form_state->getValue('my_name'))
    ->set('my_website', $form_state->getValue('my_website'));

  $dispatcher = \Drupal::service('event_dispatcher');

  $e = new DemoEvent($config);

  $event = $dispatcher->dispatch('demo_form.save', $e);

  $newData = $event->getConfig()->get();

  $config->merge($newData);

  $config->save();
}

So what happens here? After we take the submitted values and add them to the config object like before, we retrieve the event dispatcher object from the service container:

$dispatcher = \Drupal::service('event_dispatcher');

Please keep in mind that it’s recommended you inject this service into your class, but for brevity, we will retrieve it statically. You can read this article about dependency injection and the service container for more information.

Then we create a new DemoEvent object and pass it the $config through its constructor (we have not yet created the DemoEvent class, we will do that in a minute). Next, we use the dispatcher to dispatch an event of our type and assign this action the identifier demo_form.save. This will be used when subscribing to events (we’ll see this later). The dispatch() method returns the event object with modifications made to it so we can retrieve the config values that may or may not have been altered elsewhere and merge them into our original configuration. Finally, we save this object like we did initially.

Before moving onto the event subscription part of our application, let’s create the DemoEvent class we just instantiated above. In a file called DemoEvent.php located in the src/ folder of our module, we have the following:

<?php

/**
 * @file
 * Contains Drupal\event_dispatcher_demo\DemoEvent.
 */

namespace Drupal\event_dispatcher_demo;

use Symfony\Component\EventDispatcher\Event;
use Drupal\Core\Config\Config;

class DemoEvent extends Event {

  protected $config;

  /**
   * Constructor.
   *
   * @param Config $config
   */
  public function __construct(Config $config) {
    $this->config = $config;
  }

  /**
   * Getter for the config object.
   *
   * @return Config
   */
  public function getConfig() {
    return $this->config;
  }

  /**
   * Setter for the config object.
   *
   * @param $config
   */
  public function setConfig($config) {
    $this->config = $config;
  }

} 

As you can see, this is a simple class that extends the default Event class and which defines setters and getters for the config object we will be passing around using this event. And since we created it, let’s also make sure we use it in the file where we defined the form:

use Drupal\event_dispatcher_demo\DemoEvent;

The event subscriber

Now that our form is functioning normally and an event is being dispatched when the form is saved, we should take advantage of that and subscribe to it. Let’s start with the event subscriber class that implements the EventSubscriberInterface. Inside a file called ConfigSubscriber.php (name of your choice) located in the src/EventSubscriber/ folder, we have the following:

<?php

/**
 * @file
 * Contains Drupal\event_dispatcher_demo\EventSubscriber\ConfigSubscriber.
 */

namespace Drupal\event_dispatcher_demo\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ConfigSubscriber implements EventSubscriberInterface {

  static function getSubscribedEvents() {
    $events['demo_form.save'][] = array('onConfigSave', 0);
    return $events;
  }

  public function onConfigSave($event) {

    $config = $event->getConfig();

    $name_website = $config->get('my_name') . " / " . $config->get('my_website');
    $config->set('my_name_website', $name_website);
  }

}

So what happens here? The EventSubscriberInterface has only one required method called getSubscribedEvents(). This method is used to register events and callbacks to these events. So above we registered the callable onConfigSave() (found in the same class below) to the event dispatched with the identifier of demo_form.save. And in the callback method we simply add another value to the config object (based on a concatenation of the existing two values). The latter part is just for our demo purposes: here you can do what you want.

When we subscribed our onConfigSave() method to listen to the demo_form.save event, we passed a weight of 0. If you register multiple callbacks to the same event, this weight becomes important (the higher the number, the earlier it gets called). And if a callback alters the same values as one triggered before, they will get overridden. It’s good to keep this in mind.

Now in order for this event subscriber to work, we need to define it as a service and give it the event_subscriber tag. So in a file called event_dispatcher_demo.services.yml found in the root folder of our module, we will have this:

services:
  event_dispatcher_demo.config_subscriber:
    class: Drupal\event_dispatcher_demo\EventSubscriber\ConfigSubscriber
    tags:
      - { name: event_subscriber }

This is a simple service definition with the right tag that will make the container automatically instantiate an object of this class whenever the dispatcher is in play. And that is pretty much it. Clear the cache and if you now save the form again, the configuration object that gets saved will always contain a new value that is based on the first two.

Hooks

In the final part of this article we will demonstrate the use of hooks to achieve a similar goal.

First, let’s change the form submit handler and instead of dispatching events, we will invoke a hook and pass the config values to it. This is what the new submitForm() method will look like:

public function submitForm(array &$form, FormStateInterface $form_state) {

        parent::submitForm($form, $form_state);
        
        $config = $this->config('event_dispatcher_demo.demo_form_config');
        
        $config->set('my_name', $form_state->getValue('my_name'))
          ->set('my_website', $form_state->getValue('my_website'));
        
        $configData = $config->get();
        $newData = \Drupal::service('module_handler')->invokeAll('demo_config_save', array($configData));
        
        $config->merge($newData);
        
        $config->save();
}

We are not using any event objects nor the dispatcher service. Instead, we retrieve the Module Handler service that contains the invokeAll() method used to invoke hook implementations from all modules. This is essentially replacing the Drupal 7 module_invoke_all() helper. And again, it is recommended to inject this service, but for brevity, we’ll retrieve it statically.

The hook implementation invoked in our case is hook_demo_config_save and it gets one parameter, an array of values pulled from our config object. Inside $newData we will have an array of values merged from all the implementations of this hook. We then merge that into our config object and finally save it.

Let’s quickly see an example hook implementation. As with Drupal 7, these can only be in .module files:

/**
 * Implements hook_demo_config_save().
 */
function event_dispatcher_demo_demo_config_save($configValues) {

  $configValues['my_name_website'] = $configValues['my_name'] . " / " . $configValues['my_website'];

  return $configValues;

}

As you can see, we are adding a new value to the config array that will later be merged into the object getting persisted. And we have essentially the same thing as we did with the event dispatcher.

Conclusion

In this article we have taken a look at how the Symfony Event Dispatcher component works in Drupal 8. We’ve learned how flexible it makes our application when it comes to allowing others to extend functionality. Additionally, we’ve seen how the invoked hooks work in the new version of Drupal. Not much has changed since Drupal 7 in this respect apart from the frequency with which they are used. Many hooks have been replaced by plugins and annotations and the Event Dispatcher component has also taken on a big chunk of what was in D7 a hook responsibility.

Although the Event Dispatcher approach is more verbose, it is the recommended way to go forward. Where possible, we no longer use the old procedural approach characteristic to hooks but rather object oriented, decoupled and testable solutions. And Symfony helps greatly with that.

Sep 08 2014
Sep 08

Have you ever needed to remove in bulk a bunch of voting results for, let's say, a given content type? There is no option in the UI but you can find in the votingapi.module some handy functions that will allow you to write a customized update hook.

So let's say that we need to remove all the results for the article content type. If we look in the votingapi_vote table, we don't see any bundle or content type column, but we see an entity_id. So we need to get all the ids of our article nodes:

  $query = db_query("SELECT nid FROM node WHERE type = 'article'");
  foreach ($query as $res) {
    $nids[] = $res->nid;
  }

Now we have the $nids array containing all of our node IDs. Next, let's load all the votes for these IDs:

  module_load_include('module', 'votingapi', 'votingapi.module');

  $votes = votingapi_select_votes(array('entity_id' => $nids));

First we include the right module file and then we use one of its functions to select all the votes that match some criteria (in our case an array of IDs). Next, we need to worry also about the votingapi_cache table which contains the results of the voting per entity. We need to remove that as well. So we'll use another helper function from Voting API:

$results = votingapi_select_results(array('entity_id' => $nids));

Now we have also the result objects we need to delete so we can proceed with the actual removal. For this, we can use two more handy methods from the Voting API module:

votingapi_delete_votes($votes);
votingapi_delete_results($results);

And that's it. This will remove all the votes and their aggregated results from both tables. It may take some time so make sure you have enough server resources to perform this task.

To use this code, I recommend creating an update hook in a custom module that you run once. But make sure you properly test it on your test environment before deploying and running the code on production servers. Always keep in mind the possibility of the server running out of resources depending on how many votes you have in the database.

Do you have any better way of batch deleting votes/results? This is what I found and I'm curious if you know of any better ways. Let me know.

Sep 01 2014
Sep 01

Have you ever had to sort an array in PHP? There are a bunch of functions available, the most common being sort(). This function does a default sorting of the values in your array. So if you have numbers or want to do alphabetical sorting, sort() will get the job done.

But what if you require a more complex sorting logic? Let's say you have an array of names that need to be sorted in a specific order. I don't know, because your client says so. Let me show you how.

Let's say your DB returns a list of names in an array:

$names = array("Christian", "Daniel", "Adrian");

And your client says they need to be arranged in the following order: Adrian, Christian, Daniel. You can use the usort() function together with a comparator function you write yourself. How does this system work?

As a second argument of the usort() function, we pass in the name of our custom comparator function. The way this gets processed is that all the values in the array that needs sorting are passed to this function 2 at the time (usually you'll see them as $a and $b). This is done to determine which one takes precedence over which. The comparator has to return 0 if the 2 values are considered equal, a negative integer if the first value is less than the second value or a positive integer if the second value is less than the first. What less means is up to you to determine. So let's put this in practice with our example:

function _name_comparer($a, $b) {
    $correct_order = array("Adrian", "Christian", "Daniel");

    $a_key = array_search($a, $correct_order);
    $b_key = array_search($b, $correct_order);

    if ($a_key == $b_key) {
        return 0;
    }

    return ($a_key < $b_key) ? -1 : 1;
}

usort($names, "_name_comparer");

So what happens in my comparator function? First, I create an array that contains the proper order of the names. This means that each value has an integer key that can be easily compared (and that I store in the $a_key and $b_key variables). After comparing these, I return 0, a negative or positive integer. The result is that the $names array gets resorted in the order they appear in the $correct_order local variable I created. And that's it.

If the $names variable is associative and you need to maintain the keys as they were, you can use the uasort() function:

$names = array(
    "christian" => "Christian", 
    "daniel" => "Daniel", 
    "adrian" => "Adrian",
);
uasort($names, "_name_comparer");

The comparator function can stay the same, but the uasort() function will take into account and maintain the index association of your values.

And that's it. Hope this helps.

Jul 14 2014
Jul 14

In this article we will look at how to define a configuration entity type that will serve a simple purpose, describe dummy flower configuration entities. We will do so in a module called flower and will use the alpha13 release of Drupal 8 to do it.

Before we get started, let's define a practical goal for this tutorial. As I said, we will have a flower config entity type with a couple of properties: name, number of petals, color and season. And by the end, we will have a fully fledged UI to create and manage them. The final code you can also find in this repository.

So let's begin.

The configuration entity interface

The first thing we need to do is define an interface our Flower entity type class can implement and that extends the default ConfigEntityInterface. So inside of our module's src/ folder, create a file called FlowerInterface.php with the following interface:

/**
 * @file
 * Contains \Drupal\flower\FlowerInterface.
 */
 
namespace Drupal\flower;
 
use Drupal\Core\Config\Entity\ConfigEntityInterface;
 
/**
 * Provides an interface defining a flower entity type.
 */
interface FlowerInterface extends ConfigEntityInterface {
 
}

As you can see, we are just extending the default configuration entity interface without adding any methods to it (which is possible).

The configuration entity class

Next, we will focus on the crux of defining our own configuration entity class. Go ahead and create a folder inside the src/ directory called Entity, and within it, a file called FlowerEntity.php:

/**
 * @file
 * Contains \Drupal\flower\Entity\FlowerEntity.
 */
 
namespace Drupal\flower\Entity;
 
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\flower\FlowerInterface;
 
    /**
     * Defines a Flower configuration entity class.
     *
     * @ConfigEntityType(
     *   id = "flower",
     *   label = @Translation("Flower"),
     *   fieldable = FALSE,
     *   controllers = {
     *     "list_builder" = "Drupal\flower\FlowerListBuilder",
     *     "form" = {
     *       "add" = "Drupal\flower\Form\FlowerForm",
     *       "edit" = "Drupal\flower\Form\FlowerForm",
     *       "delete" = "Drupal\flower\Form\FlowerDeleteForm"
     *     }
     *   },
     *   config_prefix = "flower",
     *   admin_permission = "administer site configuration",
     *   entity_keys = {
     *     "id" = "id",
     *     "label" = "name"
     *   },
     *   links = {
     *     "edit-form" = "flower.edit",
     *     "delete-form" = "flower.delete"
     *   }
     * )
     */
    class FlowerEntity extends ConfigEntityBase implements FlowerInterface {
 
      /**
       * The ID of the flower.
       *
       * @var string
       */
      public $id;
 
      /**
       * The flower name.
       *
       * @var string
       */
      public $name;
 
      /**
       * The flower color.
       *
       * @var string
       */
      public $color;
 
      /**
       * The number of petals.
       *
       * @var int
       */
      public $petals;
 
      /**
       * The season in which this flower can be found.
       *
       * @var string
       */
      public $season;
 
    }

What we have here is a simple class defining the entity properties we want (name, id, color, number of petals and season). This class extends the default ConfigEntityBase class and implements our interface. What happens above the class definition is what's interesting though.

Using annotations, we are basically telling Drupal about our Flower entity type.

The @ConfigEntityType tells Drupal that this is a configuration entity type (as opposed to a plugin or something else). Within its definition, we have an array-like structure with the following information (I will only mention the keys that are not super obvious):

  • label - the label of the entity type passed through the translation system.
  • fieldable - the configuration entities are not fieldable, but the content entities are. Since we are using the same entity API, we can specify this.
  • controllers - all the classes needed to manage these entities. The list_builder class will provide an admin overview interface of the entities, whereas the form classes are used to perform the CRUD operations through the UI.
  • config_prefix - a configuration identifier
  • entity keys - mapping of the main entity keys to the entity properties we defined. For instance, when we call the label() method on the entity object, it will return the flower name.
  • links - administration links for editing and deleting entities with values referencing routes. Specifying them here will make Drupal add them automatically to the operations column on the entity overview page (we'll see this in a minute).

For more information about the structure of an entity class annotation, follow this documentation page.

The entity forms

The next thing we need to do is create the forms we referenced in the annotations above: for adding, editing and deleting flower entities. The cool thing is that the form for adding can be reused for editing as well. For delete, we extend a special class that gives us all we need for a confirmation form. But first, the add/edit form (FlowerForm.php) inside of the src/Form/ folder:

/**
 * @file
 * Contains \Drupal\flower\Form\FlowerForm.
 */
 
namespace Drupal\flower\Form;
 
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Url;
 
/**
 * Class FlowerForm
 *
 * Form class for adding/editing flower config entities.
 */
class FlowerForm extends EntityForm {
 
   /**
   * {@inheritdoc}
   */
  public function form(array $form, array &$form_state) {
 
    $form = parent::form($form, $form_state);
 
    $flower = $this->entity;
 
    // Change page title for the edit operation
    if ($this->operation == 'edit') {
      $form['#title'] = $this->t('Edit flower: @name', array('@name' => $flower->name));
    }
 
    // The flower name.
    $form['name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#maxlength' => 255,
      '#default_value' => $flower->name,
      '#description' => $this->t("Flower name."),
      '#required' => TRUE,
    );
 
    // The unique machine name of the flower.
    $form['id'] = array(
      '#type' => 'machine_name',
      '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
      '#default_value' => $flower->id,
      '#disabled' => !$flower->isNew(),
      '#machine_name' => array(
        'source' => array('name'),
        'exists' => 'flower_load'
      ),
    );
 
    // The flower color.
    $form['color'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Color'),
      '#maxlength' => 255,
      '#default_value' => $flower->color,
      '#description' => $this->t("Flower color."),
      '#required' => TRUE,
    );
 
    // The number of petals.
    $form['petals'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Petals'),
      '#maxlength' => 255,
      '#default_value' => $flower->petals,
      '#description' => $this->t("The number of petals."),
      '#required' => TRUE,
    );
 
    // The season.
    $form['season'] = array(
      '#type' => 'select',
      '#options' => array(
        'Spring' => 'Spring',
        'Summer' => 'Summer',
        'Automn' => 'Automn',
        'Witer' => 'Winter'
      ),
      '#title' => $this->t('Season'),
      '#maxlength' => 255,
      '#default_value' => $flower->season,
      '#description' => $this->t("The season in which this flower grows."),
      '#required' => TRUE,
    );
 
    return $form;
  }
 
  /**
   * {@inheritdoc}
   */
  public function save(array $form, array &$form_state) {
 
    $flower = $this->entity;
 
    $status = $flower->save();
 
    if ($status) {
      // Setting the success message.
      drupal_set_message($this->t('Saved the flower: @name.', array(
        '@name' => $flower->name,
      )));
    }
    else {
      drupal_set_message($this->t('The @name flower was not saved.', array(
        '@name' => $flower->name,
      )));
    }
    $url = new Url('flower.list');
    $form_state['redirect'] = $url->toString();
 
  }
 
} 

In our FlowerForm class we are extending the Drupal EntityForm class and implementing 2 of its methods: form() and save(). In the first one, we define a regular Form API form very similar to what we do in Drupal 7. But there are a few cool new things happening there as well:

  • We extend the parent form and add our elements to that definition.
  • We get the configuration entity object from the entity property of the parent class.
  • We check the operation being performed on the entity and if the user is editing it, we change the title of the page to reflect this
  • Instead of using the procedural t() function, we access $this->t() on the parent class for best practice.
  • We access the config entity public properties and set them as the defaults in the form elements' definition.
  • For the machine_name, we use the flower_load() helper function (that we will need to define in our .module file) in order to automatically check whether an entity with that ID already exists.

In the save() method we perform the simple operation of saving the entity object to the configuration system. Couldn't get simpler than this. And after the save is performed, we redirect to the flower entity overview page. Here we use the Url class to build a url object based on a route (that we will define later).

Next, let's quickly create the delete form.

Inside the same src/Form/ folder, create a FlowerDeleteForm.php file with the following class:

/**
 * @file
 * Contains \Drupal\flower\Form\FlowerDeleteForm.
 */
namespace Drupal\flower\Form;
 
use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Url;
 
/**
 * Form that handles the removal of flower entities.
 */
class FlowerDeleteForm extends EntityConfirmFormBase {
 
  /**
   * {@inheritdoc}
   */
  public function getQuestion() {
    return $this->t('Are you sure you want to delete this flower: @name?', array('@name' => $this->entity->name));
  }
  /**
   * {@inheritdoc}
   */
  public function getCancelRoute() {
    return new Url('flower.list');
  }
  /**
   * {@inheritdoc}
   */
  public function getConfirmText() {
    return $this->t('Delete');
  }
  /**
   * {@inheritdoc}
   */
  public function submit(array $form, array &$form_state) {
 
    // Delete and set message
    $this->entity->delete();
    drupal_set_message($this->t('The flower @label has been deleted.', array('@label' => $this->entity->name)));
    $form_state['redirect_route'] = $this->getCancelRoute();
 
  }
}

With this form class we are extending the Drupal EntityConfirmFormBase that provides us with all we need for a delete confirmation form. By implementing these self-explanatory methods, we take care of the entity delete process. Finally, it's time to define the admin overview page.

The entity list builder

As we declared when defining the config entity class, we now need a class file responsible for building the overview page of our entities. So straight in the src/ folder of our module you can create a FlowerListBuilder.php class file with the following class:

/**
 * @file
 *
 * Contains Drupal\flower\FlowerListBuilder
 */
 
namespace Drupal\flower;
 
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
 
 
class FlowerListBuilder extends ConfigEntityListBuilder {
 
  /**
   * {@inheritdoc}
   */
  public function buildHeader() {
    $header['label'] = $this->t('Name');
    $header['color'] = $this->t('Color');
    $header['petals'] = $this->t('Number of petals');
    $header['season'] = $this->t('Season');
    return $header + parent::buildHeader();
  }
 
  /**
   * {@inheritdoc}
   */
  public function buildRow(EntityInterface $entity) {
 
    // Label
    $row['label'] = $this->getLabel($entity);
 
    // Color
    $row['color'] = $entity->color;
 
    // Petals
    $row['petals'] = $entity->petals;
 
    // Season
    $row['season'] = $entity->season;
 
    return $row + parent::buildRow($entity);
  }
 
  /**
   * {@inheritdoc}
   */
  public function render() {
 
    $build = parent::render();
 
    $build['#empty'] = $this->t('There are no flowers available.');
    return $build;
  }
 
} 

In this class that extends the ConfigEntityListBuilder, we implement three methods. The buildHeader() method is responsible for creating the table header of our overview page whereas buildRow() will create the rows based on the number of entities and their values. Lastly, we are overriding the render() method so that we can specify a custom message to display in case there are no entities to show (personal preference). And that's basically it with the list builder class.

Miscellaneous

There are a few more things we need to take care of in order to round up our configuration entity type. The first one has just became kind of mandatory so I'll start with that: the configuration schema. So let's quickly create the folder structure inside our module (config/schema/) and inside a file called flower.schema.yml we can have the following:

# Schema for the configuration files of the Flower module.

    flower.flower.*:
      type: mapping
      label: 'Flower'
      mapping:
        id:
          type: string
          label: 'Flower identifier'
        uuid:
          type: string
          label: 'UUID'
        name:
          type: label
          label: 'Name'
        color:
          type: string
          label: 'Color'
          translatable: true
        petals:
          type: integer
          label: 'Number of petals'
        season:
          type: string
          label: 'Season'
          translatable: true

On the first line (after the comment) we start defining the schema for the (flower module).(flower configuration entity type).(all flower configuration entities). And it follows to map all the entity properties and specify what data type they are. Although the uuid property was not defined by us, Drupal adds it by default and we can specify it here.

As far as I could tell, the label-typed properties become translatable automatically whereas for all the rest we want translatable we can specify translatable: true. Translation is one of the biggest reasons for which we use these schemas for configuration entities.

And now that the schema is taken care of, it's time for some finishing touches. First, let's create our routes so that we can access everything in the browser. Inside of a file called flower.routing.yml in the module root folder, add the following:

flower.list:
  path: '/admin/structure/flowers'
  defaults:
    _entity_list: 'flower'
    _title: 'Flowers'
  requirements:
    _permission: 'administer site configuration'
flower.add:
  path: '/admin/structure/flowers/add'
  defaults:
    _entity_form: 'flower.add'
    _title: 'Add a new flower'
  requirements:
    _permission: 'administer site configuration'
flower.edit:
  path: '/admin/structure/flowers/edit/{flower}'
  defaults:
    _entity_form: 'flower.edit'
    _title: 'Edit flower'
  requirements:
    _permission: 'administer site configuration'
flower.delete:
  path: '/admin/structure/flowers/delete/{flower}'
  defaults:
    _entity_form: 'flower.delete'
    _title: 'Delete flower'
  requirements:
    _permission: 'administer site configuration'

For more information about the structure of a route file (and what the above keys actually mean), please consult this documentation page. But an important take-away are the paths we defined at admin/structure/flowers.

Second, on the flower overview page, we'd probably like a link to add new flowers to the site. So let's create another YML file in the root of our module called flower.local_actions.yml to define that link:

flower.add:
  route_name: 'flower.add'
  title: 'Add flower'
  appears_on:
    - flower.list

This is a simple local action link definition called flower.add that uses the flower.add route and appears on the page given by the route flower.list. For more information about defining local actions, consult this documentation page.

Third, we can create a menu link under the Structure admin menu that will take us to the flower overview page. So inside of a file called flower.menu_links.yml in the module root folder, add the following:

flower.list:
  title: Flowers
  description: 'Administer the flower entities'
  parent: system.admin_structure
  route_name: flower.list

Here we create a link called flower.list found under the system.admin_structure link and that uses the flower.list route name. Simple.

Finally, we need to create the auto loader function that will be used by the machine_name form element to check whether an entity with a given machine name already exists (on the flower add form). So inside the flower.module file, create this function:

/**
 * Menu argument loader. Returns a flower entity
 *
 * @param $id
 * @return \Drupal\Core\Entity\EntityInterface|static
 */
function flower_load($id) {
  return FlowerEntity::load($id);
}

And don't forget to use the FlowerEntity class at the top of the file:

use \Drupal\flower\Entity\FlowerEntity;

And that should be about it. Clear the caches, make sure the module is enabled, and start poking at it. Navigate to /admin/structure/flowers and create, edit, delete flower entities. Additionally, you can turn on configuration translation and translate all your entities into multiple languages. Cool, no?

Conclusion

In this tutorial we've looked at how we can create our own simple configuration entity type in Drupal 8. The alpha13 version (latest at the time of writing) has been used for this, so make sure that if you are using a newer one you make the necessary code adaptations if needed.

In Drupal 7 we do not have configuration entities and we are left with creating custom tables that hold data meant as configuration. And obviously, integration with the modest D7 entity API is practically inexistent. This all changes in Drupal 8 with the development of a robust entity API - fully integrated with the multilingual and configuration systems. Because of this, we now have exportable and translatable configuration entities used to manage more complex data that is not content.

And with all these new developments, we are being introduced to a few new concepts that can scare us a bit (services, dependency injection, plugins, OOP and so on). However, once we get used to them a bit, they will become a friend rather than foe and open the door to more sane, performant and modern development within the Drupal framework.

Jul 05 2014
Jul 05

How to Build a Drupal 8 Module

Even though Drupal 7 core fell short of a proper way of handling its brand new entity system (we currently rely on the great Entity module for that), it did give us EntityFieldQuery. For those of you who don’t know, EntityFieldQuery is a very powerful querying class used to search Drupal entities programatically (nodes, users, etc).

It provides a number of methods that make it easy to query entities based on conditions such as field values or class properties. If you don’t know how it works, feel free to check out this documentation page or this great tutorial on the subject.

In this article I am going to talk about what we have in Drupal 8 for querying entities. There is no more EntityFieldQuery, but there’s an entity.query service that will instantiate a query object for a given entity type (and that implements the \Drupal\Core\Entity\Query\QueryInterface). We can access this service statically through the \Drupal namespace or using dependency injection.

First up, we’ll look at querying node entities and then we’ll see how to load them. The same techniques will work with other content entities as well (users, comments etc), but also with configuration entities, and that’s really cool.

The entity query service

As mentioned, there are two ways we can access the entity.query service that we use for querying entities. Statically, we can do this:

$query = \Drupal::entityQuery('node');

Instead of node, we can specify any other entity type machine name and what we get inside the $query variable is the query object for our entity type. The entityQuery() static method on the \Drupal namespace is a shortcut for doing so using the entity.query service.

Alternatively (and the highly recommended approach) is to use dependency injection.

If you have access to the container, you can load the service from there and then get the right query object:

$entity_query_service = $container->get('entity.query');
$query = $entity_query_service->get('node');

As you can see, we use the get() method on the entity_query service to instantiate a query object for the entity type with the machine name passed as a parameter.

Querying entities

Let’s illustrate a couple of examples of querying for node entities using this object.

A very simple query that returns the published nodes:

$query = \Drupal::entityQuery('node')
    ->condition('status', 1);
    
$nids = $query->execute();

$nids will be an array of entity ids (in our case node ids) keyed by the revision ids (if there is revisioning enabled for the entity type) or the entity ids if not. Let’s see an example in which we add more property conditions as well as field conditions:

$query = \Drupal::entityQuery('node')
    ->condition('status', 1)
    ->condition('changed', REQUEST_TIME, 'condition('title', 'cat', 'CONTAINS')
    ->condition('field_tags.entity.name', 'cats');

$nids = $query->execute();

In this query, we retrieve the node ids of all the published nodes that have been last updated before the current time, that have the word cat inside their title and that have a taxonomy term called cats as a reference in the field_tags.

As you can see, there is no more distinction between propertyCondition and fieldCondition (as there is in D7 with EntityFieldQuery). Additionally, we can include conditions based on referenced entities tacking on the entity.(column) to the entity reference field name.

An important thing to note is that we also have the langcode parameter in the condition() method by which we can specify what translation of the node should be included in the query. For instance, we can retrieve node IDs that contain a specific value inside of a field in one language but another value inside the same field for another language.

For more information on the condition() method you should consult the API documentation.

The next thing we are going to look at is using condition groups (both AND and OR) for more powerful queries:

$query = \Drupal::entityQuery('node')
    ->condition('status', 1)
    ->condition('changed', REQUEST_TIME, 'orConditionGroup()
    ->condition('title', 'cat', 'CONTAINS')
    ->condition('field_tags.entity.name', 'cats');

$nids = $query->condition($group)->execute();

Above, we altered our previous query so as to retrieve nodes that either have the cat string in their title or have a reference to the term called cats in their field_tags field. And we did so by creating an orConditionGroup object that we then pass to the query as a condition. And we can group together multiple conditions within a andConditionGroup as well.

There are many other methods on the QueryInterface that can extend the query (such as for sorting, range, etc). I encourage you to check them out in the documentation and experiment with them. For now, though, let’s take a quick look at what to do with the result set.

Loading entities

As I mentioned above, the execute() method on the query object we’ve been working with returns an array of entity IDs. Supposedly we now have to load those entity objects and work with them. How do we do that?

In Drupal 7 we had the entity_load() function to which we passed an array of IDs and that would return an array of objects. In Drupal 8, this helper function is maintained and you can use it pretty much in the same way, except only for one entity at a time:

$node = entity_load('node', $nids[1]);

And the return value is a node object. To load multiple nodes, you can use the entity_load_multiple() function:

$nodes = entity_load_multiple('node', $nids);

Which then returns an array of entity objects keyed by their ids.

A bonus nugget of information is that both of these functions are wrappers for the storage manager of the entities in question. They basically retrieve the storage manager statically and then call the load() and loadMultiple() methods, respectively, on it:

Statically, you could do similarly:

$node_storage = \Drupal::entityManager()->getStorage('node');

// Load multiple nodes
$node_storage->loadMultiple($ids);
// Load a single node
$node_storage->load($id);

But better yet, you could use dependency injection and retrieve the storage class from the container:

$node_storage = $container->get('entity.manager')->getStorage('node');

And then proceed with the loading. Using dependency injection is usually the recommended way to go when it’s possible, i.e. when working within a class. This makes it easier to test your class and better decouples it from the rest of the application.

Conclusion

In this article we’ve seen how to work with querying and loading entities in Drupal 8. There has been an overhaul of the D7 EntityFieldQuery class that turned into a robust API for querying both content and configuration entities. We’ve looked at querying content entities but the system works just the same with config entities. And that is a bit of a win for the new Drupal 8 entity system.

We’ve also seen how to load entities based on the IDs resulted in these queries and what is actually behind the wrapper functions that perform these operations. Next up, we are going to look at defining our own content entity type in Drupal 8. For a refresher on how we do it in Drupal 7, you can check out these Sitepoint articles on the subject.

Jun 23 2014
Jun 23

In this article we will continue exploring the powers of Views and focus on how to use relationships, contextual filters and rewrite field outputs. In a previous tutorial I showed you how to create a new View and perform basic customizations for it. We’ve seen how to select a display format, which fields to show and how to filter and sort the results.

In this article we will go a bit further and see what relationships and contextual filters are – the two most important options found under the Advanced fieldset at the right of the View edit page. Additionally, we’ll rewrite the output of our fields and combine their values into one.

To begin with, I have a simple article View that just shows the titles. Very easy to set up if you want to follow along. And there are three things I want to achieve going forward:

  1. Make it so that the View shows also the username of the article author
  2. Make is so that the View shows only articles authored by the logged in user
  3. Make it so that the author username shows up in parenthesis after the title

Relationships

First, let’s have the View include the author of the articles. If the View is displaying fields (rather than view modes or anything else), all we have to do is find the field with the author username, right? Wrong. The problem is the following: the node table only contains a reference to the user entity that created the node (in the form of a user ID – uid). So that’s pretty much all we will find if we look for user related fields: Content: Author uid.

What we need to do is use a relationship to the user entity found in the user table. Relationships are basically a fancy way of saying that table A (in our case node) will join with table B (in our case user) in order to retrieve data related to it from there (such as the name of the user and many others). And the join will happen in our case on the uid field which will match in both tables.

So let’s go ahead and add a new relationship of the type Content: Author. Under Identifier, we can put a descriptive name for this relationship like Content Author. The rest we can leave as default.

Now if you go and add a new field, you’ll notice many others that relate to the user who authored the content. Go ahead and add the User: Name field. In its settings, you’ll see a Relationship select list at the top where the relationship identifier we just specified is automatically selected. That means this field is being pulled in using that relationship (or table join). Saving the field will now add the username of the author, already visible in the View preview.

relationships

You can also chain relationships. For instance, if the user entity has a reference to another table using a unique identifier, you can add a second relationship. It will use the first one and bring in fields from that table. So the end result will be that the View will show fields that relate to the node through the user who authored the node but not strictly from the user table but somewhere else connected to the author. And on and on you can join tables like this.

Contextual filters

Contextual filters are similar to regular filters in that you can use mainly the same fields to filter the records on. Where contextual filters differ greatly is that you do not set the filtering value when you create the View, but it is taken from context.

There are many different contexts a filter value can come from, but mainly it comes from the URL. However, you can instruct Views to look elsewhere for contexts as well – such as the ID of the logged in user.

What we’ll do now is add a contextual filter so that the View shows only the articles authored by the logged in user. So go ahead and add a new contextual filter of the type Content: Author uid. Next, under the WHEN THE FILTER VALUE IS NOT IN THE URL fieldset, select the Provide default value radio. Our goal here is to have Views look elsewhere if it does not find the user ID in the URL.

contextual filters

You then have some options under the Type select list, where you should choose User ID from logged in user. This will make Views take the ID of the user that is logged in and pass it to the View as a filter. The rest you can leave as is and save the filter. You’ll immediately notice in your preview that only articles authored by you show up. The filtering is taking place dynamically. If you log in with another user account, you should see only the articles authored by that user account.

A great thing about contextual filters is that if you are displaying a View programatically in a custom module, you can pass the filtering value in code, which opens the door to many possibilities.

Rewriting fields

The last thing we will do in this tutorial is look at rewriting fields in order to concatenate their values. We will illustrate this technique by changing the title field to include the author username in parenthesis.

We’ll start by rearranging the order of the fields and move the title to be the last one showing. The reason we want to do this is that when you rewrite fields, you can use tokens that get values only from fields that are added before the one being rewritten. And since we want to rewrite the title field, we want the token for the username value to be present so we need to move it before the title field.

Now that the title field is last, edit the author username field and uncheck the box Create a label and then check the box Exclude from display. You can now save the field. The reason we are excluding this field from being displayed in our View is so that we don’t duplicate it once we concatenate it to the title field.

rewriting fields

Next, edit the title field and under REWRITE RESULTS, check the box Rewrite the output of this field. A new textarea should appear below where we will write the new contents of this field. If you write some gibberish in there and save the field, you’ll notice the title gets replaced by that gibberish.

Below this textarea, you’ll notice also some REPLACEMENT PATTERNS. These represent tokens of all the fields in the View loaded before this one (and including this one as well). So if you followed along, you’ll see there [name] and [title], among others.

What we need to do now is put these tokens in this box, wrapped with the text or markup we want. Having said that we want the username to be in parenthesis after the node title, we can add the following to the text box to achieve this:

[title] ([name])

Save the field and check out the result. Now you should have the author user in parenthesis. However, it’s still not perfect. We left the title field’s Link this field to the original piece of content box checked and this is breaking the output for us a bit due to also the username having a link to the user profile page. What we want is a clean link to the node title and in parenthesis (which themselves do not link to anything), the username linking to the user profile page.

So first up, add a new field called Content: Path (the path to the node). Make sure you exclude it from display, remove its label and move it before the title field. Then, edit the title field, uncheck the Link this field to the original piece of content box and replace the REWRITE RESULTS text with this:

 href="[path]">[title] ([name])

The [path] token is available from the new field we just added. And after you save, you should see already in the preview a much cleaner display of title nodes and usernames in parenthesis.

Conclusion

In this tutorial we’ve looked at three main aspects of building Views in Drupal 7: relationships, contextual filters and rewriting fields. We’ve seen how with the use of relationships we can use information also from related entities, not just those on the base table a View is built on. Contextual filters are great for when the View needs to display content dynamically depending on various contextual conditions (such as a URL or logged-in user). Lastly, we’ve learned how to rewrite fields and build more complex ones with values taken from multiple fields. As you can see, this technique is very powerful for theming Views as it allows us to output complex markup.

Views is pretty much the most popular Drupal module and it is highly complex. Despite its complexity, building views as a site administrator is very easy. All you need to understand is a few basic concepts and you are good to go. Developing for Views to extend its functionality or expose data to it is also an enjoyable experience. If you’d like to know more about that, you can read my tutorial on exposing your own custom module table to Views right here on Sitepoint.com.

Jun 18 2014
Jun 18

How to Build a Drupal 8 Module

In the previous article on Drupal 8 module development, we’ve looked at creating block types and forms. We’ve seen that blocks are now reusable and how everything we need to do for defining block types happens in one single class. Similarly, form generation functions are also grouped under one class with specific methods performing tasks similar to what we are used to in Drupal 7.

In this tutorial, I will continue where we left off. I will illustrate how we can turn our DemoForm into a form used to store a value through the Drupal 8 configuration system. Following that, we will talk a bit about the service container and dependency injection by way of illustration.

Don’t forget that you can check out this repository if you want to get all the code we write in this tutorial series.

When we first defined our DemoForm, we extended the FormBase class which is the simplest implementation of the FormInterface. However, Drupal 8 also comes with a ConfigFormBase that provides some additional functionality which makes it very easy to interact with the configuration system.

What we will do now is transform DemoForm into one which will be used to store the email address the user enters. The first thing we should do is replace the extended class with ConfigFormBase (and of course use it):

use Drupal\Core\Form\ConfigFormBase;

class DemoForm extends ConfigFormBase {

Before we move on to changing other things in the form, let’s understand a bit how simple configuration works in Drupal 8. I say simple because there are also configuration entities that are more complex and that we will not cover today. As it stands now, configuration provided by modules (core or contrib) is stored in YAML files. On enabling a module, this data gets imported into the database (for better performance while working with it). Through the UI we can change this configuration which is then easily exportable to YAML files for deployment across different sites.

A module can provide default configuration in a YAML file located in the config/install folder in the module root directory. The convention for naming this file is to prefix it with the name of the module. So let’s create one called demo.settings.yml. Inside this file, let’s paste the following:

demo:
  email_address: [email protected]

This is a nested structure (like an associative array in PHP). Under the key demo, we have another key|value pair. And usually to access these nested values we use a dot(.). In our case demo.email_address.

Once we have this file in place, an important thing you need to remember is that this file gets imported only when the module is installed. So go ahead and reinstall it. And now we can turn back to our form and go through the methods that need adapting one by one.

This is how the buildForm() method should look like now:

public function buildForm(array $form, array &$form_state) {
  
  $form = parent::buildForm($form, $form_state);
  
  $config = $this->config('demo.settings');
  
  $form['email'] = array(
    '#type' => 'email',
    '#title' => $this->t('Your .com email address.'),
    '#default_value' => $config->get('demo.email_address')
  );
  
  return $form;
}

First of all, as opposed to FormBase, the ConfigFormBase class implements this method as well in order to add elements to the form array (a submit button). So we can use what the parent did before adding our own elements.

Now for the configuration part. Drupal 8 provides a Config object that we can use to interact with the configuration. Some classes already have it available through dependency injection. ConfigFormBase is one such class.

As you can see, we are using the config() method of the parent class to retrieve a Config object populated with our demo.settings simple configuration. Then, for the #default_value of the email form element, we use the get() method of the Config object to retrieve the value of the email address.

Next, we only need to change the submit handler because the validateForm() method can stay the same for now:

public function submitForm(array &$form, array &$form_state) {
  
  $config = $this->config('demo.settings');
  $config->set('demo.email_address', $form_state['values']['email']);
  $config->save();
  
  return parent::submitForm($form, $form_state);
}

In this method we first retrieve the Config object for our configuration (like we did before). Then, we use its set() method to change the value of the email_address to the value the user submitted. Then we use the save() method to save the configuration. Lastly, we extend the parent submit handler because it does contain some functionality (in this case it sets a Drupal message to the screen).

And that’s pretty much it. You can clear the cache and try it out. By submitting a new email address, you are storing it in the configuration. The module demo.settings.yml file won’t change of course, but you can go and export the demo.settings configuration and import it into another site.

The service container and dependency injection

The next thing we are going to look at is the service container. The idea behind services is to split functionality into reusable components. Therefore a service is a PHP class that performs some global operations and that is registered with the service container in order to be accessed.

Dependency injection is the way through which we pass objects to other objects in order to ensure decoupling. Each service needs to deal with one thing and if it needs another service, the latter can be injected into the former. But we’ll see how in a minute.

Going forward, we will create a very simple service and register it with the container. It will only have one real method that returns a simple value. Then, we will inject that service as a dependency to our DemoController and make use of the value provided by the service.

In order to register a service, we need to create a demo.services.yml file located in the root of our module, with the following contents:

services:
    demo.demo_service:
        class: Drupal\demo\DemoService

The file naming convention is module_name.services.yml.

The first line creates an array of services. The second line defines the first service (called demo_service, prefixed by the module name). The third line specifies the class that will be instantiated for this service. It follows to create the DemoService.php class file in the src/ folder of our module. This is what my service does (nothing really, it’s just to illustrate how to use it):

<?php

/**
 * @file
 * Contains Drupal\demo\DemoService.
 */

namespace Drupal\demo;

class DemoService {
  
  protected $demo_value;
  
  public function __construct() {
    $this->demo_value = 'Upchuk';
  }
  
  public function getDemoValue() {
    return $this->demo_value;
  }
  
}

No need to explain anything here as it’s very basic. Next, let’s turn to our DemoController and use this service. There are two ways we can do this: accessing the container globally through the \Drupal class or use dependency injection to pass an object of this class to our controller. Best practice says we should do it the second way, so that’s what we’ll do. But sometimes you will need to access a service globally. For that, you can do something like this:

$service = \Drupal::service('demo.demo_service');

And now $service is an object of the class DemoService we just created. But let’s see how to inject our service in the DemoController class as a dependency. I will explain first what needs to be done, then you’ll see the entire controller with all the changes made to it.

First, we need access to the service container. With controllers, this is really easy. We can extend the ControllerBase class which gives us that in addition to some other helpers. Alternatively, our Controller can implement the ContainerInjectionInterface that also gives us access to the container. But we’ll stick to ControllerBase so we’ll need to use that class.

Next, we need to also use the Symfony 2 ContainerInterface as a requirement of the create() method that instantiates another object of our controller class and passes to it the services we want.

Finally, we’ll need a constructor to get the passed service objects (the ones that create() returns) and assign them to properties for later use. The order in which the objects are returned by the create() method needs to be reflected in the order they are passed to the constructor.

So let’s see our revised DemoController:

<?php

/**
 * @file
 * Contains \Drupal\demo\Controller\DemoController.
 */

namespace Drupal\demo\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * DemoController.
 */
class DemoController extends ControllerBase {
  
  protected $demoService;
  
  /**
   * Class constructor.
   */
  public function __construct($demoService) {
    $this->demoService = $demoService;
  }
  
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('demo.demo_service')
    );
  }
  
  /**
   * Generates an example page.
   */
  public function demo() {
    return array(
      '#markup' => t('Hello @value!', array('@value' => $this->demoService->getDemoValue())),
    );
  }
}

As you can see, all the steps are there. The create() method creates a new instance of our controller class passing to it our service retrieved from the container. And in the end, an instance of the DemoService class gets stored in the $demoService property, and we can use it to call its getDemoValue() method. And this value is then used in the Hello message. Clear your cache and give it a try. Go to the demo/ path and you should see Hello Upchuk! printed on the page.

I’m sure you can see the power of the service container as we can now write decoupled functionality and pass it where it’s needed. I did not show you how, but you can also declare dependencies when you register services. This means that when Drupal instantiates a service object, it will do so for all its dependencies as well, and pass them to its constructor. You can read more about how to do that on this documentation page.

Conclusion

In this article we’ve looked at a lot of cool stuff. We’ve seen how the configuration system manages simple configuration and what we have available form-wise for this. I do encourage you to explore how the ConfigFormBase is implemented and what you have available if you extend it. Additionally, you should play around in the UI with importing/exporting configuration between sites. This will be a great improvement for the deployment process from now on.

Then, we looked at services, what they are and how they work. A great way of maintaining reusable and decoupled pieces of functionality accessible from anywhere. And I do hope the concept of dependency injection is no longer so scary (if it was for you). It is basically the equivalent of passing parameters to procedural functions, but done using constructor methods (or setters), under the hood, by Symfony and its great service container.

Pages

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