Exporting our module in feature
When you are installing the contributed modules like context, panels, ckeditor etc., you can see that the configurations are exportable via features and the logic to export these configs via features are already implemented in these modules. Similarly we can also export our own configurations via features.
I am going to elaborate each step with an example.
Consider i have a schema in my module "mymodule" and the data in those configurations are going to be saved in a table "mymodule_configs".
This is my schema declaration in my install file.
$schema['mymodule_configs'] = array(
'fields' => array(
'id' => array(
'description' => 'The ID',
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
),
'name' => array(
'description' => 'Name of the config',
'type' => 'text',
'not null' => TRUE,
'serialize' => TRUE,
),
'Image_info' => array(
'type' => 'boolean',
'default' => FALSE,
),
'video_info' => array(
'type' => 'boolean',
'default' => FALSE,
),
'updated_count' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('id'),
);
The table above is a schema required in my module and this schema tracks my configurations required in my module. Imagine these information are collected from an admin UI (front end) and i have provided two configurations.
1. id = 1 , name = 'album configuration', Image info = TRUE, 'video info' => FALSE, 'updated_count' = 0
2. id=2, name = 'video album', ' Image info' = TRUE, 'video info' => TRUE, 'updated_count' = 0
Imagine updated_count is dynamic and it is 0 by default.
If i have three environments (eg : dev, qa and production), i need to manually enter these info in all these three environments. But if i can make it exportable via features, this is going to be very helpful as i don't need to do anything in other environments.
How can we export our own schema via features?
- Implement the feature hook to recognize our module.
Implement the hook "hook_features_api" to define the feature specification for your module.
/**
* Implements hook_features_api().
*/
function mymodule_features_api() {
return array(
'mymodule_config' => array(
'name' => 'My module configurations',
'file' => drupal_get_path('module', 'mymodule') .'/mymodule.features.inc',
'default_hook' => 'mymodule_config_features_settings',
'feature_source' => TRUE,
),
)
}
The main key value of this multi dimensional array represents the component name. (mymodule_config)
file - This is the place where the feature related code resides in our module and its in mymodule.features.inc
name - name of your feature (will show it in the feature admin UI)
default hook - This will be called when the feature gets enabled and the feature code is going to be exported in this module. This name will be taken for module name for the creation of exported configs.
feature source - specifies to the feature to make the feature components available for our module.
Next lines of codes are going to be written in the "mymodule.features.inc"
2. Specify which all instances of this component needs to be exported
/**
* Implements hook_features_export_options.
*/
function mymodule_config_features_export_options() {
$query = db_query("SELECT id,name FROM {mymodule_configs}");
$configs = $query->fetchAll();
foreach ($configs as $config) {
$options[$config->id] = $config->name;
}
return $options;
}
Note that above hook is a component hook. The format should be component (mymodule_config)_export_options.
Specify which all instances of this schema you want to export. I am going to export all the instances.
$config->id is going to be exported and $config->name should display in the feature instance (admin UI)
3.Specify the info that needs to be exported.
/*
* Implements hook_features_export
*/
function mymodule_config_features_export($data, &$export, $module_name) {
// We have module dependencies in order for this module to function properly
// So we'll add them here.
$export['dependencies']['mymodule'] = 'mymodule';
// The following is the simplest implementation of a straight object export.
// with no further export processors called.
foreach ($data as $component) {
// Callback to load the info of the particular config id.
$export['features']['mymodule_config'][$component] = $component;
}
return array();
}
The above hook is also a component hook and it is called by the hook_features_api.
Arguments to this hook
data - the array of components selected from "mymodule_config_features_export_options".
export - what needs to be exported
module - this is the name of the module that is going to be created with exported code.
We can specify the dependencies and the object that needs to be exported. When a specific instance of the component we are looking to export is selected, this will include the necessary item, plus
any dependencies into our export array.
This module should be strictly dependant on the "mymodule" and i have given that dependency in code. You can select dependencies from feature admin UI also.
4.Export the data
This specifies what code to be generated while exporting our module.
/*
* Implements hook_features_export_render.
*/
function mymodule_config_features_export_render($module_name, $data, $export = NULL) {
$code = array();
foreach ($data as $config_id) {
// Retrieve the mymodule config info.
$item = _mymodule_cofig_load_congigurations($config_id);
// Add the info to feature.
unset($item->updated_count);
$items[$config_id] = $item;
}
$code = ' $data = ' . features_var_export($items, ' ') . ';' . PHP_EOL;
$code[] = ' return $data;';
return array('mymodule_config_feature_settings' => $code);
}
// Call back to retrieve the mymodule configs.
function mymodule_cofig_load_congigurations($id) {
$query = db_query("SELECT * FROM {mymodule_configs}");
$configs = $query->fetchAll();
return $configs;
}
I don't want to export the updated_count as it is dynamic and it depends on total number of contents and it may vary in each environments and it may show overriden all the time. So i am not exporting that.
5. Rebuild the data to be exported
This hook will be executed when the module is installed.
/**
* Implements hook_features_rebuild().
*/
function mymodule_configs_features_rebuild($module) {
mymodule_configs_features_revert($module);
}
6.Revert the changes
This is going to be executed to reflect the changes from the exported code to the database.
/**
* Implements hook_features_revert().
*/
function mymodule_configs_features_revert($module) {
if ($data = features_get_default('mymodule_configs', $module)) {
foreach ($data as $id => $config) {
db_query("DELETE FROM {mymodule_configs} WHERE id = :id", array(':id' => $id));
db_query("INSERT INTO {mymodule_configs} (id, name, image_info, video_info) VALUES(:id, :name, :image_info, :video_info)", array(':id' => $id, ':name' => $item['name'], :image_info => $item['image_info'], :video_info => $item['video_info']));
}
}
}
We can specify additional options in this revert logic. (For example , i need to make a flag value FALSE when this is reverted. I can specify that code in revert logic.
The revert logic will be active when the database and code is not matching.
Now the next step is to go to feaure UI and check whether the "mymodule configs" is showing in the feature admin UI export section. Select the instances you want to export and download the feature.
You can see the feature created file in "feature_name.mymodule_config_feature_settings.inc"