Skip to main content

Understanding Drupal 8's config entities

Drupal 8 comes with a new type of entity, a config entity.

Those working with Drupal 7 will be familiar with entities for their content.

Drupal 8 introduces the concept of a config entity, which is very similar to a CTools exportable in Drupal 7, but with much of the entity system goodness.

by lee.rowlands /

Those using a code based methodology to build sites would be well familiar with CTools exportables.

In Drupal 7 CTools exportables are utilised by modules such as Views, Panels, Page Manager, Context and many more to provide a relative pain-less way of exporting configuration for the purposes of deployment and version control.

Drupal 8 introduces a new concept called a Config Entity to emulate much of this functionality and more.

Example config entities

Already in D8 core we have config entities for Views, Vocabularies, Contact form categories, User email templates, display settings, block instances, shortcut sets, custom menus and Image styles.

On the list still to be converted are node types and languages.

See the issue tracking these conversions for more information.

Difference from content entities

Entities in the Drupal 7 sense of the word have been renamed to 'Content Entities'. The main difference between content and config entities is how they are stored and (at the moment) config entities are not fieldable (although this issue flirts with changing that).

Config entities are stored using the CMI system, i.e. in yaml files in your config folder. Config entity yaml files follow a particular naming convention (prefix) and shipping a module with some default configuration is as easy as creating a new yaml file. 

For example see the views module's config folder where a number of default views can be found.

As an example of how big a change to workflow these will make in Drupal 8 - download the latest version of Drupal 8 from git and create a view. Notice there is no 'export' link anymore? Wondering why? Navigate to your active config store (this will be sites/default/files/config/activeXXXX) where XXXX is a hash. Inside there you'll find the yaml files for the current views on your site. Migrating these to your deployment site will be as simple as committing these files to version control and deploying to the staging area of your production site and then importing them using the UI or drush commands.

Starting to see the significance of config entities to your deployment workflow now? You can already find a lot of your configuration in that folder, how nice will it be when you can find node types, menus, block instances, text formats etc. Gone are the days of exporting features, verifying you got everything you think you needed by deploying to a second site then rinse and repeat. Edit: Justin rightly points out in the comments that ensuring you have dependencies is still a important part of the config staging workflow.

See it in action

As an example, lets create an image style

Have a look in your active config - which will be sites/default/files/config/activeXXX you'll see your existing images styles

Now go and create a new one using the image style UI, you'll find a new config file in your active store

Now copy this file and the image style manifest to the staging store on your other site, this could be via version control or some other deployment method, but for the purposes of this example, we'll just copy and paste on a local install. The staging store is in sites/default/files/config/stagingXXX. It should look like this.

Now in the UI on the second site (at admin/config/development/sync), sync your config

And now view your image style list in the second site

Note that running an import will delete all files in the staging directory.

Under the hood

So how do you go about creating a config entity? The process is quite simple - if you have a module in Drupal 7 that integrates with CTools exportables for your storage - you will most likely need to use config entities for your storage in Drupal 8. Some are already predicting that modules that don't will be considered broken. The process is actually quite straight forward.

Defining your config entity class

First you need an entity plugin, lets say your entity type and module is called robot. Following PSR-0 you create the following folder

robot/lib/Drupal/robot/Entity/Robot.php

Inside here you define your Robot config entity class

/**
 * @file
 * Contains Drupal\robot\Entity\Robot.
 */

namespace Drupal\robot\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;

/**
 * Defines the robot entity.
 *
 * @ConfigEntityType(
 *   id = "robot",
 *   label = @Translation("Robot"),
 *   module = "robot",
 *   controllers = {
 *     list = "Drupal\robot\RobotListController",
 *     form = {
 *       "add" = "Drupal\robot\RobotAddFormController",
 *       "delete" = "Drupal\robot\RobotDeleteFormController"
 *       "edit" = "Drupal\robot\RobotEditFormController" 
 *     },
 *   },
 *   config_prefix = "robot",
 *   admin_permission = 'administer robots',
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *   },
 *   links = {
 *     "edit-form" = "robot.robot_edit",
 *     "delete-form" = "robot.robot_delete"
 *   }
 * )
 */
class Robot extends ConfigEntityBase {
  // ....Definition of robot's properties...
  // e.g.:

  /**
   * The custom block type ID.
   *
   * @var string
   */
  public $id;
}

Then you need to create the storage, list and form controllers to provide (funnily enough) the storage, list and form functionality

Storage controller

Your storage controller needs to extend the Drupal\Core\Config\Entity\ConfigStorageController but unless you have complex requirements you can largely use the default implementation

List controller

List controllers are also new to Drupal 8, but basically they provide a unified way of listing a group of entities, something that was present with CTools exportables. Think the views listing at admin/structure/views for example.

Your list controller needs to extend Drupal\Core\Config\Entity\ConfigEntityListController

You'll need custom implementations of the ::buildRow(), ::buildHeader() and ::getOperations() methods as a bare minimum, see some of the implementations in core for a good starting point.

Form controller

The form controller extends Drupal\Core\Entity\EntityFormController and you'll need to implement the ::form() method to define the form (using the form api) required for a user to create a new instance of your entity

The ::save() method is where you can handle saving of your entity, most implementations will do something like:

/**
   * Overrides Drupal\Core\Entity\EntityFormController::save().
   */
  public function save(array $form, array &$form_state) {
    $robot = $this->entity;
    $status = $robot->save();
    // .. do other things with the updated/inserted entity
  }

There are other methods you might need to override, but the save and form will be the most common methods you have to override

Summary

Config entities will change how we work in Drupal 8

As you can see from the meta issue, there is still plenty of work to be done - fast track your Drupal 8 skills by being involved in its inception - now is the time to get involved!

Edit: Note thanks to @timplunkett who pointed out a few issues with my example code.

Edit: 11 Mar 2014 Note thanks to @xtfer who pointed out that core had moved on, example code updated.