-
Earl Miles authoredEarl Miles authored
export.inc 8.71 KiB
<?php
// $Id$
/**
* @file
* Contains code to make it easier to have exportable objects.
*
* These are objects that can live in code OR the database, and versions in
* the database will override versions that are in code.
*
* This doesn't include a write routine since drupal_write_record is
* more or less sufficient.
*/
/**
in schema:
'export' = array(
'key' => 'name', // unique key to identify records
'default hook' => '', // name of hook to get default objects
'status' => '', // name of variable to store enabled/disabled status
'object' => '', // name of the object to put this data on
'sub records' => array(
/////// sub records not yet implemented
array(
'table' => // table subsidiary records are in
'parent' => // field in parent that matches
'child' => // field in child that matches
'array' => // name of array to store record in
'key' => // field to use as a key to store sub records.
),
),
*/
/**
* Load some number of exportable objects.
*
* This function will cache the objects, load subsidiary objects if necessary,
* check default objects in code and properly set them up. It will cache
* the results so that multiple calls to load the same objects
* will not cause problems.
*
* It attempts to reduce, as much as possible, the number of queries
* involved.
*
* @param $table
* The name of the table to be loaded from. Data is expected to be in the
* schema to make all this work.
* @param $type
* A string to notify the loader what the argument is
* - all: load all items. This is the default. $args is unused.
* - names: $args will be an array of specific named objects to load.
* - condition: $args will be a keyed array of conditions. The conditions
* must be in the schema for this table or errors will result.
* @param $args
* An array of arguments whose actual use is defined by the $type argument.
*/
function ctools_export_load_object($table, $type = 'all', $args = array()) {
static $cache = array();
static $cached_defaults = FALSE;
static $cached_database = FALSE;
$schema = ctools_export_get_schema($table);
$export = $schema['export'];
if (!isset($cache[$table])) {
$cache[$table] = array();
}
$return = array();
// Don't load anything we've already cached.
if ($type == 'names' && !empty($args)) {
foreach ($args as $name) {
if (isset($cache[$table][$name])) {
$return[$name] = $cache[$table][$name];
unset($args[$name]);
}
}
// If nothing left to load, return the result.
if (empty($args)) {
return $return;
}
}
// Build the query
$query = "SELECT * FROM {" . $table . "}";
$conditions = array();
$query_args = array();
// We do not have to load anything if we have already cached everything.
if ($type != 'all' || !$cached_database) {
// If they passed in names, add them to the query.
if ($type == 'names') {
$conditions[] = "$export[key] IN (" . db_placeholders($args, $schema['fields'][$export['key']]['type']) . ")";
$query_args = $args;
}
else if ($type == 'conditions') {
foreach ($args as $key => $value) {
$conditions[] = "$key = " . db_type_placeholder($schema['fields'][$key]['type']);
$query_args[] = $value;
}
}
// Make a string out of the conditions.
if ($conditions) {
$query .= " WHERE " . implode(' AND ', $conditions);
}
$result = db_query($query, $query_args);
// Unpack the results of the query onto objects and cache them.
while ($data = db_fetch_object($result)) {
$object = ctools_export_unpack_object($schema, $data, $export['object']);
$object->type = t('Normal');
$cache[$table][$object->{$export['key']}] = $object;
if ($type == 'conditions') {
$return[$object->{$export['key']}] = $object;
}
}
}
if ($type == 'all') {
$cached_database = TRUE;
}
// @todo Load subrecords.
if ($export['default hook'] && !$cached_defaults) {
// @todo add a method to load .inc files for this.
$defaults = module_invoke_all($export['default hook']);
$status = variable_get($export['status'], array());
foreach ($defaults as $object) {
if ($type == 'conditions') {
// if this does not match all of our conditions, skip it.
foreach ($args as $key => $value) {
if (!isset($object->$key) || $object->$key != $value) {
continue;
}
}
}
else if ($type == 'names') {
if (!in_array($names, $object->{$export['key']})) {
continue;
}
}
// Determine if default panel is enabled or disabled.
if (isset($status[$object->name])) {
$object->disabled = $status[$object->name];
}
if (!empty($cache[$table][$object->name])) {
$cache[$table][$object->name]->type = t('Overridden');
if ($type == 'conditions') {
$return[$object->name] = $cache[$table][$object->name]->type;
}
}
else {
$object->type = t('Default');
// move the 'display' to the new 'primary' location.
$object->primary = $object->display;
unset($object->display);
$cache[$table][$object->name] = $object;
if ($type == 'conditions') {
$return[$object->name] = $object;
}
}
}
// We only actually force this when retrieving all, because we may not
// have retrieved an object from the database and could thus incorrectly
// identify one as being a 'default' object when it is actually
// overridden. So we settle for a potential minor performance decrease
// in order to get this correct.
if ($type == 'all') {
// Make sure we don't run that again later on.
$cached_defaults = TRUE;
}
}
// If fetching all, we've done so and we are finished.
if ($type == 'all') {
return $cache[$table];
}
if ($type == 'names') {
foreach ($args as $name) {
if (isset($cache[$table][$name])) {
$return[$name] = $cache[$table][$name];
}
}
}
// For conditions,
return $return;
}
/**
* Unpack data loaded from the database onto an object.
*
* @param $schema
* The schema from drupal_get_schema().
* @param $data
* The data as loaded by db_fetch_object().
* @param $object
* If an object, data will be unpacked onto it. If a string
* an object of that type will be created.
*/
function ctools_export_unpack_object($schema, $data, $object = 'stdClass') {
if (is_string($object)) {
if (class_exists($object)) {
$object = new $object;
}
else {
$object = new stdClass;
}
}
// Go through our schema and build correlations.
foreach ($schema['fields'] as $field => $info) {
$object->$field = empty($info['serialize']) ? $data->$field : unserialize($data->$field);
}
return $object;
}
/**
* Export a field.
*
* This is a replacement for var_export(), allowing us to more nicely
* format exports. It will recurse down into arrays and will try to
* properly export bools when it can, though PHP has a hard time with
* this since they often end up as strings or ints.
*/
function ctools_var_export($var, $prefix = '') {
if (is_array($var)) {
if (empty($var)) {
$output = 'array()';
}
else {
$output = "array(\n";
foreach ($var as $key => $value) {
$output .= " '$key' => " . ctools_var_export($value, ' ') . ",\n";
}
$output .= ')';
}
}
else if (is_bool($var)) {
$output = $var ? 'TRUE' : 'FALSE';
}
else {
$output = var_export($var, TRUE);
}
if ($prefix) {
$output = str_replace("\n", "\n$prefix", $output);
}
return $output;
}
/**
* Get the schema for a given table.
*
* This looks for data the export subsystem needs and applies defaults so
* that it's easily available.
*/
function ctools_export_get_schema($table) {
$schema = drupal_get_schema($table);
if (!isset($schema['export'])) {
$schema['export'] = array();
}
// Add some defaults
$schema['export'] += array(
'key' => 'name',
'object' => 'stdClass',
'status' => 'default_' . $table,
'default hook' => 'default_' . $table,
);
return $schema;
}
/**
* Set the status of a default $object as a variable.
*
* The status, in this case, is whether or not it is 'enabled' or 'disabled'
* and is only valid for in-code objects that do not have a database
* equivalent. This function does not check to make sure $object actually
* exists.
*/
function ctools_export_set_status($table, $name, $new_status = TRUE) {
$schema = ctools_export_get_schema($table);
$status = variable_get($schema['export']['status'], array());
$status[$name] = $new_status;
variable_set($schema['export']['status'], $status);
}