Not every environment or copy of a site you may be working on will be created equally. You may want to enable logging on a development site or need to use different API keys depending on the environment. But you also need to make sure that your instance-specific configuration overrides don't make it into the database, mistakenly get exported, or compromise security.
In this tutorial, you will learn how to:
- Override the global
$config
array in settings.php (or settings.local.php) - Retrieve overridden (immutable) configuration (read-only mode)
- Retrieve original (mutable) configuration for updating (read/write mode)
- Set dynamic values for configuration instead of overriding values
When you create a module for Drupal, it can be useful to provide default configuration. This can be settings for a form, the placement of a block, or something more complex like the default image styles provided by the Image module in core. A module can provide default configuration for simple configuration or configuration entities.
In this tutorial, we will cover:
- Possible locations for default configuration
- What happens with configuration when a module is installed or uninstalled
- Managing dependencies in configuration
- Where to find examples of default configuration
Configuration entities are suitable for creating user-defined configuration, such as image styles, views, content types, etc. A configuration entity type is defined by a module, default configuration is provided by that module as well as any other module, and then users can create zero or more configuration entities through Drupal's administrative UI.
In this tutorial, you will learn about:
- What configuration entities are
- Configuration entity types versus configuration entities
- An example in core: image style
- Overview of the process of creating your own configuration entity types in a module
When working on configuration in a module, whether as part of a migration that uses Migrate Plus configuration entities, or while developing custom configuration entities, you'll often need to re-import the configuration stored in the .yml files of the modules config/install/ or config/optional/ directories. This is tricky though, because Drupal only reads in those default configuration settings when the module is first enabled. So any changes you make to those files after the module has been installed will not be reflected without these workarounds.
Knowing how to do this can improve the developer experience of adding (or debugging) the default configuration that's provided with a module. Or for anyone using Migrate Plus configuration entities as part of a migration.
In this tutorial we'll:
- Learn about the Configuration Development module
- Look at how you can use Drush to perform a partial configuration import
- Write an implementation of
hook_uninstall()
to remove a module's configuration when it's uninstalled
By the end of this tutorial you should be able to re-import the configuration provided by a module without having to uninstall and then reinstall the module.
Now that we have some default simple configuration stored in a settings YAML file, let's utilize it in a form that our site administrators can use to update those values. We'll make use of some services and methods in Drupal's Configuration API in order to retrieve, update, and save simple configuration values with a form.
In this tutorial, we'll cover how to load configuration entity data in a module. We'll change the AdminSettingsForm.php we created and replace the simple textfield we were using with a dropdown select list. Then we'll use data from our Transcode Profile module's configuration entity, loaded by the EntityTypeManager
via the services container, to choose our preferred Transcode Profile.
By the end of this tutorial, you should be able to:
- Know how to load configuration entities using
EntityTypeManager
via the services container - Update the AdminSettingsForm.php to use a dropdown select list
- Save your preferred transcode profile from a list of transcode profile entities
- Update the default configuration provided with the demo module to include transcode profiles
We have half of our editorial process in place. Existing editorial groups can now edit content they are responsible for, and only that content. However, to enable editors to effectively manage the flow of articles coming in, we need something else: an editorial workflow system that allows them to easily determine when an article is ready for review, and to either send it back to the reporter when it needs more work, or to publish it on the site when it’s good to go.
Out of the box, Drupal allows a piece of content to be either published or unpublished. When the “published” checkbox is unchecked on the node editing form, only users with the “administer nodes” permission are allowed to view the content. That’s enough for some sites, but it doesn’t give our reporters and editors as much control as they need. For example, there’s no way for a reporter to mark an article as an in-progress draft and come back to it later. In addition, there’s no easy way for an editor to tell a reporter that an article needs more work—the editor must contact the author manually.
In this lesson, we'll get an overview of how the Workbench Moderation module can do this for us. We'll look at Workbench states and transitions, and how we use these to moderate our content.
Additional resources
The final step to complete our site for Our Media is to get the editorial workflow in place. In this lesson, we'll add moderation to the Article content type, create the correct States and Transitions we need, and then finish up by setting permissions so it all works the way we expect.
Additional resources
We now have our new editorial system in place and the Our Media site is ready to go. We’ve met the needs of our client, but there are some other modules that are also worth checking out:
We have more detailed instruction in another video on using the Views Bulk Operations (VBO) module.
Additional resources
Congratulations! The Our Media website now includes all the major features that the staff wanted. We’ve used the Workflow Access module to create a distributed system for content management, preventing collisions when editors review content they are responsible for. We’ve also provided the team with an editorial workflow, which makes it easy for them to track changes to content as it moves through the editorial process. On top of that, all of this is done in a consistent, easy-to-use interface, so editors quickly find the content they need. In this summary, we'll take a tour of the completed Our Media site, discuss our implementation points, and review the modules we used and discussed throughout the series.
Additional resources
Fields are the building blocks of Drupal's powerful content modeling system. The field API allows for the development of custom field types to suit almost any data display and collection needs. Developers can create custom field types that can be bundled together and attached to various pieces of content. Fields allow a Drupal Site Administrator to create an information architecture that matches the needs of each individual site.
This series will provide you with all the information you need to be able to define a custom field in your own module. After completing all the lessons in this series, you should have a firm grasp of the Drupal 7 field API and the tools and knowledge you need in order to define your own custom field types.
Prerequisites
This series requires an understanding of PHP and basic Drupal 7 module development. For a refresher, or if you get stuck, check out our Drupal 7 Module Development series.
Additional resources
Drupal 7 Module Development series (Drupalize.Me).
Before diving into the code it's important to understand some of the building blocks that make up the Field API. There's a lot of different terminology in the Field API and it helps to understand what each of the terms mean. As well as understanding the relationship between the Fields and Entities in Drupal 7. Knowing these things will give you a strong foundation on which to start exploring the Drupal 7 Field API.
Terms & concepts covered in this video:
- What is a field? what is an instance?
- How do fields relate to entities?
- Field types
- Field storage
- Field widgets
- Field formatters
For more information about these terms see the Drupal.org handbook page: https://drupal.org/node/443540
- Field CRUD API - creates field instances and bundles, e.g.) what you see on the manage fields page.
- Field attach API - connects entities and fields, uses info from Field Info API to retrieve defined fields and do things like display their widget on the appropriate entity form when someone tries to edit an entity.
- Field info API - retrieve information about defined fields and instances.
- Field storage API - pluggable back-end storage for fields. Defaults to SQL backend provided by core.
- Field language API - provides native multilingual support for fields.
There's quite a bit of documentation and other resources already available to help you better understand the Drupal 7 Field API. Lets take a look at what's already available on Drupal.org, in the Examples project, and in the Drupal 7 core code that will serve as good reference material. We'll be referring back to these resources in later lessons, and they'll serve as a great place to look up additional information or to continue your learning via other examples.
Resources covered in this video:
- The Drupal.org Handbook for Field API
- API documentation in field.api.php, which gives an overview of all the Field API hooks.
- Field API documentation available on api.drupal.org - https://api.drupal.org/api/drupal/modules%21field%21field.module/group/…
- Examples for Developers project
We've also got some additional resources here on Drupalize.Me that will serve as a good refresher for how/where fields are used in Drupal:
- For learning more about how to use fields in the UI and how the UI works - http://drupalize.me/series/intro-fields-site-builders-series
- Attaching fields to custom entities - http://drupalize.me/videos/make-your-entity-fieldable-bundles
Additional resources
Before we can start building our custom field we need a vanilla Drupal site to work with and a skeleton module. This lesson will ensure you've got Drupal 7 up and running and walk through creation of a basic .info file and .module file for the module we'll be building. If you're already familiar with Drupal module development this lesson can likely be skipped and you can simply download the attached starter files, add them to an existing Drupal site, and continue on with the next lesson.
Grab a fresh copy of Drupal 7, and install it. If you need a refresher on installing Drupal checkout this series.
You'll also want to download and install the devel module as we'll make use of some of the debugging functions in provides (namely dsm()
) in later lessons in this series.
Alternatly, you can grab the .zip file under the companion files listed on this page which contains Drupal 7, and a database dump you can import to get started.
The first step to defining a custom field is telling Drupal that our module provides a field. This is done by implementing hook_field_info(), hook_field_formatter_info(), and hook_field_widget_info(). The combination of which provides some basic information about our field including a label, description, default settings, and basic information about how the field will be formatted and what widgets can be used for data collection.
In this lesson we'll implement the basics for the following hooks:
Doing so will allow us to enable our module and see our new field type appear in the list of available fields to add to a content type. The field won't do much beyond that yet, but it's a good start towards telling Drupal about our custom RGB field type.
If you want to just follow along and look at the already written code you can grab a copy in the companion files section of this page and use that to follow along.
Before we can actually get our field to store data for us we need to define what the data that we're going to store looks like. The Field API does this with hook_field_schema(), which uses a very similar syntax to what is used by the hook_schema() that modules can use to define database tables. In this particular case though we're only defining what the column, or columns, that store our specific data will be and allowing the Field Storage API to decide what the structure of the created table, or tables, should be. This allows our field to remain mostly storage system agnostic and frees us from having to worry about things like how the stored field data is connected back to the entity that it belongs to, or how to format our table for proper handling of revision data or translations.
- Documentation for hook_field_schema - https://api.drupal.org/api/drupal/modules%21field%21field.api.php/funct…
- Schema API docs - https://drupal.org/developing/api/schema
Example hook_field_schema()
implemenatation.
/**
* Implements hook_field_schema().
*/
function rgb_field_schema($field) {
$columns = array(
'rgb' => array(
'type' => 'varchar',
'length' => 6,
'not null' => FALSE,
),
'label' => array(
'type' => 'varchar',
'length' => 128,
'not null' => FALSE,
),
);
$indexes = array(
'rgb' => array('rgb'),
);
return array(
'columns' => $columns,
'indexes' => $indexes,
);
}
Additional resources
The term widget refers to the form element, or elements, that are presented to the user when they are entering data for a field. For example, the file upload field on the Article content type is the widget for the image field attached to that content type. When a field instance is attached to a bundle and an admin is creating or editing an entity of that bundle type the Field Attach API calls out to each individual field and asks it for the widget it would like to use to collect data. Adding a widget for a custom field type is a combination of implementing hook_field_widget_info() and hook_field_widget_form().
Examples:
/**
* Implements hook_field_widget_info().
*/
function rgb_field_widget_info() {
return array(
'rgb_textfield' => array(
'label' => t('RGB Textfields'),
'field types' => array('rgb_color'),
),
);
}
/**
* Implements hook_field_widget_form().
*/
function rgb_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
if ($field['cardinality'] == 1) {
$element['#type'] = 'fieldset';
}
$element['rgb'] = array(
'#type' => 'textfield',
'#field_prefix' => t('RGB: #'),
'#size' => 6,
'#default_value' => isset($items[$delta]['rgb']) ? $items[$delta]['rgb'] : '',
);
$element['label'] = array(
'#type' => 'textfield',
'#field_prefix' => t('Color name: '),
'#default_value' => isset($items[$delta]['label']) ? $items[$delta]['label'] : '',
);
return $element;
}
Additional resources
Before our field can save user provided data we need to use hook_field_validate() and hook_field_is_empty() to perform validation on field data. In certain context values like 0, FALSE, and NULL can all be a valid value. In fact, even a blank space could be valid input for a field. As such, it's not possible for Drupal to know what constitutes an empty state for a field without a little extra help. The same is true for checking if the value of a field is valid.
Examples:
/**
* Implementation of hook_field_is_empty().
*/
function rgb_field_is_empty($item, $field) {
if (empty($item['rgb']) || empty($item['label'])) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_field_validate().
*/
function rgb_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
foreach($items as $delta => $item) {
if (!empty($item['rgb'])) {
// Make sure it's 6 characters.
if (drupal_strlen($item['rgb']) !== 6) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'rgb_length',
'message' => t('%name: the hex color must be 6 characters.', array('%name' => $instance['label'])),
);
}
}
}
}
Additional resources
In order to allow for maximum flexibility in our widget we can add widget settings that apply to each individual instance of our field. By implementing hook_field_widget_settings_form() and then refactoring some of our existing code we can make it possible for a site administrator to set a custom prefix value for the label field, which the Field API will store for as part of the field instance's settings and we can use it when creating our widget.
Implementations of hook_field_widget_settings_form()
return a Form API array that represents the element or elements that you would like to add to the widget settings form. Values are automatically serialized and saved as part of the field's instance configuration and can be accessed by the passed in $instance
array's $instance['widget']['settings']
key.
Example:
/**
* Implements hook_field_widget_settings_form().
*/
function rgb_field_widget_settings_form($field, $instance) {
$element = array(
'rgb_label_text' => array(
'#type' => 'textfield',
'#title' => t('Alternate label text'),
'#description' => t('If an alternate label text is provided it will be used in place of the default "Color" title for the label field.'),
'#default_value' => isset($instance['widget']['settings']['rgb_label_text']) ? $instance['widget']['settings']['rgb_label_text'] : '',
),
);
return $element;
}
Additional resources
Displaying that data that was collected and saved for our a field requires creating a field formatter. Formatters consist of an implementation of hook_field_formatter_info() and hook_field_formatter_view(). The former provides meta-data about the formatter for the Field API and the latter does the heavy lifting of determining what the output is actually going to look like.
A module can define more than one field formatter.
Implementations of hook_field_formatter_view()
return a renderable array representing the content you would like to display to the end user. Generally this is an escaped version of content provided by a site administrator with some additional HTML formatting applied.
Depending on the values being output you would likely want to also use a theme function for your field formatter by implementing hook_theme() and providing either a theme() function that can be overriden or a template file. We're not going to cover that in this lesson since the focus here is on the technical requirements for impelementing a field formatter. Howerver, I would say that it's best practcie to always output any HTML with a theme function. You can find out more about creating themeable output by watching these videos from our library: http://drupalize.me/videos/integrating-theme-system and http://drupalize.me/videos/using-drupal-render-api
Example:
/**
* Implements hook_field_formatter_info().
*/
function rgb_field_formatter_info() {
return array(
'rgb_raw' => array(
'label' => t('Raw color value'),
'field types' => array('rgb_color'),
),
'rgb_box' => array(
'label' => t('Color block with label'),
'field types' => array('rgb_color'),
),
);
}
/**
* Implements hook_field_formatter_view().
*/
function rgb_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, &$items, $display) {
$element = array();
switch ($display['type']) {
case 'rgb_raw':
foreach ($items as $key => $value) {
$element[$key] = array(
'#type' => 'markup',
'#markup' => t('#@hex', array('@hex' => $value['rgb'])),
);
}
break;
case 'rgb_box':
foreach ($items as $key => $value) {
$element[$key] = array(
'#type' => 'markup',
'#markup' => '' . check_plain($value['label']) . '',
);
}
break;
}
return $element;
}