Index: modules/field/field.info.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.info.inc,v retrieving revision 1.10 diff -u -r1.10 field.info.inc --- modules/field/field.info.inc 1 Aug 2009 06:03:12 -0000 1.10 +++ modules/field/field.info.inc 1 Aug 2009 22:30:50 -0000 @@ -87,6 +87,7 @@ 'field types' => array(), 'widget types' => array(), 'formatter types' => array(), + 'storage types' => array(), 'fieldable types' => array(), ); @@ -119,7 +120,7 @@ } drupal_alter('field_widget_info', $info['widget types']); - // Populate formatters. + // Populate formatter types. foreach (module_implements('field_formatter_info') as $module) { $formatter_types = (array) module_invoke($module, 'field_formatter_info'); foreach ($formatter_types as $name => $formatter_info) { @@ -133,6 +134,20 @@ } drupal_alter('field_formatter_info', $info['formatter types']); + // Populate storage types. + foreach (module_implements('field_storage_info') as $module) { + $storage_types = (array) module_invoke($module, 'field_storage_info'); + foreach ($storage_types as $name => $storage_info) { + // Provide defaults. + $storage_info += array( + 'settings' => array(), + ); + $info['storage types'][$name] = $storage_info; + $info['storage types'][$name]['module'] = $module; + } + } + drupal_alter('field_storage_info', $info['storage types']); + // Populate information about 'fieldable' entities. foreach (module_implements('fieldable_info') as $module) { $fieldable_types = (array) module_invoke($module, 'fieldable_info'); @@ -284,8 +299,8 @@ * returned. * @return * Either a widget type description, as provided by - * hook_field_widget_info(), or an array of all existing widget - * types, keyed by widget type name. + * hook_field_widget_info(), or an array of all existing widget types, keyed + * by widget type name. */ function field_info_widget_types($widget_type = NULL) { $info = _field_info_collate_types(); @@ -307,8 +322,9 @@ * (optional) A formatter type name. If ommitted, all formatter types will be * returned. * @return - * Either a formatter type description, as provided by hook_field_formatter_info(), - * or an array of all existing widget types, keyed by widget type name. + * Either a formatter type description, as provided by + * hook_field_formatter_info(), or an array of all existing formatter types, + * keyed by formatter type name. */ function field_info_formatter_types($formatter_type = NULL) { $info = _field_info_collate_types(); @@ -324,6 +340,30 @@ } /** + * Return hook_field_storage_info() data. + * + * @param $storage_type + * (optional) A storage type name. If ommitted, all storage types will be + * returned. + * @return + * Either a storage type description, as provided by + * hook_field_storage_info(), or an array of all existing storage types, + * keyed by storage type name. + */ +function field_info_storage_types($storage_type = NULL) { + $info = _field_info_collate_types(); + $storage_types = $info['storage types']; + if ($storage_type) { + if (isset($storage_types[$storage_type])) { + return $storage_types[$storage_type]; + } + } + else { + return $storage_types; + } +} + +/** * Return hook_fieldable_info() data. * * @param $obj_type @@ -468,8 +508,8 @@ * @param $type * A widget type name. * @return - * The field type's default settings, as provided by hook_field_info(), or an - * empty array. + * The widget type's default settings, as provided by + * hook_field_widget_info(), or an empty array. */ function field_info_widget_settings($type) { $info = field_info_widget_types($type); @@ -482,8 +522,8 @@ * @param $type * A field formatter type name. * @return - * The field formatter's default settings, as provided by - * hook_field_info(), or an empty array. + * The formatter type's default settings, as provided by + * hook_field_formatter_info(), or an empty array. */ function field_info_formatter_settings($type) { $info = field_info_formatter_types($type); @@ -491,5 +531,19 @@ } /** + * Return a field formatter's default settings. + * + * @param $type + * A field storage type name. + * @return + * The storage type's default settings, as provided by + * hook_field_storage_info(), or an empty array. + */ +function field_info_storage_settings($type) { + $info = field_info_storage_types($type); + return isset($info['settings']) ? $info['settings'] : array(); +} + +/** * @} End of "defgroup field_info" */ Index: modules/field/field.attach.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v retrieving revision 1.33 diff -u -r1.33 field.attach.inc --- modules/field/field.attach.inc 16 Jul 2009 10:30:12 -0000 1.33 +++ modules/field/field.attach.inc 1 Aug 2009 22:30:49 -0000 @@ -446,9 +446,11 @@ $function($obj_type, $queried_objects, $age, $skip_fields, $options); } - // Invoke the storage engine's hook_field_storage_load(): the field storage - // engine loads the rest. - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age, $skip_fields, $options); + // Invoke storage engines hook_field_storage_load(): to load the rest. + foreach (module_implements('field_storage_load') as $module) { + $function = $module . '_field_storage_load'; + $function($obj_type, $queried_objects, $age, $skip_fields, $options); + } // Invoke field-type module's hook_field_load(). _field_invoke_multiple('load', $obj_type, $queried_objects, $age, $options); @@ -655,8 +657,11 @@ $function($obj_type, $object, $skip_fields); } - // Field storage module saves any remaining unsaved fields. - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_INSERT, $skip_fields); + // Field storage backends save any remaining unsaved fields. + foreach (module_implements('field_storage_write') as $module) { + $function = $module . '_field_storage_write'; + $function($obj_type, $object, FIELD_STORAGE_INSERT, $skip_fields); + } list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object); if ($cacheable) { @@ -683,8 +688,11 @@ $function($obj_type, $object, $skip_fields); } - // Field storage module saves any remaining unsaved fields. - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_UPDATE, $skip_fields); + // Field storage backends save any remaining unsaved fields. + foreach (module_implements('field_storage_write') as $module) { + $function = $module . '_field_storage_write'; + $function($obj_type, $object, FIELD_STORAGE_UPDATE, $skip_fields); + } list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object); if ($cacheable) { @@ -703,7 +711,12 @@ */ function field_attach_delete($obj_type, $object) { _field_invoke('delete', $obj_type, $object); - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_delete', $obj_type, $object); + + // Field storage backends delete their data. + foreach (module_implements('field_storage_delete') as $module) { + $function = $module . '_field_storage_delete'; + $function($obj_type, $object); + } // Let other modules act on deleting the object. foreach (module_implements('field_attach_delete') as $module) { @@ -728,7 +741,12 @@ */ function field_attach_delete_revision($obj_type, $object) { _field_invoke('delete_revision', $obj_type, $object); - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_delete_revision', $obj_type, $object); + + // Field storage backends delete their data. + foreach (module_implements('field_storage_delete_revision') as $module) { + $function = $module . '_field_storage_delete_revision'; + $function($obj_type, $object); + } // Let other modules act on deleting the revision. foreach (module_implements('field_attach_delete_revision') as $module) { @@ -831,7 +849,8 @@ } // If the request hasn't been handled, let the storage engine handle it. if (!$skip_field) { - $function = variable_get('field_storage_module', 'field_sql_storage') . '_field_storage_query'; + $field = field_info_field($field_name); + $function = $field['storage']['module'] . '_field_storage_query'; $results = $function($field_name, $conditions, $count, $cursor, $age); } @@ -923,8 +942,6 @@ * The name of the newly created bundle. */ function field_attach_create_bundle($bundle) { - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_create_bundle', $bundle); - // Clear the cache. field_cache_clear(); @@ -943,7 +960,6 @@ * The new name of the bundle. */ function field_attach_rename_bundle($bundle_old, $bundle_new) { - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_rename_bundle', $bundle_old, $bundle_new); db_update('field_config_instance') ->fields(array('bundle' => $bundle_new)) ->condition('bundle', $bundle_old) @@ -972,12 +988,15 @@ * The bundle to delete. */ function field_attach_delete_bundle($bundle) { - // Delete the instances themseves + // First, delete the instances themseves. $instances = field_info_instances($bundle); foreach ($instances as $instance) { field_delete_instance($instance['field_name'], $bundle); } + // Clear the cache. + field_cache_clear(); + // Let other modules act on deleting the bundle. foreach (module_implements('field_attach_delete_bundle') as $module) { $function = $module . '_field_attach_delete_bundle'; Index: modules/field/field.crud.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.crud.inc,v retrieving revision 1.21 diff -u -r1.21 field.crud.inc --- modules/field/field.crud.inc 14 Jul 2009 10:27:29 -0000 1.21 +++ modules/field/field.crud.inc 1 Aug 2009 22:30:50 -0000 @@ -232,7 +232,9 @@ 'cardinality' => 1, 'locked' => FALSE, 'settings' => array(), + 'storage' => array(), ); + // Create all per-field-type properties (needed here as long as we have // settings that impact column definitions). $field['settings'] += field_info_field_settings($field['type']); @@ -240,13 +242,27 @@ $field['active'] = 1; $field['deleted'] = 0; + // Provide default storage. + $field['storage'] += array( + 'type' => 'field_sql_storage', + 'settings' => array(), + ); + // Check that the storage type is known. + $storage_type = field_info_storage_types($field['storage']['type']); + if (!$storage_type) { + throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type']))); + } + $storage_module = $storage_type['module']; + // TODO : storage_active, columns, de-activate... + $storage_active = module_exists($storage_module); + // Provide default storage settings. + $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']); + // Collect storage information. $schema = (array) module_invoke($field['module'], 'field_schema', $field); $schema += array('columns' => array(), 'indexes' => array()); - // 'columns' are hardcoded in the field type. $field['columns'] = $schema['columns']; - // 'indexes' can be both hardcoded in the field type, and specified in the // incoming $field definition. $field += array( @@ -264,9 +280,9 @@ // Store the field and create the id. drupal_write_record('field_config', $field); - // Invoke hook_field_storage_create_field after the field is - // complete (e.g. it has its id). - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_create_field', $field); + // Invoke hook_field_storage_create_field after drupal_write_record() sets the + // field id. + module_invoke($storage_module, 'field_storage_create_field', $field); // Clear caches field_cache_clear(TRUE); @@ -342,6 +358,7 @@ // Make sure all settings expected in the current execution context are // present. $field['settings'] += field_info_field_settings($field['type']); + $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']); module_invoke_all('field_read_field', $field); @@ -367,8 +384,9 @@ * The field name to delete. */ function field_delete_field($field_name) { - // Mark field storage for deletion. - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_delete_field', $field_name); + // Mark field data for deletion. + $field = field_read_field($field_name); + module_invoke($field['storage']['module'], 'field_storage_delete_field', $field_name); // Mark any instances of the field for deletion. db_update('field_config_instance') @@ -685,8 +703,10 @@ ->condition('bundle', $bundle) ->execute(); - // Mark all data associated with the field for deletion. - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_delete_instance', $field_name, $bundle); + // Mark instance data for deletion. + $field = field_read_field($field_name); + module_invoke($field['storage']['module'], 'field_storage_delete_instance', $field_name, $bundle); + // Clear the cache. field_cache_clear(); } Index: modules/field/field.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.api.php,v retrieving revision 1.24 diff -u -r1.24 field.api.php --- modules/field/field.api.php 1 Aug 2009 06:03:12 -0000 1.24 +++ modules/field/field.api.php 1 Aug 2009 22:30:49 -0000 @@ -1070,6 +1070,48 @@ */ /** + * Expose Field API storage backends. + * + * TODO + * @return + * An array describing the storage backends implemented by the module. + * + * The keys are storage type names. To avoid name clashes, storage type + * names should be prefixed with the name of the module that exposes them. + * + * The values are arrays describing the storage type, with the following + * key/value pairs: + * - label: The human-readable name of the storage type. + * - description: A short description for the storage type. + * - settings: An array whose keys are the names of the settings available + * for the storage type, and whose values are the default values for those + * settings. + */ +function hook_field_storage_info() { + return array( + 'field_sql_storage' => array( + 'label' => t('Default SQL storage'), + 'description' => t('TODO'), + 'settings' => array(), + ), + ); +} + +/** + * Perform alterations on Field API storage types. + * + * @param $info + * Array of informations on storage types exposed by + * hook_field_field_storage_info() implementations. + */ +function hook_field_storage_info_alter(&$info) { + // Add a setting to a storage type. + $info['field_sql_storage']['settings'] += array( + 'mymodule_additional_setting' => 'default value', + ); +} + +/** * Load field data for a set of objects. * * @param $obj_type @@ -1159,26 +1201,6 @@ } /** - * Act on creation of a new bundle. - * - * @param $bundle - * The name of the bundle being created. - */ -function hook_field_storage_create_bundle($bundle) { -} - -/** - * Act on a bundle being renamed. - * - * @param $bundle_old - * The old name of the bundle. - * @param $bundle_new - * The new name of the bundle. - */ -function hook_field_storage_rename_bundle($bundle_old, $bundle_new) { -} - -/** * Act on creation of a new field. * * @param $field Index: modules/field/modules/field_sql_storage/field_sql_storage.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module,v retrieving revision 1.17 diff -u -r1.17 field_sql_storage.module --- modules/field/modules/field_sql_storage/field_sql_storage.module 15 Jul 2009 17:55:18 -0000 1.17 +++ modules/field/modules/field_sql_storage/field_sql_storage.module 1 Aug 2009 22:30:50 -0000 @@ -18,6 +18,18 @@ } /** + * Implement hook_field_storage_info(). + */ +function field_sql_storage_field_storage_info() { + return array( + 'field_sql_storage' => array( + 'label' => t('Default SQL storage'), + 'description' => t('TODO'), + ), + ); +} + +/** * Generate a table name for a field data table. * * @param $field @@ -212,7 +224,8 @@ foreach ($objects as $obj) { list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $obj); foreach (field_info_instances($bundle) as $field_name => $instance) { - if (!isset($skip_fields[$field_name]) && (!isset($options['field_name']) || $options['field_name'] == $instance['field_name'])) { + $field = field_info_field($field_name); + if ($field['storage']['type'] == 'field_sql_storage' && !isset($skip_fields[$field_name]) && (!isset($options['field_name']) || $options['field_name'] == $instance['field_name'])) { $objects[$id]->{$field_name} = array(); $field_ids[$field_name][] = $load_current ? $id : $vid; $delta_count[$id][$field_name] = 0; @@ -259,67 +272,64 @@ $etid = _field_sql_storage_etid($obj_type); $instances = field_info_instances($bundle); - foreach ($instances as $instance) { - $field_name = $instance['field_name']; - if (isset($skip_fields[$field_name])) { - continue; - } - + foreach ($instances as $field_name => $instance) { $field = field_info_field($field_name); - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); + if ($field['storage']['type'] == 'field_sql_storage' && !isset($skip_fields[$field_name])) { + $table_name = _field_sql_storage_tablename($field); + $revision_name = _field_sql_storage_revision_tablename($field); - // Leave the field untouched if $object comes with no $field_name property. - // Empty the field if $object->$field_name is NULL or an empty array. + // Leave the field untouched if $object comes with no $field_name property. + // Empty the field if $object->$field_name is NULL or an empty array. - // Function property_exists() is slower, so we catch the more frequent cases - // where it's an empty array with the faster isset(). - if (isset($object->$field_name) || property_exists($object, $field_name)) { - // Delete and insert, rather than update, in case a value was added. - if ($op == FIELD_STORAGE_UPDATE) { - db_delete($table_name)->condition('etid', $etid)->condition('entity_id', $id)->execute(); - if (isset($vid)) { - db_delete($revision_name)->condition('etid', $etid)->condition('entity_id', $id)->condition('revision_id', $vid)->execute(); - } - } - - if ($object->$field_name) { - // Prepare the multi-insert query. - $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta'); - foreach ($field['columns'] as $column => $attributes) { - $columns[] = _field_sql_storage_columnname($field_name, $column); - } - $query = db_insert($table_name)->fields($columns); - if (isset($vid)) { - $revision_query = db_insert($revision_name)->fields($columns); + // Function property_exists() is slower, so we catch the more frequent cases + // where it's an empty array with the faster isset(). + if (isset($object->$field_name) || property_exists($object, $field_name)) { + // Delete and insert, rather than update, in case a value was added. + if ($op == FIELD_STORAGE_UPDATE) { + db_delete($table_name)->condition('etid', $etid)->condition('entity_id', $id)->execute(); + if (isset($vid)) { + db_delete($revision_name)->condition('etid', $etid)->condition('entity_id', $id)->condition('revision_id', $vid)->execute(); + } } - $delta_count = 0; - foreach ($object->$field_name as $delta => $item) { - $record = array( - 'etid' => $etid, - 'entity_id' => $id, - 'revision_id' => $vid, - 'bundle' => $bundle, - 'delta' => $delta, - ); + if ($object->$field_name) { + // Prepare the multi-insert query. + $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta'); foreach ($field['columns'] as $column => $attributes) { - $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL; + $columns[] = _field_sql_storage_columnname($field_name, $column); } - $query->values($record); + $query = db_insert($table_name)->fields($columns); if (isset($vid)) { - $revision_query->values($record); + $revision_query = db_insert($revision_name)->fields($columns); } - if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) { - break; + $delta_count = 0; + foreach ($object->$field_name as $delta => $item) { + $record = array( + 'etid' => $etid, + 'entity_id' => $id, + 'revision_id' => $vid, + 'bundle' => $bundle, + 'delta' => $delta, + ); + foreach ($field['columns'] as $column => $attributes) { + $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL; + } + $query->values($record); + if (isset($vid)) { + $revision_query->values($record); + } + + if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) { + break; + } } - } - // Execute the insert. - $query->execute(); - if (isset($vid)) { - $revision_query->execute(); + // Execute the insert. + $query->execute(); + if (isset($vid)) { + $revision_query->execute(); + } } } } @@ -336,22 +346,47 @@ $etid = _field_sql_storage_etid($obj_type); $instances = field_info_instances($bundle); - foreach ($instances as $instance) { - $field_name = $instance['field_name']; + foreach ($instances as $field_name => $instance) { $field = field_read_field($field_name); - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); - db_delete($table_name) - ->condition('etid', $etid) - ->condition('entity_id', $id) - ->execute(); - db_delete($revision_name) - ->condition('etid', $etid) - ->condition('entity_id', $id) - ->execute(); + if ($field['storage']['type'] == 'field_sql_storage') { + $table_name = _field_sql_storage_tablename($field); + $revision_name = _field_sql_storage_revision_tablename($field); + db_delete($table_name) + ->condition('etid', $etid) + ->condition('entity_id', $id) + ->execute(); + db_delete($revision_name) + ->condition('etid', $etid) + ->condition('entity_id', $id) + ->execute(); + } } } +/** + * Implement hook_field_storage_delete_revision(). + * + * This function actually deletes the data from the database. + */ +function field_sql_storage_field_storage_delete_revision($obj_type, $object) { + list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object); + $etid = _field_sql_storage_etid($obj_type); + + if (isset($vid)) { + $instances = field_info_instances($bundle); + foreach ($instances as $field_name => $instance) { + $field = field_read_field($field_name); + if ($field['storage']['type'] == 'field_sql_storage') { + $revision_name = _field_sql_storage_revision_tablename($field); + db_delete($revision_name) + ->condition('etid', $etid) + ->condition('entity_id', $id) + ->condition('revision_id', $vid) + ->execute(); + } + } + } +} /** * Implement hook_field_storage_query(). @@ -443,30 +478,6 @@ } /** - * Implement hook_field_storage_delete_revision(). - * - * This function actually deletes the data from the database. - */ -function field_sql_storage_field_storage_delete_revision($obj_type, $object) { - list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object); - $etid = _field_sql_storage_etid($obj_type); - - if (isset($vid)) { - $instances = field_info_instances($bundle); - foreach ($instances as $instance) { - $field_name = $instance['field_name']; - $field = field_read_field($field_name); - $revision_name = _field_sql_storage_revision_tablename($field); - db_delete($revision_name) - ->condition('etid', $etid) - ->condition('entity_id', $id) - ->condition('revision_id', $vid) - ->execute(); - } - } -} - -/** * Implement hook_field_storage_delete_instance(). * * This function simply marks for deletion all data associated with the field. @@ -486,10 +497,10 @@ } /** - * Implement hook_field_storage_rename_bundle(). + * Implement hook_field_attach_rename_bundle(). */ -function field_sql_storage_field_storage_rename_bundle($bundle_old, $bundle_new) { - $instances = field_info_instances($bundle_old); +function field_sql_storage_field_attach_rename_bundle($bundle_old, $bundle_new) { + $instances = field_info_instances($bundle_new); foreach ($instances as $instance) { $field = field_read_field($instance['field_name']); $table_name = _field_sql_storage_tablename($field);