Using Chaos Tools module to create exportables

Recently I created a patch for the migrate module so you can export content sets, paste them into your module and have them automatically imported. This works in much the same way as Views handles default views in code, with the version in the database overriding the version in code. Using the Chaos Tools (ctools) module it was, relatively, easy to add this functionality. Here are the steps involved if you want to add it to your own module.

First, for each type of exportable object you want to handle, you need to create a table in the database to store the data in. Using Drupal's schema API you can easily define you table structure. As normal you have the 'fields', 'primary key' and other properties. However you need to define one additional property called export. In our example below, we will refer to our object as 'myobj'.

The 'export' property is a keyed array which allows you to define some attributes, such as your module's current API version and the minimum one still supported. Most importantly it allows you to define the function hook name (default hook) that other modules need to use in order to define default objects, and also the base name for the include file that contains this hook (api). The default hook is analogous to Views' hook_default_view_views() and the api file base name would match the 'views_default' part of the file name of 'examplemodule.views_default.inc'.

<?php
function mymodule_schema() {
 
$schema['mymodule_myobj'] = array(
   
'description' => t('Table storing myobj definitions.'),
   
'export' => array(
     
'key' => 'oid',
     
'identifier' => 'myobj',
     
'default hook' => 'default_mymodule_myobj'// Function hook name.
     
'api' => array(
       
'owner' => 'mymodule',
       
'api' => 'default_mymodule_myobjs'// Base name for api include files.
       
'minimum_version' => 1,
       
'current_version' => 1,
      ),
    ),
   
'fields' => array(
     
'oid' => array(
       
'type' => 'serial',
       
'unsigned' => TRUE,
       
'not null' => TRUE,
      ),
     ......
   
'primary key' => array('fooid'),
  );
  return
$schema;
}
?>

In your hook_menu() you should define a path where users can export a 'myobj' object. In our example below, this is 'admin/build/mymodule/%/export' where the % matches our myobj's unique identifier. This identifier will be passed to a form called mymodule_export_myobj.

<?php
/**
* Implementation of hook_menu().
*/
function mymodule_menu() {
 
$items['admin/build/mymodule/%/export'] = array(
   
'title' => 'Export',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('mymodule_export_myobj', 3),
   
'access arguments' => array('mymodule export permission'),
   
'type' => MENU_CALLBACK,
  );

  return

$items;
}

/**
* Export a myobj and display it in a form.
*/
function mymodule_export_myobj(&$form_state, $oid) {
 
$obj = mymodule_myobj_load($oid);
 
drupal_set_title(check_plain($obj->description));
 
$code = mymodule_myobj_export($obj);
 
$lines = substr_count($code, "\n");

 

$form['export'] = array(
   
'#title' => t('Export data'),
   
'#type' => 'textarea',
   
'#value' => $code,
   
'#rows' => $lines,
   
'#description' => t('Copy the export text and paste it into another myobj using the import function.'),
  );
  return
$form;
}
?>

The mymodule_export_myobj() form function calls two functions, mymodule_myobj_load() to fetch the object in the desired format, and mymodule_myobj_export() to generate the exportable.

<?php
/**
* Load a single myobj.
*/
function mymodule_myobj_load($oid) {
 
ctools_include('export');
 
$result = ctools_export_load_object('mymodule_myobjs', 'names', array($oid));
  if (isset(
$result[$oid])) {
    return
$result[$oid];
  }
}

/**
* Export a myobj.
*/
function mymodule_myobj_export($obj, $indent = '') {
 
ctools_include('export');
 
$output = ctools_export_object('mymodule_myobjs', $obj, $indent);
  return
$output;
}
?>

Both of these functions call ctools_include('export') which loads the necessary ctools file containing the functions relating to exportables. ctools_export_load_object() fetches all the definitions it has for your object from the database, using the table schema as defined in your install file. It will also load all the default object hooks (in our example this would be hook_default_mymodule_myobj()), and merge new objects into its list to return. In addition, it handles things like caching and the loading of subsidiary objects if necessary. ctools_export_object() takes the passed in loaded object and converts it into PHP code which can then be printed out in the export form.

The ctools module doesn't provide an import function. It's up to each module to carry out this task, but with Drupal's drupal_write_record() function, this isn't so big a deal.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Great article!

I would say the only thing that it misses is that it doesn't discuss the 'name' field which is how exportable objects are identified as being in code or not in code when sing the default hook. It doesn't have to be 'name' which it is by default, there's a flag in the 'export' section of the schema that you can use to change it.

You can also hook into bulk export for easy bulk exporting of objects.

Interesting

Hi Stella, interesting article. I wonder if this could be applied to content types, so that I can store them in code rather than in the DB?

Barbra Scurley

I don't know If I said it already but ...I'm so glad I found this site...Keep up the good work I read a lot of blogs on a daily basis and for the most part, people lack substance but, I just wanted to make a quick comment to say GREAT blog. Thanks, :) A definite great read....

Edward Lane

Hey, great blog...but I don’t understand how to add your site in my rss reader. Can you Help me, please :)