diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/field/field.api.php | 87 | ||||
-rw-r--r-- | modules/field/field.attach.inc | 105 | ||||
-rw-r--r-- | modules/field/field.crud.inc | 6 | ||||
-rw-r--r-- | modules/field/modules/field_sql_storage/field_sql_storage.module | 65 |
4 files changed, 191 insertions, 72 deletions
diff --git a/modules/field/field.api.php b/modules/field/field.api.php index ef08b0b75..143366908 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -440,8 +440,40 @@ function hook_field_attach_form($obj_type, $object, &$form, &$form_state) { } /** + * Act on field_attach_pre_load. + * + * This hook allows modules to load data before the Field Storage API, + * optionally preventing the field storage module from doing so. + * + * This lets 3rd party modules override, mirror, shard, or otherwise store a + * subset of fields in a different way than the current storage engine. + * Possible use cases include : per-bundle storage, per-combo-field storage... + * + * @param $obj_type + * The type of objects for which to load fields; e.g. 'node' or 'user'. + * @param $objects + * An array of objects for which to load fields. The keys for primary id and + * bundle name to load are identified by hook_fieldable_info for $obj_type. + * @param $age + * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or + * FIELD_LOAD_REVISION to load the version indicated by each object. + * @param $additions + * An array of field data for the objects being loaded, keyed by entity id, + * field name, and item delta number. + * @param $skip_fields + * An array keyed by names of fields whose data has already been loaded and + * therefore should not be loaded again. The values associated to these keys + * are not specified. + * @return + * Loaded field values are added to $additions and loaded field names are set + * as keys in $skip_fields. + */ +function hook_field_attach_pre_load($obj_type, $objects, $age, &$additions, &$skip_fields) { +} + +/** * Act on field_attach_load. - * + * * This hook is invoked after the field module has performed the operation. * * See field_attach_load() for details and arguments. @@ -482,27 +514,47 @@ function hook_field_attach_presave($obj_type, $object) { /** * Act on field_attach_insert. - * - * This hook is invoked after the field module has performed the operation. * - * See field_attach_insert() for details and arguments. + * This hook allows modules to store data before the Field Storage + * API, optionally preventing the field storage module from doing so. + * + * @param $obj_type + * The type of $object; e.g. 'node' or 'user'. + * @param $object + * The object with fields to save. + * @param $skip_fields + * An array keyed by names of fields whose data has already been written and + * therefore should not be written again. The values associated to these keys + * are not specified. + * @return + * Saved field names are set set as keys in $skip_fields. */ -function hook_field_attach_insert($obj_type, $object) { +function hook_field_attach_pre_insert($obj_type, $object, &$skip_fields) { } /** * Act on field_attach_update. - * - * This hook is invoked after the field module has performed the operation. * - * See field_attach_update() for details and arguments. + * This hook allows modules to store data before the Field Storage + * API, optionally preventing the field storage module from doing so. + * + * @param $obj_type + * The type of $object; e.g. 'node' or 'user'. + * @param $object + * The object with fields to save. + * @param $skip_fields + * An array keyed by names of fields whose data has already been written and + * therefore should not be written again. The values associated to these keys + * are not specified. + * @return + * Saved field names are set set as keys in $skip_fields. */ -function hook_field_attach_update($obj_type, $object) { +function hook_field_attach_pre_update($obj_type, $object, &$skip_fields) { } /** * Act on field_attach_delete. - * + * * This hook is invoked after the field module has performed the operation. * * See field_attach_delete() for details and arguments. @@ -592,11 +644,15 @@ function hook_field_attach_delete_bundle($bundle) { * FIELD_LOAD_CURRENT to load the most recent revision for all * fields, or FIELD_LOAD_REVISION to load the version indicated by * each object. + * @param $skip_fields + * An array keyed by names of fields whose data has already been loaded and + * therefore should not be loaded again. The values associated to these keys + * are not specified. * @return * An array of field data for the objects, keyed by entity id, field * name, and item delta number. */ -function hook_field_storage_load($obj_type, $queried_objs, $age) { +function hook_field_storage_load($obj_type, $queried_objs, $age, $skip_fields) { } /** @@ -609,8 +665,12 @@ function hook_field_storage_load($obj_type, $queried_objs, $age) { * @param $op * FIELD_STORAGE_UPDATE when updating an existing object, * FIELD_STORAGE_INSERT when inserting a new object. + * @param $skip_fields + * An array keyed by names of fields whose data has already been written and + * therefore should not be written again. The values associated to these keys + * are not specified. */ -function hook_field_storage_write($obj_type, $object, $op) { +function hook_field_storage_write($obj_type, $object, $op, $skip_fields) { } /** @@ -627,6 +687,9 @@ function hook_field_storage_delete($obj_type, $object) { /** * Delete a single revision of field data for an object. * + * Deleting the current (most recently written) revision is not + * allowed as has undefined results. + * * @param $obj_type * The entity type of object, such as 'node' or 'user'. * @param $object diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index d52f9db2a..2b05937f4 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -94,16 +94,33 @@ define('FIELD_STORAGE_INSERT', 'insert'); * * Fieldable types call Field Attach API functions during their own * API calls; for example, node_load() calls field_attach_load(). A - * fieldable type may is not required to use all of the Field Attach + * fieldable type is not required to use all of the Field Attach * API functions. * * Most Field Attach API functions define a corresponding hook * function that allows any module to act on Field Attach operations - * for any object, and access or modify all the field, form, or - * display data for that object and operation. These all-module hooks - * are distinct from those of the Field Types API, such as - * hook_field_load(), that are only invoked for the module that - * defines a specific field type. + * for any object after the operation is complete, and access or + * modify all the field, form, or display data for that object and + * operation. For example, field_attach_view() invokes + * hook_field_attach_view(). These all-module hooks are distinct from + * those of the Field Types API, such as hook_field_load(), that are + * only invoked for the module that defines a specific field type. + * + * field_attach_load(), field_attach_insert(), and + * field_attach_update() also define pre-operation hooks, + * e.g. hook_field_attach_pre_load(). These hooks run before the + * corresponding Field Storage API and Field Type API operations. + * They allow modules to define additional storage locations + * (e.g. denormalizing, mirroring) for field data on a per-field + * basis. They also allow modules to take over field storage + * completely by instructing other implementations of the same hook + * and the Field Storage API itself not to operate on specified + * fields. + * + * The pre-operation hooks do not make the Field Storage API + * irrelevant. The Field Storage API is essentially the "fallback + * mechanism" for any fields that aren't being intercepted explicitly + * by pre-operation hooks. */ /** @@ -241,7 +258,7 @@ function _field_attach_form($obj_type, $object, &$form, $form_state) { function _field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) { $queried_objects = array(); - // Fetch avaliable nodes from cache. + // Fetch avaliable objects from cache. foreach ($objects as $object) { list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object); $cid = "field:$obj_type:$id:$vid"; @@ -254,17 +271,45 @@ function _field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) { $queried_objects[$id] = $objects[$id]; } } - // Fetch other nodes from the database. + + // Fetch other objects from the database. if ($queried_objects) { - // We need the raw additions to be able to cache them, so - // content_storage_load() and hook_field_load() must not alter - // nodes directly but return their additions. - $additions = module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age); + // The loading order is: + // - hook_field_attach_pre_load() + // - storage engine's hook_field_storage_load() + // - field-type modules hook_field_load() + // - hook_field_attach_load() + // We need the raw additions to be able to cache them, so the hooks must + // not alter objects directly but return their additions. At each step, + // results are merged into the $queried_objects, and into the $additions + // array, that will eventually get cached. + + // Invoke hook_field_attach_pre_load(): let any module load field + // data before the storage engine, accumulating along the way. + $additions_pre_load = array(); + $skip_fields = array(); + foreach (module_implements('field_attach_pre_load') as $module) { + $function = $module . '_field_attach_pre_load'; + $function($obj_type, $queried_objects, $age, $additions_pre_load, $skip_fields); + } + + // Invoke the storage engine's hook_field_storage_load(): the field storage + // engine loads the rest. + $additions = module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age, $skip_fields); + + // First, merge the additions from the storage engine. foreach ($additions as $id => $obj_additions) { foreach ($obj_additions as $key => $value) { $queried_objects[$id]->$key = $value; } } + // Then, merge the pre_load additions, so that they take precedence. + foreach ($additions_pre_load as $id => $obj_additions) { + foreach ($obj_additions as $key => $value) { + $queried_objects[$id]->$key = $value; + $additions[$id][$key] = $value; + } + } // TODO D7 : to be consistent we might want to make hook_field_load() accept // multiple objects too. Which forbids going through _field_invoke(), but @@ -281,13 +326,15 @@ function _field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) { } } + // Invoke field-type modules hook_field_load(). $custom_additions = _field_invoke('load', $obj_type, $object); foreach ($custom_additions as $key => $value) { $queried_objects[$id]->$key = $value; $additions[$id][$key] = $value; } - // Let other modules act on loading the object. + // Invoke hook_field_attach_load(): let other modules act on loading the + // object. // TODO : this currently doesn't get cached (we cache $additions). // This should either be called after we fetch from cache, or return an // array of additions. @@ -464,14 +511,18 @@ function _field_attach_presave($obj_type, &$object) { */ function _field_attach_insert($obj_type, &$object) { - // Let other modules act on inserting the object. - foreach (module_implements('field_attach_insert') as $module) { - $function = $module . '_field_attach_insert'; - $function($obj_type, $object); + _field_invoke('insert', $obj_type, $object); + + // Let other modules act on inserting the object, accumulating saved + // fields along the way. + $saved = array(); + foreach (module_implements('field_attach_pre_insert') as $module) { + $function = $module . '_field_attach_pre_insert'; + $function($obj_type, $object, $saved); } - _field_invoke('insert', $obj_type, $object); - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_INSERT); + // 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, $saved); list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object); if ($cacheable) { @@ -489,14 +540,18 @@ function _field_attach_insert($obj_type, &$object) { */ function _field_attach_update($obj_type, &$object) { - // Let other modules act on updating the object. - foreach (module_implements('field_attach_update') as $module) { - $function = $module . '_field_attach_update'; - $function($output, $obj_type, $object); + _field_invoke('update', $obj_type, $object); + + // Let other modules act on updating the object, accumulating saved + // fields along the way. + $saved = array(); + foreach (module_implements('field_attach_pre_update') as $module) { + $function = $module . '_field_attach_pre_update'; + $function($obj_type, $object, $saved); } - _field_invoke('update', $obj_type, $object); - module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_UPDATE); + // 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, $saved); list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object); if ($cacheable) { diff --git a/modules/field/field.crud.inc b/modules/field/field.crud.inc index 93f24bf56..b96dbfb4b 100644 --- a/modules/field/field.crud.inc +++ b/modules/field/field.crud.inc @@ -346,10 +346,12 @@ function field_create_instance($instance) { _field_write_instance($instance); - module_invoke_all('field_create_instance', $instance); - // Clear caches field_cache_clear(); + + // Invoke external hooks after the cache is cleared for API consistency. + module_invoke_all('field_create_instance', $instance); + return FALSE; } diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.module b/modules/field/modules/field_sql_storage/field_sql_storage.module index 774d275bd..4d17110d0 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.module +++ b/modules/field/modules/field_sql_storage/field_sql_storage.module @@ -152,6 +152,9 @@ function _field_sql_storage_schema($field) { ); } +/** + * Implementation of hook_field_storage_create_field(). + */ function field_sql_storage_field_storage_create_field($field) { $schema = _field_sql_storage_schema($field); foreach ($schema as $name => $table) { @@ -159,6 +162,9 @@ function field_sql_storage_field_storage_create_field($field) { } } +/** + * Implementation of hook_field_storage_delete_field(). + */ function field_sql_storage_field_storage_delete_field($field_name) { // Mark all data associated with the field for deletion. $table = _field_sql_storage_tablename($field_name); @@ -168,22 +174,9 @@ function field_sql_storage_field_storage_delete_field($field_name) { } /** - * Load field data for a set of objects from the database. - * - * @param $obj_type - * The entity type of objects being loaded, such as 'node' or - * 'user'. - * @param $objects - * The array of objects for which to load data. - * @param $age - * FIELD_LOAD_CURRENT to load the most recent revision for all - * fields, or FIELD_LOAD_REVISION to load the version indicated by - * each object. - * @return - * An array of field data for the objects, keyed by entity id, field - * name, and item delta number. + * Implementation of hook_field_storage_load(). */ -function field_sql_storage_field_storage_load($obj_type, $objects, $age) { +function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_fields = array()) { $etid = _field_sql_storage_etid($obj_type); $load_current = $age == FIELD_LOAD_CURRENT; @@ -200,6 +193,10 @@ function field_sql_storage_field_storage_load($obj_type, $objects, $age) { $additions = array(); foreach ($field_ids as $field_name => $ids) { + if (isset($skip_fields[$field_name])) { + continue; + } + $field = field_info_field($field_name); $table = $load_current ? _field_sql_storage_tablename($field_name) : _field_sql_storage_revision_tablename($field_name); @@ -229,13 +226,20 @@ function field_sql_storage_field_storage_load($obj_type, $objects, $age) { return $additions; } -function field_sql_storage_field_storage_write($obj_type, $object, $op) { +/** + * Implementation of hook_field_storage_write(). + */ +function field_sql_storage_field_storage_write($obj_type, $object, $op, $skip_fields) { list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object); $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; + } + $table_name = _field_sql_storage_tablename($field_name); $revision_name = _field_sql_storage_revision_tablename($field_name); $field = field_read_field($field_name); @@ -298,14 +302,9 @@ function field_sql_storage_field_storage_write($obj_type, $object, $op) { } /** - * Delete all field data for a single object. This function actually - * deletes the data from the database. + * Implementation of hook_field_storage_delete(). * - * @param $obj_type - * The entity type of the object being deleted, such as 'node' or - * 'user'. - * @param $object - * The object for which to delete field data. + * This function actually deletes the data from the database. */ function field_sql_storage_field_storage_delete($obj_type, $object) { list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object); @@ -328,16 +327,9 @@ function field_sql_storage_field_storage_delete($obj_type, $object) { } /** - * Delete field data for a single revision of a single object. - * Deleting the current (most recently written) revision is not - * allowed as has undefined results. This function actually deletes - * the data from the database. + * Implementation of hook_field_storage_delete_revision(). * - * @param $obj_type - * The entity type of the object being deleted, such as 'node' or - * 'user'. - * @param $object - * The object for which to delete field data. + * 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); @@ -357,8 +349,12 @@ function field_sql_storage_field_storage_delete_revision($obj_type, $object) { } } +/** + * Implementation of hook_field_storage_delete_instance(). + * + * This function simply marks for deletion all data associated with the field. + */ function field_sql_storage_field_storage_delete_instance($field_name, $bundle) { - // Mark all data associated with the field for deletion. $table_name = _field_sql_storage_tablename($field_name); $revision_name = _field_sql_storage_revision_tablename($field_name); db_update($table_name) @@ -371,6 +367,9 @@ function field_sql_storage_field_storage_delete_instance($field_name, $bundle) { ->execute(); } +/** + * Implementation of hook_field_storage_rename_bundle(). + */ function field_sql_storage_field_storage_rename_bundle($bundle_old, $bundle_new) { $instances = field_info_instances($bundle_old); foreach ($instances as $instance) { |