Building and deploying sites using Drupal “Features”

NorthPoint Digital was a Gold Sponsor this September at the New England Regional Developers (NERD) Summit hosted at University of Massachusetts in Amherst. It was a three-day conference full of talks, workshops, and coding challenges that attracted a great combination of software developers and business developers from all over the New England area as well as tech leaders like Josh Kroenig, a co-founder of Pantheon, one of our newest hosting partners. In fact, size of the crowd for the opening keynote far exceeded that of the Design for Drupal conference at the MIT Strata Center in early August!

nerd-summit-logo

I had an opportunity to present on “Building and Deploying Sites using Drupal Features” which mostly catered towards beginner Drupal users, but also offered insight to more-experienced Drupal developers covering topics such as strategies to avoid common pitfalls of Features. The talk wrapped up with an introduction to the future of Features in Drupal 8 – the Content Management Initiative. 

What is the Features module and why is it important for web developers?

The Features module enables the capture and management of features in Drupal. A feature is a collection of entities that when combined captures a certain use case. Perhaps the greatest value the module provides to web developers is the ability to take site configuration data and transform it into source code, which can then be version controlled and deployed to a different environment.

Drupal 7 core offered no built-in facility to package together site configuration (content types, taxonomies, roles, permissions, etc), and the Features module filled that void.

In this blog entry, I’ll discuss the process of creating your first Features and deploying site configuration to different environments using a fairly trivial example. In a real-world scenario, multiple developers might be working on the same set of complex features – when a client or manager requests a change, there could be serious impact especially if a poorly thought out strategy is employed from the early stages.

I’ll be introducing a strategy for architecting features in order to avoid some of the common pitfalls like circular dependencies that can occur and cause issues for developers.

Drupal 8 is currently in Beta 2 – what will the future hold for the Features module? I’ll discuss how the newest version of Drupal incorporates some of the strengths of Features into core (Content Management Initiative).

What kind of scenario could you face that will require you to use the Features module for a project you’re working on? Let’s introduce a common use case.

Use Case

A client approaches you and requests a user interface (UI) for reporting information about projects that users have created on the site, as well as other attributes such as the category the project falls into and the members who are working on it.

Let’s say we don’t have any of the core entities like content types created to represent projects, members, categories, and connections between them. We’ll need to build everything from a base level all the way up to the UI level and effectively be able to deal with changes that may arise throughout the development lifecycle.

By the time you’ve finished development, you want to have a UI element like a View to display information about projects created as illustrated in the image below.

 

nerd-summit-features-ui

 

 

 

Building your first set of features and deploying configuration data 

We start off by constructing the basic building blocks required to implement this use case: content types for Projects and Members and a taxonomy for Categories. Each of these building blocks will be stored in their own respective feature. Think of this as separation of concerns – a common software engineering principle. While we will eventually connect these building blocks, we will keep them separate at this stage of development. This strategy will be elaborated on in the next section.

In Drupal 7, it is easy to create a feature. Upon downloading and enabling the Features module, you can navigate to Structure -> Features on the admin toolbar. After clicking the ‘create feature’ tab, you’ll be presented with an interface where you can select individual entities to add to your feature. In this case, we’ll be creating three separate features – one for Projects, Members, and Categories.

 

creating-project

 

The above image shows the Features interface when we’re initially building the Projects feature. We just want to add the Projects content type to this. Upon selecting it, several dependencies are automatically filled in. You’ll see the field bases and field instances are checked off for the different fields we added to the content type like ‘status.’ You’ll also want to specify a package in order to logically group all the features together. For the demo I did at the NERD Summit, I simply grouped everything in the NERD package.

The next step is to generate the feature. For now, we’ll just use the Drupal UI – you can specify a location in your file system where you’d like the feature written to and click ‘generate feature.’ This creates a module that holds the site configuration you selected. If you go to that directory and list the contents in it, you’ll see the .module and .info files created as well as other files to hold some of the attributes of the content type.

 

 project-code

 

 

In order to deploy your newly-created features onto a different environment like development, staging, or production, you can add the modules to your version control system like Git, commit the changes to your branch, pull down any changes from the remote repository, and then push to the desired branch on that same repository. Since a feature effectively is a module, you can enable it on the modules menu on the new environment or directly through the features menu.

What happens if you make a change to a field on a content type that’s in a feature on your local development environment and you want that change to be reflected in an environment where the feature is already deployed? For instance, say we change the text length of the description field for the Projects content type – part of the Project feature.

Modifying an existing feature

Once you make the change to the text field, it is reflected in the database but not in source code. If you were to navigate to the features menu, you’d notice that Projects has a status of ‘overridden.’ While we could bring the new changes to code by clicking ‘generate feature’ like we did before, it is a much quicker process and better practice to utilize the Drush module.

The Drush module provides a command line interface to Drupal and allows you to quickly execute common commands like clearing all caches (drush cc all). This module was developed by Moshe Weitzman, a core contributer to Drupal and the Director of R&D at Acquia, another important hosting partner. A couple of commands are available for use with Features – features-update and features-revert.

The command features-update <feature name> takes changes reflected in the database for a given feature and brings them into source code, which can then be put into version control. Features-revert <feature name> takes changes in source code and applies them to the database.

In this case we want to execute ‘drush fu projects’ (short for features-update) in order to take our changes and reflect them in code. The following image shows the modified file after executing a ‘git status.’

 

drush-changes

 

 

 

In order to deploy the change to your environment, you can follow the same steps as before with your version control system. Depending on your environment and hosting provider, if applicable, you would need to do a features revert in order to take the new code changes and apply them to the database in order to prevent errors from being thrown and inaccurate results when storing and retrieving data.

Some hosting providers like Pantheon (that I used for my demonstration) are automatically configured to do a features-revert-all upon deployment, which is a nice time saver.

Strategies for avoiding common pitfalls

Having an effective strategy to architect features is important whether you’re working on a project individually or on a team. When creating features to implement a particular use case, I think it’s important to consider common software engineering principles such as separation of concerns and decoupling. It’s also important to think of each feature as a standalone entity that can be turned on or off.

The image below reflects a set of features at different levels and indicates the dependencies between them.

 

feature-tree

 

Creating integration features 

We initially created ‘core features’ or simple building blocks to lay the foundation to solve our problem of creating a user interface that shows project content and associated data like members.

The core features we created represent the base level entities required. When creating the content types, we didn’t make any references between them – that is done at the integration level. At this point, we can add an entity reference (another contributed Drupal module) to Projects in order to add a member to it. Similarly, we can add a taxonomy term reference to the Projects feature in order to associate a category with it.

 

create-integration-feature

 

 

After adding an entity reference to Projects to make an association with the Members content type, we can create a new feature to capture this integration following a similar process as before. This time, we need to specify that both Projects and Members are dependencies of the feature – in other words, the feature can’t be enabled if one of the components is removed or disabled. The image above shows the dependencies checked off as well as the new field ‘field_member’ that was added.

Creating UI features 

We will use a simple View in Drupal in order to create the reporting interface that displays information about projects created. The UI feature would only require the two integration features as dependencies – not any of the core level features like Projects.

After deploying this set of features to our desired environment, what would happen if the requirements changed and it was no longer necessary to have a member associated with a project? If we go to the Projects content type and remove the member field, what features would be impacted?

Managing the dependency tree

Due to the way we designed this set of features and managed the dependencies carefully, only the Project UI and Project-Member Integration features are affected.

 

change-to-view

 

 

 

 

You might have expected that the Projects feature would be impacted as well as it contains the Project content type. However, we didn’t add the relationship until we created the integration feature, so the core level features remain unaffected.

By organizing features into a hierarchical structure like a tree, it’s easier to manage features when they become more complex over time. This was a toy problem used to illustrate the effectiveness of this strategy. Entities like content types and the relationships amongst them can get quite complicated over the lifecycle in a web development project.

When making a low-level change to a core feature, it has an impact on all other features dependent on it. It’s important to know the impact of changes you’re making! This is similar to object oriented software development in the sense that if you make a change to a base or framework class, it’s going to impact all other classes that inherit from it.

Previewing “Features” in Drupal 8 – Content Management Initiative

Drupal 8 is including some of the functionality present in the Features module directly into core, primarily to solve the problem of moving site configuration from one environment to another.

Site configuration will be entirely file based and captured in YAML files – there is no longer any connection with the database. The admin interface makes it easy to transport all site configuration by providing options like import or export. Drupal 8 makes use of UUID (Universally Unique Identifier) stored in the system.site.yml file. The UUID of your site must match the UUID on the target site in order to import the configuration of that site.

The import option greatly simplifies the problem of staging configuration. Selecting import rather than packaging up features and executing a features-revert is much more user friendly. Since Drupal 8 is entirely object oriented, getters and setters will provide developers with a unified interface to get and set configuration values. 

The goal of Content Management in Drupal 8 isn’t necessarily to replace the Features module in Drupal 7 entirely – it’s to provide a standard process of moving site configuration. There will likely be another Features module developed in order to satisfy some of the limitations: 1) packaging up a subset of site configuration data to fit a certain use case, 2) creating the ability to reuse configuration in other projects, and 3) enabling developers on a team to work on separate features simultaneously on the same site.

Leave a Reply