Exportable objects are objects that can live either in the database or in code, or in both. If they live in both, then the object in code is considered to be "overridden", meaning that the version in code is ignored in favor of the version in the database. The main benefit to this is that you can move objects that are intended to be structure or feature-related into code, thus removing them entirely from the database. This is a very important part of the deployment path, since in an ideal world, the database is primarily user generated content, whereas site structure and site features should be in code. However, many many features in Drupal rely on objects being in the database and provide UIs to create them. Using this system, you can give your objects dual life. They can be created in the UI, exported into code and put in revision control. Views and Panels both use this system heavily. Plus, any object that properly implements this system can be utilized by the Features module to be used as part of a bundle of objects that can be turned into feature modules. Typically, exportable objects have two identifiers. One identifier is a simple serial used for database identification. It is a primary key in the database and can be used locally. It also has a name which is an easy way to uniquely identify it. This makes it much less likely that importing and exporting these objects across systems will have collisions. They still can, of course, but with good name selection, these problems can be worked around.
function mymodule_schema() { $schema['mymodule_myobj'] = array( 'description' => t('Table storing myobj definitions.'), 'export' => array( 'key' => 'name', 'identifier' => 'obj', // Exports will be as $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( 'name' => array( 'type' => 'varchar', 'length' => '255', 'description' => 'Unique ID for this object. Used to identify it programmatically.', ), 'oid' => array( 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.', 'no export' => TRUE, // Do not export database-only keys. ), // ...... 'primary key' => array('oid'), 'unique keys' => array( 'name' => array('name'), ), ); return $schema; }
/** * Load a single myobj. */ function mymodule_myobj_load($name) { ctools_include('export'); $result = ctools_export_load_object('mymodule_myobjs', 'names', array($name)); if (isset($result[$name])) { return $result[$name]; } }
/** * Load a single myobj. */ function mymodule_myobj_save(&$myobj) { $update = (isset($myobj->oid) && is_numeric($myobj->oid)) ? array('oid') : array(); drupal_write_record('myobj', $myobj, $update); }
/** * Implementation of hook_menu(). */ function mymodule_menu() { $items['admin/build/mymodule/%/export'] = array( 'title' => 'Export', 'page callback' => 'mymodule_export_myobj', 'page arguments' => array(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) { $myobj = mymodule_myobj_load($oid); drupal_set_title(check_plain($myobj->description)); $code = mymodule_myobj_export($myobj); return drupal_get_form('ctools_export_form', $code, $check_plain($myobj->description)); }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.
/** * 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. * * By declaring this function in the schema, anyone can easily export a myobj * just by knowing that myobj exists. */ function mymodule_myobj_export($myobj, $indent = '') { ctools_include('export'); $output = ctools_export_object('mymodule_myobjs', $myobj, $indent); return $output; }
/** * Implementation of hook_menu(). */ function mymodule_menu() { $items['admin/build/mymodule/import'] = array( 'title' => 'Import', 'page callback' => 'drupal_get_form', 'page arguments' => array('mymodule_export_myobj'), 'access arguments' => array('mymodule import permission'), 'type' => MENU_CALLBACK, ); return $items; }In this case, the page callback is a form, where the user can paste the exported code. We can provide an optional name field to change the name of the object while importing. This can be used as a poor man's clone. The method used here can also be used to very easily clone objects without going through the export/import process manually.
/** * Form from page callback to import a myobj */ function mymodule_export_myobj(&$form, &$form_state) { $form['name'] = array( '#type' => 'textfield', '#title' => t('Myobj name'), '#description' => t('Enter the name of the new myobj. This is optional and is not necessary if you do not wish to rename the object.'), ); $form['object'] = array( '#type' => 'textarea', '#title' => t('Paste variant code here'), '#rows' => 15, ); } /** * Make sure that an import actually provides a handler. */ function mymodule_export_myobj_validate($form, &$form_state) { // First, run the PHP and turn the input code into an object. ob_start(); eval($form_state['values']['object']); ob_end_clean(); // The object should appear as $myobj. This was the "identifier" set in the export section of the schema. if (empty($myobj)) { $errors = ob_get_contents(); if (empty($errors)) { $errors = t('No myobj found.'); } form_error($form['object'], t('Unable to get a myobj from the import. Errors reported: @errors', array('@errors' => $errors))); } $form_state['obj'] = obj; } /** * Save the imported object. */ function page_manager_handler_import_submit($form, &$form_state) { $myobj = $form_state['obj']; if (!empty($form_state['values']['name'])) { $myobj->name = $form_state['values']['name']; } mymodule_myobj_save($myobj); $form_state['redirect'] = 'admin/build/mymodule/' . $myobj->name . '/edit'; }