Import/Export API (Input/Output API)

Events happening in the community are now at Drupal community events on www.drupal.org.
moonray's picture

Based on the discussions we had at DrupalCon, I've written up the beginnings of an API with a standardized set of hooks and some functions.

The details of how, for instance, the block module would save an object to disc might start through using the not-so-desirable form API, due to lack of another option. In the future, however, this could be changed using a more optimal method.

Also included is a generalized function to fetch set of default objects that are embedded in PHP (like views module or context module implement).

Discussion is encouraged. :-)

io.module

<?php
/**
* Fetches a list of objects that are implemented using standardized i/o.
*
* @return
*   An array of object types.
*/
function get_registered_objects() {
 
$types = module_invoke_all('register_objects');
   
    return
$types;
}

/**
* Load an object of type $type.
*
* @param $type
*   Object type to load
* @param
*   ... Arguments to pass to the hook that identify a specific object (i.e. nid).
*
* @return
*   An object.
*/
function load($type) {
 
$object = module_invoke_all('load_'. $type);
 
drupal_alter('load_'. $type, $object);
 
  return
$object;
}

/**
* Save an object.
*/
function save($type, $object) {
 
module_invoke_all('verify_'. $type, $object);
 
drupal_alter('verify_'. $type, $object);
 
 
module_invoke_all('save_'. $type, $object);
 
drupal_alter('save_'. $type, $object);
}

/**
* Invokes hook_OBJECT_define() to collect all OBJECTS provided in code by modules.
*
* @return
*   An array of default OBJECT objects.
*/
function defaults($type) {
  static
$cache = array();
 
  if (!
$cache[$type]) {
   
$cache[$type] = array();
    foreach (
module_implements($type .'_define') as $module) {
     
$function = $module .'_'. $type .'_define';
     
$cache[$type] = array_merge($cache[$type], $function());
    }
    foreach (
$cache[$type] as $key => $object) {
     
$object = (object) $object;
     
$cache[$type][$key] = $object;
    }
  }
 
  return
$cache[$type];
}
?>

An example for the block module.
modules/block.io.inc

<?php
/**
* Implementation of hook_register_objects().
*
* @return
*   Returns an array of object names.
*/
function block_register_objects() {
 
$objects = array('block');
 
  return
$objects;
}

/**
* Implementation of hook_default_OBJECT().
*
* @return
*   Returns object in its default state. This allows you to only pass in
*   only the changes, resulting in shorter code.
*/
function block_default_block() {
  
$block = new stdClass;
  
$block->title = '';
  
$block->body = '';
   ...
etc.;
  
   return
$block;
}

/**
* Implementation of hook_load_OBJECT().
*
* @param $module
* @param $delta
*
* @return
*   Returns a block object.
*/
function block_load_block($module, $delta) {
 
// Do magic
 
 
return $object;
}

/**
* Implementation of hook_verify_OBJECT().
*
* @param $object
*/
function block_verify_block($object) {
 
// Probably use formapi and hook_verify to verify object
}

/**
* Implementation of hook_save_OBJECT().
*
* @param $object
*/
function block_save_block($object) {
 
// Save the block
}
?>

Comments

Thanks for pulling this

jmiccolis's picture

Thanks for pulling this together, though I would like some clarification on a few points.;

  1. Can you elaborate on how you see get_registered_objects() being useful. What will it enable?
  2. In your api you document hook_OBJECT_define() but in the example you implement hook_default_OBJECT(). Each of these seems to serve a different and possibly conflicting usage. Which approach are you advocating?
  3. I'm not sure at all about the load/save/verify hooks. How do you see them working, what do you see them making possible?

The purpose I see

moonray's picture
  1. The purpose I see get_registered_objects() serve is to announce which objects exist (obviously). A module can, and likely will have more than one object.

    So, what could it be used for? An import module would be able to use this to verify that all modules needed to import the object defined by a configuration file exist. Because the OBJECT in hook_load_OBJECT can be anything, there is no way to really know what OBJECTs are available without having to write some convoluted code (an issue we have with the variables right now, for instance).

  2. To be honest, hook_default_OBJECT() was a half-thought, and can probably be scrapped. Having a way to minimize export code/text would be nice, but maybe that's a little out of scope here?
  3. load/save/verify is how eventually an object should always be handled by code. An example that already exists is node_load() which always (well, mostly) returns a fleshed out object. The same goes for node_save().

    Verify would eventually replace the current form_api version hook_validate(), and ensure an object is always valid before being written to db/disc, no matter if the object was created by code, or entered through a web form.

    Having that same consistent way to load and save objects no matter whether they are nodes, views, configuration options, etc. will make for consistency and an easy way to write code to import/export.

shameless plug / call for collaboration

jpetso's picture

Having that same consistent way to load and save objects no matter whether they are nodes, views, configuration options, etc. will make for consistency and an easy way to write code to import/export.

I took a shot at the same objectives, with the difference of breaking it down to a field level - the result is Field Tool. Perhaps with a couple of changes/additions, we could find a common approach for not only importing and exporting the fields of such an object but also the object itself?

Packaging & Deployment

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: