| 1 |
<?php
|
| 2 |
// $Id: field.crud.inc,v 1.40 2009/10/22 00:49:12 dries Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* Field CRUD API, handling field and field instance creation and deletion.
|
| 7 |
*/
|
| 8 |
|
| 9 |
/**
|
| 10 |
* @defgroup field_structs Field API data structures
|
| 11 |
* @{
|
| 12 |
* Represent Field API fields and instances.
|
| 13 |
*
|
| 14 |
* The Field API defines two primary data structures, Field and
|
| 15 |
* Instance, and the concept of a Bundle. A Field defines a
|
| 16 |
* particular type of data that can be attached to objects. A Field
|
| 17 |
* Instance is a Field attached to a single Bundle. A Bundle is a set
|
| 18 |
* of fields that are treated as a group by the Field Attach API and
|
| 19 |
* is related to a single fieldable entity type.
|
| 20 |
*
|
| 21 |
* For example, suppose a site administrator wants Article nodes to
|
| 22 |
* have a subtitle and photo. Using the Field API or Field UI module,
|
| 23 |
* the administrator creates a field named 'subtitle' of type 'text'
|
| 24 |
* and a field named 'photo' of type 'image'. The administrator
|
| 25 |
* (again, via a UI) creates two Field Instances, one attaching the
|
| 26 |
* field 'subtitle' to the 'node' bundle 'article' and one attaching
|
| 27 |
* the field 'photo' to the 'node' bundle 'article'. When the node
|
| 28 |
* system uses the Field Attach API to load all fields for an Article
|
| 29 |
* node, it passes the node's entity type (which is 'node') and
|
| 30 |
* content type (which is 'article') as the node's bundle.
|
| 31 |
* field_attach_load() then loads the 'subtitle' and 'photo' fields
|
| 32 |
* because they are both attached to the 'node' bundle 'article'.
|
| 33 |
*
|
| 34 |
* Field objects are (currently) represented as an array of key/value
|
| 35 |
* pairs. The object properties are:
|
| 36 |
*
|
| 37 |
* @param array $field:
|
| 38 |
* - id (integer, read-only)
|
| 39 |
* The primary identifier of the field. It is assigned automatically
|
| 40 |
* by field_create_field().
|
| 41 |
* - field_name (string)
|
| 42 |
* The name of the field. Each field name is unique within Field API.
|
| 43 |
* When a field is attached to an object, the field's data is stored
|
| 44 |
* in $object->$field_name.
|
| 45 |
* - type (string)
|
| 46 |
* The type of the field, such as 'text' or 'image'. Field types
|
| 47 |
* are defined by modules that implement hook_field_info().
|
| 48 |
* - cardinality (integer)
|
| 49 |
* The number of values the field can hold. Legal values are any
|
| 50 |
* positive integer or FIELD_CARDINALITY_UNLIMITED.
|
| 51 |
* - translatable (integer)
|
| 52 |
* Whether the field is translatable.
|
| 53 |
* - locked (integer)
|
| 54 |
* TODO: undefined.
|
| 55 |
* - module (string, read-only)
|
| 56 |
* The name of the module that implements the field type.
|
| 57 |
* - active (integer, read-only)
|
| 58 |
* TRUE if the module that implements the field type is currently
|
| 59 |
* enabled, FALSE otherwise.
|
| 60 |
* - deleted (integer, read-only)
|
| 61 |
* TRUE if this field has been deleted, FALSE otherwise. Deleted
|
| 62 |
* fields are ignored by the Field Attach API. This property exists
|
| 63 |
* because fields can be marked for deletion but only actually
|
| 64 |
* destroyed by a separate garbage-collection process.
|
| 65 |
* - columns (array, read-only).
|
| 66 |
* An array of the Field API columns used to store each value of
|
| 67 |
* this field. The column list may depend on field settings; it is
|
| 68 |
* not constant per field type. Field API column specifications are
|
| 69 |
* exactly like Schema API column specifications but, depending on
|
| 70 |
* the field storage module in use, the name of the column may not
|
| 71 |
* represent an actual column in an SQL database.
|
| 72 |
* - indexes (array).
|
| 73 |
* An array of indexes on data columns, using the same definition format
|
| 74 |
* as Schema API index specifications. Only columns that appear in the
|
| 75 |
* 'columns' setting are allowed. Note that field types can specify
|
| 76 |
* default indexes, which can be modified or added to when
|
| 77 |
* creating a field.
|
| 78 |
* - settings (array)
|
| 79 |
* A sub-array of key/value pairs of field-type-specific settings. Each
|
| 80 |
* field type module defines and documents its own field settings.
|
| 81 |
* - storage (array)
|
| 82 |
* A sub-array of key/value pairs identifying the storage backend to use for
|
| 83 |
* the for the field.
|
| 84 |
* - type (string)
|
| 85 |
* The storage backend used by the field. Storage backends are defined
|
| 86 |
* by modules that implement hook_field_storage_info().
|
| 87 |
* - module (string, read-only)
|
| 88 |
* The name of the module that implements the storage backend.
|
| 89 |
* - active (integer, read-only)
|
| 90 |
* TRUE if the module that implements the storage backend is currently
|
| 91 |
* enabled, FALSE otherwise.
|
| 92 |
* - settings (array)
|
| 93 |
* A sub-array of key/value pairs of settings. Each storage backend
|
| 94 |
* defines and documents its own settings.
|
| 95 |
*
|
| 96 |
* Field Instance objects are (currently) represented as an array of
|
| 97 |
* key/value pairs. The object properties are:
|
| 98 |
*
|
| 99 |
* @param array $instance:
|
| 100 |
* - id (integer, read-only)
|
| 101 |
* The primary identifier of this field instance. It is assigned
|
| 102 |
* automatically by field_create_instance().
|
| 103 |
* - field_id (integer, read-only)
|
| 104 |
* The foreign key of the field attached to the bundle by this instance.
|
| 105 |
* It is populated automatically by field_create_instance().
|
| 106 |
* - field_name (string)
|
| 107 |
* The name of the field attached to the bundle by this instance.
|
| 108 |
* - object_type (string)
|
| 109 |
* The name of the object type the instance is attached to.
|
| 110 |
* - bundle (string)
|
| 111 |
* The name of the bundle that the field is attached to.
|
| 112 |
* - label (string)
|
| 113 |
* A human-readable label for the field when used with this
|
| 114 |
* bundle. For example, the label will be the title of Form API
|
| 115 |
* elements for this instance.
|
| 116 |
* - description (string)
|
| 117 |
* A human-readable description for the field when used with this
|
| 118 |
* bundle. For example, the description will be the help text of
|
| 119 |
* Form API elements for this instance.
|
| 120 |
* - required (integer)
|
| 121 |
* TRUE if a value for this field is required when used with this
|
| 122 |
* bundle, FALSE otherwise. Currently, required-ness is only enforced
|
| 123 |
* during Form API operations, not by field_attach_load(),
|
| 124 |
* field_attach_insert(), or field_attach_update().
|
| 125 |
* - default_value_function (string)
|
| 126 |
* The name of the function, if any, that will provide a default value.
|
| 127 |
* - deleted (integer, read-only)
|
| 128 |
* TRUE if this instance has been deleted, FALSE otherwise.
|
| 129 |
* Deleted instances are ignored by the Field Attach API.
|
| 130 |
* This property exists because instances can be marked for deletion but
|
| 131 |
* only actually destroyed by a separate garbage-collection process.
|
| 132 |
* - settings (array)
|
| 133 |
* A sub-array of key/value pairs of field-type-specific instance
|
| 134 |
* settings. Each field type module defines and documents its own
|
| 135 |
* instance settings.
|
| 136 |
* - widget (array)
|
| 137 |
* A sub-array of key/value pairs identifying the Form API input widget
|
| 138 |
* for the field when used by this bundle.
|
| 139 |
* - type (string)
|
| 140 |
* The type of the widget, such as text_textfield. Widget types
|
| 141 |
* are defined by modules that implement hook_field_widget_info().
|
| 142 |
* - settings (array)
|
| 143 |
* A sub-array of key/value pairs of widget-type-specific settings.
|
| 144 |
* Each field widget type module defines and documents its own
|
| 145 |
* widget settings.
|
| 146 |
* - weight (float)
|
| 147 |
* The weight of the widget relative to the other elements in object
|
| 148 |
* edit forms.
|
| 149 |
* - module (string, read-only)
|
| 150 |
* The name of the module that implements the widget type.
|
| 151 |
* - display (array)
|
| 152 |
* A sub-array of key/value pairs identifying build modes and the way the
|
| 153 |
* field values should be displayed in each build mode.
|
| 154 |
* - full (array)
|
| 155 |
* A sub-array of key/value pairs of the display options to be used
|
| 156 |
* when the field is being displayed in the "full" build mode.
|
| 157 |
* - label (string)
|
| 158 |
* Position of the label. 'inline', 'above' and 'hidden' are the
|
| 159 |
* values recognized by the default 'field' theme implementation.
|
| 160 |
* - type (string)
|
| 161 |
* The type of the display formatter, or 'hidden' for no display.
|
| 162 |
* - settings (array)
|
| 163 |
* A sub-array of key/value pairs of display options specific to
|
| 164 |
* the formatter.
|
| 165 |
* - weight (float)
|
| 166 |
* The weight of the field relative to the other object components
|
| 167 |
* displayed in this build mode.
|
| 168 |
* - module (string, read-only)
|
| 169 |
* The name of the module which implements the display formatter.
|
| 170 |
* - teaser
|
| 171 |
* - ...
|
| 172 |
* - other_build_mode
|
| 173 |
* - ...
|
| 174 |
*
|
| 175 |
* Bundles are represented by two strings, an entity type and a bundle name.
|
| 176 |
*
|
| 177 |
* TODO D7 : document max length for field types, widget types,
|
| 178 |
* formatter names...
|
| 179 |
*/
|
| 180 |
/**
|
| 181 |
* @} End of "defgroup field_structs".
|
| 182 |
*/
|
| 183 |
|
| 184 |
/**
|
| 185 |
* @defgroup field_crud Field CRUD API
|
| 186 |
* @{
|
| 187 |
* Create, update, and delete Field API fields, bundles, and instances.
|
| 188 |
*
|
| 189 |
* Modules use this API, often in hook_install(), to create custom
|
| 190 |
* data structures. UI modules will use it to create a user interface.
|
| 191 |
*
|
| 192 |
* The Field CRUD API uses
|
| 193 |
* @link field_structs Field API data structures @endlink.
|
| 194 |
*/
|
| 195 |
|
| 196 |
/**
|
| 197 |
* Create a field.
|
| 198 |
*
|
| 199 |
* This function does not bind the field to any bundle; use
|
| 200 |
* field_create_instance() for that.
|
| 201 |
*
|
| 202 |
* @param $field
|
| 203 |
* A field structure. The field_name and type properties are required.
|
| 204 |
* Other properties, if omitted, will be given the following default values:
|
| 205 |
* - cardinality: 1
|
| 206 |
* - locked: FALSE
|
| 207 |
* - indexes: the field-type indexes, specified by the field type's
|
| 208 |
* hook_field_schema(). The indexes specified in $field are added
|
| 209 |
* to those default indexes. It is possible to override the
|
| 210 |
* definition of a field-type index by providing an index with the
|
| 211 |
* same name, or to remove it by redefining it as an empty array
|
| 212 |
* of columns. Overriding field-type indexes should be done
|
| 213 |
* carefully, for it might seriously affect the site's performance.
|
| 214 |
* - settings: each omitted setting is given the default value defined in
|
| 215 |
* hook_field_info().
|
| 216 |
* - storage:
|
| 217 |
* - type: the storage backend specified in the 'field_default_storage'
|
| 218 |
* system variable.
|
| 219 |
* - settings: each omitted setting is given the default value specified in
|
| 220 |
* hook_field_storage_info().
|
| 221 |
* @return
|
| 222 |
* The $field structure with the id property filled in.
|
| 223 |
* @throw
|
| 224 |
* FieldException
|
| 225 |
*/
|
| 226 |
function field_create_field($field) {
|
| 227 |
// Field name is required.
|
| 228 |
if (empty($field['field_name'])) {
|
| 229 |
throw new FieldException('Attempt to create an unnamed field.');
|
| 230 |
}
|
| 231 |
// Field type is required.
|
| 232 |
if (empty($field['type'])) {
|
| 233 |
throw new FieldException('Attempt to create a field with no type.');
|
| 234 |
}
|
| 235 |
// Field name cannot contain invalid characters.
|
| 236 |
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $field['field_name'])) {
|
| 237 |
throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
|
| 238 |
}
|
| 239 |
|
| 240 |
// Field name cannot be longer than 32 characters. We use drupal_strlen()
|
| 241 |
// because the DB layer assumes that column widths are given in characters,
|
| 242 |
// not bytes.
|
| 243 |
if (drupal_strlen($field['field_name']) > 32) {
|
| 244 |
throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
|
| 245 |
array('%name' => $field['field_name'])));
|
| 246 |
}
|
| 247 |
|
| 248 |
// Ensure the field name is unique over active and disabled fields.
|
| 249 |
// We do not care about deleted fields.
|
| 250 |
$prior_field = field_read_field($field['field_name'], array('include_inactive' => TRUE));
|
| 251 |
if (!empty($prior_field)) {
|
| 252 |
$message = $prior_field['active']?
|
| 253 |
t('Attempt to create field name %name which already exists and is active.', array('%name' => $field['field_name'])):
|
| 254 |
t('Attempt to create field name %name which already exists, although it is inactive.', array('%name' => $field['field_name']));
|
| 255 |
throw new FieldException($message);
|
| 256 |
}
|
| 257 |
|
| 258 |
// Disallow reserved field names. This can't prevent all field name
|
| 259 |
// collisions with existing object properties, but some is better
|
| 260 |
// than none.
|
| 261 |
foreach (entity_get_info() as $type => $info) {
|
| 262 |
if (in_array($field['field_name'], $info['object keys'])) {
|
| 263 |
throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
|
| 264 |
}
|
| 265 |
}
|
| 266 |
|
| 267 |
$field += array(
|
| 268 |
'cardinality' => 1,
|
| 269 |
'translatable' => FALSE,
|
| 270 |
'locked' => FALSE,
|
| 271 |
'settings' => array(),
|
| 272 |
'storage' => array(),
|
| 273 |
'deleted' => 0,
|
| 274 |
);
|
| 275 |
|
| 276 |
// Check that the field type is known.
|
| 277 |
$field_type = field_info_field_types($field['type']);
|
| 278 |
if (!$field_type) {
|
| 279 |
throw new FieldException(t('Attempt to create a field of unknown type %type.', array('%type' => $field['type'])));
|
| 280 |
}
|
| 281 |
// Create all per-field-type properties (needed here as long as we have
|
| 282 |
// settings that impact column definitions).
|
| 283 |
$field['settings'] += field_info_field_settings($field['type']);
|
| 284 |
$field['module'] = $field_type['module'];
|
| 285 |
$field['active'] = 1;
|
| 286 |
|
| 287 |
// Provide default storage.
|
| 288 |
$field['storage'] += array(
|
| 289 |
'type' => variable_get('field_storage_default', 'field_sql_storage'),
|
| 290 |
'settings' => array(),
|
| 291 |
);
|
| 292 |
// Check that the storage type is known.
|
| 293 |
$storage_type = field_info_storage_types($field['storage']['type']);
|
| 294 |
if (!$storage_type) {
|
| 295 |
throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type'])));
|
| 296 |
}
|
| 297 |
// Provide default storage settings.
|
| 298 |
$field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
|
| 299 |
$field['storage']['module'] = $storage_type['module'];
|
| 300 |
$field['storage']['active'] = 1;
|
| 301 |
// Collect storage information.
|
| 302 |
$schema = (array) module_invoke($field['module'], 'field_schema', $field);
|
| 303 |
$schema += array('columns' => array(), 'indexes' => array());
|
| 304 |
// 'columns' are hardcoded in the field type.
|
| 305 |
$field['columns'] = $schema['columns'];
|
| 306 |
// 'indexes' can be both hardcoded in the field type, and specified in the
|
| 307 |
// incoming $field definition.
|
| 308 |
$field += array(
|
| 309 |
'indexes' => array(),
|
| 310 |
);
|
| 311 |
$field['indexes'] += $schema['indexes'];
|
| 312 |
|
| 313 |
// The serialized 'data' column contains everything from $field that does not
|
| 314 |
// have its own column and is not automatically populated when the field is
|
| 315 |
// read.
|
| 316 |
$data = $field;
|
| 317 |
unset($data['columns'], $data['field_name'], $data['type'], $data['active'], $data['module'], $data['storage_type'], $data['storage_active'], $data['storage_module'], $data['locked'], $data['cardinality'], $data['deleted']);
|
| 318 |
|
| 319 |
$record = array(
|
| 320 |
'field_name' => $field['field_name'],
|
| 321 |
'type' => $field['type'],
|
| 322 |
'module' => $field['module'],
|
| 323 |
'active' => $field['active'],
|
| 324 |
'storage_type' => $field['storage']['type'],
|
| 325 |
'storage_module' => $field['storage']['module'],
|
| 326 |
'storage_active' => $field['storage']['active'],
|
| 327 |
'locked' => $field['locked'],
|
| 328 |
'data' => $data,
|
| 329 |
'cardinality' => $field['cardinality'],
|
| 330 |
'translatable' => $field['translatable'],
|
| 331 |
'deleted' => $field['deleted'],
|
| 332 |
);
|
| 333 |
|
| 334 |
// Store the field and get the id back.
|
| 335 |
drupal_write_record('field_config', $record);
|
| 336 |
$field['id'] = $record['id'];
|
| 337 |
|
| 338 |
// Invoke hook_field_storage_create_field after the field is
|
| 339 |
// complete (e.g. it has its id).
|
| 340 |
try {
|
| 341 |
// Invoke hook_field_storage_create_field after
|
| 342 |
// drupal_write_record() sets the field id.
|
| 343 |
module_invoke($storage_type['module'], 'field_storage_create_field', $field);
|
| 344 |
}
|
| 345 |
catch (Exception $e) {
|
| 346 |
// If storage creation failed, remove the field_config record before
|
| 347 |
// rethrowing the exception.
|
| 348 |
db_delete('field_config')
|
| 349 |
->condition('id', $field['id'])
|
| 350 |
->execute();
|
| 351 |
throw $e;
|
| 352 |
}
|
| 353 |
|
| 354 |
// Clear caches
|
| 355 |
field_cache_clear(TRUE);
|
| 356 |
|
| 357 |
// Invoke external hooks after the cache is cleared for API consistency.
|
| 358 |
module_invoke_all('field_create_field', $field);
|
| 359 |
|
| 360 |
return $field;
|
| 361 |
}
|
| 362 |
|
| 363 |
/*
|
| 364 |
* Update a field.
|
| 365 |
*
|
| 366 |
* Any module may forbid any update for any reason. For example, the
|
| 367 |
* field's storage module might forbid an update if it would change
|
| 368 |
* the storage schema while data for the field exists. A field type
|
| 369 |
* module might forbid an update if it would change existing data's
|
| 370 |
* semantics, or if there are external dependencies on field settings
|
| 371 |
* that cannot be updated.
|
| 372 |
*
|
| 373 |
* @param $field
|
| 374 |
* A field structure. $field['field_name'] must provided; it
|
| 375 |
* identifies the field that will be updated to match this
|
| 376 |
* structure. Any other properties of the field that are not
|
| 377 |
* specified in $field will be left unchanged, so it is not
|
| 378 |
* necessary to pass in a fully populated $field structure.
|
| 379 |
* @return
|
| 380 |
* Throws a FieldException if the update cannot be performed.
|
| 381 |
* @see field_create_field()
|
| 382 |
*/
|
| 383 |
function field_update_field($field) {
|
| 384 |
// Check that the specified field exists.
|
| 385 |
$prior_field = field_read_field($field['field_name']);
|
| 386 |
if (empty($prior_field)) {
|
| 387 |
throw new FieldException('Attempt to update a non-existent field.');
|
| 388 |
}
|
| 389 |
|
| 390 |
// Use the prior field values for anything not specifically set by the new
|
| 391 |
// field to be sure that all values are set.
|
| 392 |
$field += $prior_field;
|
| 393 |
$field['settings'] += $prior_field['settings'];
|
| 394 |
|
| 395 |
// Some updates are always disallowed.
|
| 396 |
if ($field['type'] != $prior_field['type']) {
|
| 397 |
throw new FieldException("Cannot change an existing field's type.");
|
| 398 |
}
|
| 399 |
if ($field['storage']['type'] != $prior_field['storage']['type']) {
|
| 400 |
throw new FieldException("Cannot change an existing field's storage type.");
|
| 401 |
}
|
| 402 |
|
| 403 |
// Collect the new storage information, since what is in
|
| 404 |
// $prior_field may no longer be right.
|
| 405 |
$schema = (array) module_invoke($field['module'], 'field_schema', $field);
|
| 406 |
$schema += array('columns' => array(), 'indexes' => array());
|
| 407 |
// 'columns' are hardcoded in the field type.
|
| 408 |
$field['columns'] = $schema['columns'];
|
| 409 |
// 'indexes' can be both hardcoded in the field type, and specified in the
|
| 410 |
// incoming $field definition.
|
| 411 |
$field += array(
|
| 412 |
'indexes' => array(),
|
| 413 |
);
|
| 414 |
$field['indexes'] += $schema['indexes'];
|
| 415 |
|
| 416 |
$has_data = field_has_data($field);
|
| 417 |
|
| 418 |
// See if any module forbids the update by throwing an exception.
|
| 419 |
foreach (module_implements('field_update_forbid') as $module) {
|
| 420 |
$function = $module . '_field_update_forbid';
|
| 421 |
$function($field, $prior_field, $has_data);
|
| 422 |
}
|
| 423 |
|
| 424 |
// Tell the storage engine to update the field. Do this before
|
| 425 |
// saving the new definition since it still might fail.
|
| 426 |
module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_update_field', $field, $prior_field, $has_data);
|
| 427 |
|
| 428 |
// Save the new field definition. @todo: refactor with
|
| 429 |
// field_create_field.
|
| 430 |
|
| 431 |
// The serialized 'data' column contains everything from $field that does not
|
| 432 |
// have its own column and is not automatically populated when the field is
|
| 433 |
// read.
|
| 434 |
$data = $field;
|
| 435 |
unset($data['columns'], $data['field_name'], $data['type'], $data['locked'], $data['module'], $data['cardinality'], $data['active'], $data['deleted']);
|
| 436 |
$field['data'] = $data;
|
| 437 |
|
| 438 |
// Store the field and create the id.
|
| 439 |
$primary_key = array('id');
|
| 440 |
drupal_write_record('field_config', $field, $primary_key);
|
| 441 |
|
| 442 |
// Clear caches
|
| 443 |
field_cache_clear(TRUE);
|
| 444 |
|
| 445 |
// Invoke external hooks after the cache is cleared for API consistency.
|
| 446 |
module_invoke_all('field_update_field', $field, $prior_field, $has_data);
|
| 447 |
}
|
| 448 |
|
| 449 |
/**
|
| 450 |
* Read a single field record directly from the database. Generally,
|
| 451 |
* you should use the field_info_field() instead.
|
| 452 |
*
|
| 453 |
* This function will not return deleted fields. Use
|
| 454 |
* field_read_fields() instead for this purpose.
|
| 455 |
*
|
| 456 |
* @param $field_name
|
| 457 |
* The field name to read.
|
| 458 |
* @param array $include_additional
|
| 459 |
* The default behavior of this function is to not return a field that
|
| 460 |
* is inactive. Setting
|
| 461 |
* $include_additional['include_inactive'] to TRUE will override this
|
| 462 |
* behavior.
|
| 463 |
* @return
|
| 464 |
* A field structure, or FALSE.
|
| 465 |
*/
|
| 466 |
function field_read_field($field_name, $include_additional = array()) {
|
| 467 |
$fields = field_read_fields(array('field_name' => $field_name), $include_additional);
|
| 468 |
return $fields ? current($fields) : FALSE;
|
| 469 |
}
|
| 470 |
|
| 471 |
/**
|
| 472 |
* Read in fields that match an array of conditions.
|
| 473 |
*
|
| 474 |
* @param array $params
|
| 475 |
* An array of conditions to match against.
|
| 476 |
* @param array $include_additional
|
| 477 |
* The default behavior of this function is to not return fields that
|
| 478 |
* are inactive or have been deleted. Setting
|
| 479 |
* $include_additional['include_inactive'] or
|
| 480 |
* $include_additional['include_deleted'] to TRUE will override this
|
| 481 |
* behavior.
|
| 482 |
* @return
|
| 483 |
* An array of fields matching $params. If
|
| 484 |
* $include_additional['include_deletd'] is TRUE, the array is keyed
|
| 485 |
* by field id, otherwise it is keyed by field name.
|
| 486 |
*/
|
| 487 |
function field_read_fields($params = array(), $include_additional = array()) {
|
| 488 |
$query = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC));
|
| 489 |
$query->fields('fc');
|
| 490 |
|
| 491 |
// Turn the conditions into a query.
|
| 492 |
foreach ($params as $key => $value) {
|
| 493 |
$query->condition($key, $value);
|
| 494 |
}
|
| 495 |
if (!isset($include_additional['include_inactive']) || !$include_additional['include_inactive']) {
|
| 496 |
$query
|
| 497 |
->condition('fc.active', 1)
|
| 498 |
->condition('fc.storage_active', 1);
|
| 499 |
}
|
| 500 |
$include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']);
|
| 501 |
if (!$include_deleted) {
|
| 502 |
$query->condition('fc.deleted', 0);
|
| 503 |
}
|
| 504 |
|
| 505 |
$fields = array();
|
| 506 |
$results = $query->execute();
|
| 507 |
foreach ($results as $record) {
|
| 508 |
$field = unserialize($record['data']);
|
| 509 |
$field['id'] = $record['id'];
|
| 510 |
$field['field_name'] = $record['field_name'];
|
| 511 |
$field['type'] = $record['type'];
|
| 512 |
$field['module'] = $record['module'];
|
| 513 |
$field['active'] = $record['active'];
|
| 514 |
$field['storage']['type'] = $record['storage_type'];
|
| 515 |
$field['storage']['module'] = $record['storage_module'];
|
| 516 |
$field['storage']['active'] = $record['storage_active'];
|
| 517 |
$field['locked'] = $record['locked'];
|
| 518 |
$field['cardinality'] = $record['cardinality'];
|
| 519 |
$field['translatable'] = $record['translatable'];
|
| 520 |
$field['deleted'] = $record['deleted'];
|
| 521 |
|
| 522 |
module_invoke_all('field_read_field', $field);
|
| 523 |
|
| 524 |
// Populate storage information.
|
| 525 |
$schema = (array) module_invoke($field['module'], 'field_schema', $field);
|
| 526 |
$schema += array('columns' => array(), 'indexes' => array());
|
| 527 |
$field['columns'] = $schema['columns'];
|
| 528 |
|
| 529 |
$field_name = $field['field_name'];
|
| 530 |
if ($include_deleted) {
|
| 531 |
$field_name = $field['id'];
|
| 532 |
}
|
| 533 |
$fields[$field_name] = $field;
|
| 534 |
}
|
| 535 |
return $fields;
|
| 536 |
}
|
| 537 |
|
| 538 |
/**
|
| 539 |
* Mark a field for deletion, including all its instances and all data
|
| 540 |
* associated with it.
|
| 541 |
*
|
| 542 |
* @param $field_name
|
| 543 |
* The field name to delete.
|
| 544 |
*/
|
| 545 |
function field_delete_field($field_name) {
|
| 546 |
// Delete all non-deleted instances.
|
| 547 |
$field = field_info_field($field_name);
|
| 548 |
if (isset($field['bundles'])) {
|
| 549 |
foreach ($field['bundles'] as $obj_type => $bundles) {
|
| 550 |
foreach ($bundles as $bundle) {
|
| 551 |
$instance = field_info_instance($obj_type, $field_name, $bundle);
|
| 552 |
field_delete_instance($instance);
|
| 553 |
}
|
| 554 |
}
|
| 555 |
}
|
| 556 |
|
| 557 |
// Mark field data for deletion.
|
| 558 |
module_invoke($field['storage']['module'], 'field_storage_delete_field', $field);
|
| 559 |
|
| 560 |
// Mark the field for deletion.
|
| 561 |
db_update('field_config')
|
| 562 |
->fields(array('deleted' => 1))
|
| 563 |
->condition('field_name', $field_name)
|
| 564 |
->execute();
|
| 565 |
|
| 566 |
// Clear the cache.
|
| 567 |
field_cache_clear(TRUE);
|
| 568 |
|
| 569 |
module_invoke_all('field_delete_field', $field);
|
| 570 |
}
|
| 571 |
|
| 572 |
/**
|
| 573 |
* Creates an instance of a field, binding it to a bundle.
|
| 574 |
*
|
| 575 |
* @param $instance
|
| 576 |
* A field instance structure. The field_name, object_type and
|
| 577 |
* bundle properties are required. Other properties, if omitted,
|
| 578 |
* will be given the following default values:
|
| 579 |
* - label: the field name
|
| 580 |
* - description: empty string
|
| 581 |
* - weight: 0
|
| 582 |
* - required: FALSE
|
| 583 |
* - default_value_function: empty string
|
| 584 |
* - settings: each omitted setting is given the default value specified in
|
| 585 |
* hook_field_info().
|
| 586 |
* - widget:
|
| 587 |
* - type: the default widget specified in hook_field_info().
|
| 588 |
* - settings: each omitted setting is given the default value specified in
|
| 589 |
* hook_field_widget_info().
|
| 590 |
* - display:
|
| 591 |
* Settings for the 'full' build mode will be added, and each build mode
|
| 592 |
* will be completed with the follwong default values:
|
| 593 |
* - label: 'above'
|
| 594 |
* - type: the default formatter specified in hook_field_info().
|
| 595 |
* - settings: each omitted setting is given the default value specified in
|
| 596 |
* hook_field_formatter_info().
|
| 597 |
* @return
|
| 598 |
* The $instance structure with the id property filled in.
|
| 599 |
* @throw
|
| 600 |
* FieldException
|
| 601 |
*/
|
| 602 |
function field_create_instance($instance) {
|
| 603 |
$field = field_read_field($instance['field_name']);
|
| 604 |
if (empty($field)) {
|
| 605 |
throw new FieldException("Attempt to create an instance of a field that doesn't exist or is currently inactive.");
|
| 606 |
}
|
| 607 |
// Check that the required properties exists.
|
| 608 |
if (empty($instance['object_type'])) {
|
| 609 |
throw new FieldException(t('Attempt to create an instance of field @field_name without an object type.', array('@field_name' => $instance['field_name'])));
|
| 610 |
}
|
| 611 |
if (empty($instance['bundle'])) {
|
| 612 |
throw new FieldException(t('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $instance['field_name'])));
|
| 613 |
}
|
| 614 |
|
| 615 |
// Set the field id.
|
| 616 |
$instance['field_id'] = $field['id'];
|
| 617 |
|
| 618 |
// Note that we do *not* prevent creating a field on non-existing bundles,
|
| 619 |
// because that would break the 'Body as field' upgrade for contrib
|
| 620 |
// node types.
|
| 621 |
|
| 622 |
// TODO: Check that the widget type is known and can handle the field type ?
|
| 623 |
// TODO: Check that the formatters are known and can handle the field type ?
|
| 624 |
// TODO: Check that the display build modes are known for the object type ?
|
| 625 |
// Those checks should probably happen in _field_write_instance() ?
|
| 626 |
// Problem : this would mean that a UI module cannot update an instance with a disabled formatter.
|
| 627 |
|
| 628 |
// Ensure the field instance is unique within the bundle.
|
| 629 |
// We only check for instances of active fields, since adding an instance of
|
| 630 |
// a disabled field is not supported.
|
| 631 |
$prior_instance = field_read_instance($instance['object_type'], $instance['field_name'], $instance['bundle']);
|
| 632 |
if (!empty($prior_instance)) {
|
| 633 |
$message = t('Attempt to create a field instance that already exists.');
|
| 634 |
throw new FieldException($message);
|
| 635 |
}
|
| 636 |
|
| 637 |
_field_write_instance($instance);
|
| 638 |
|
| 639 |
// Clear caches
|
| 640 |
field_cache_clear();
|
| 641 |
|
| 642 |
// Invoke external hooks after the cache is cleared for API consistency.
|
| 643 |
module_invoke_all('field_create_instance', $instance);
|
| 644 |
|
| 645 |
return $instance;
|
| 646 |
}
|
| 647 |
|
| 648 |
/*
|
| 649 |
* Update an instance of a field.
|
| 650 |
*
|
| 651 |
* @param $instance
|
| 652 |
* An associative array represeting an instance structure. The required
|
| 653 |
* keys and values are:
|
| 654 |
* field_name: The name of an existing field.
|
| 655 |
* bundle: The bundle this field belongs to.
|
| 656 |
* Read-only_id properties are assigned automatically. Any other
|
| 657 |
* properties specified in $instance overwrite the existing values for
|
| 658 |
* the instance.
|
| 659 |
* @throw
|
| 660 |
* FieldException
|
| 661 |
* @see field_create_instance()
|
| 662 |
*/
|
| 663 |
function field_update_instance($instance) {
|
| 664 |
// Check that the specified field exists.
|
| 665 |
$field = field_read_field($instance['field_name']);
|
| 666 |
if (empty($field)) {
|
| 667 |
throw new FieldException("Attempt to update an instance of a nonexistent field.");
|
| 668 |
}
|
| 669 |
|
| 670 |
// Check that the field instance exists (even if it is inactive, since we
|
| 671 |
// want to be able to replace inactive widgets with new ones).
|
| 672 |
$prior_instance = field_read_instance($instance['object_type'], $instance['field_name'], $instance['bundle'], array('include_inactive' => TRUE));
|
| 673 |
if (empty($prior_instance)) {
|
| 674 |
throw new FieldException("Attempt to update a field instance that doesn't exist.");
|
| 675 |
}
|
| 676 |
|
| 677 |
$instance['id'] = $prior_instance['id'];
|
| 678 |
$instance['field_id'] = $prior_instance['field_id'];
|
| 679 |
|
| 680 |
_field_write_instance($instance, TRUE);
|
| 681 |
|
| 682 |
// Clear caches.
|
| 683 |
field_cache_clear();
|
| 684 |
}
|
| 685 |
|
| 686 |
/**
|
| 687 |
* Store an instance record in the field configuration database.
|
| 688 |
*
|
| 689 |
* @param $instance
|
| 690 |
* An instance structure.
|
| 691 |
* @param $update
|
| 692 |
* Whether this is a new or existing instance.
|
| 693 |
*/
|
| 694 |
function _field_write_instance($instance, $update = FALSE) {
|
| 695 |
$field = field_read_field($instance['field_name']);
|
| 696 |
$field_type = field_info_field_types($field['type']);
|
| 697 |
|
| 698 |
// Set defaults.
|
| 699 |
$instance += array(
|
| 700 |
'settings' => array(),
|
| 701 |
'display' => array(),
|
| 702 |
'widget' => array(),
|
| 703 |
'required' => FALSE,
|
| 704 |
'label' => $instance['field_name'],
|
| 705 |
'description' => '',
|
| 706 |
'weight' => 0,
|
| 707 |
'deleted' => 0,
|
| 708 |
);
|
| 709 |
|
| 710 |
// Set default instance settings.
|
| 711 |
$instance['settings'] += field_info_instance_settings($field['type']);
|
| 712 |
|
| 713 |
// Set default widget and settings.
|
| 714 |
$instance['widget'] += array(
|
| 715 |
// TODO: what if no 'default_widget' specified ?
|
| 716 |
'type' => $field_type['default_widget'],
|
| 717 |
'settings' => array(),
|
| 718 |
'weight' => 0,
|
| 719 |
);
|
| 720 |
// Check widget module.
|
| 721 |
$widget_type = field_info_widget_types($instance['widget']['type']);
|
| 722 |
$instance['widget']['module'] = $widget_type['module'];
|
| 723 |
$instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']);
|
| 724 |
|
| 725 |
// Make sure there is at least display info for the 'full' build mode.
|
| 726 |
$instance['display'] += array(
|
| 727 |
'full' => array(),
|
| 728 |
);
|
| 729 |
// Set default display settings for each build mode.
|
| 730 |
foreach ($instance['display'] as $build_mode => $display) {
|
| 731 |
$instance['display'][$build_mode] += array(
|
| 732 |
'label' => 'above',
|
| 733 |
// TODO: what if no 'default_formatter' specified ?
|
| 734 |
'type' => $field_type['default_formatter'],
|
| 735 |
'settings' => array(),
|
| 736 |
'weight' => 0,
|
| 737 |
);
|
| 738 |
$formatter_type = field_info_formatter_types($instance['display'][$build_mode]['type']);
|
| 739 |
// TODO : 'hidden' will raise PHP warnings.
|
| 740 |
$instance['display'][$build_mode]['module'] = $formatter_type['module'];
|
| 741 |
$instance['display'][$build_mode]['settings'] += field_info_formatter_settings($instance['display'][$build_mode]['type']);
|
| 742 |
}
|
| 743 |
|
| 744 |
// The serialized 'data' column contains everything from $instance that does
|
| 745 |
// not have its own column and is not automatically populated when the
|
| 746 |
// instance is read.
|
| 747 |
$data = $instance;
|
| 748 |
unset($data['id'], $data['field_id'], $data['field_name'], $data['bundle'], $data['deleted']);
|
| 749 |
|
| 750 |
$record = array(
|
| 751 |
'field_id' => $instance['field_id'],
|
| 752 |
'field_name' => $instance['field_name'],
|
| 753 |
'object_type' => $instance['object_type'],
|
| 754 |
'bundle' => $instance['bundle'],
|
| 755 |
'data' => $data,
|
| 756 |
'deleted' => $instance['deleted'],
|
| 757 |
);
|
| 758 |
// We need to tell drupal_update_record() the primary keys to trigger an
|
| 759 |
// update.
|
| 760 |
if ($update) {
|
| 761 |
$record['id'] = $instance['id'];
|
| 762 |
$primary_key = array('id');
|
| 763 |
}
|
| 764 |
else {
|
| 765 |
$primary_key = array();
|
| 766 |
}
|
| 767 |
drupal_write_record('field_config_instance', $record, $primary_key);
|
| 768 |
}
|
| 769 |
|
| 770 |
/**
|
| 771 |
* Read a single instance record directly from the database. Generally,
|
| 772 |
* you should use the field_info_instance() instead.
|
| 773 |
*
|
| 774 |
* This function will not return deleted instances. Use
|
| 775 |
* field_read_instances() instead for this purpose.
|
| 776 |
*
|
| 777 |
* @param $obj_type
|
| 778 |
* The type of object to which the field is bound.
|
| 779 |
* @param $field_name
|
| 780 |
* The field name to read.
|
| 781 |
* @param $bundle
|
| 782 |
* The bundle to which the field is bound.
|
| 783 |
* @param array $include_additional
|
| 784 |
* The default behavior of this function is to not return an instance that
|
| 785 |
* has been deleted, or whose field is inactive. Setting
|
| 786 |
* $include_additional['include_inactive'] or
|
| 787 |
* $include_additional['include_deleted'] to TRUE will override this
|
| 788 |
* behavior.
|
| 789 |
* @return
|
| 790 |
* An instance structure, or FALSE.
|
| 791 |
*/
|
| 792 |
function field_read_instance($obj_type, $field_name, $bundle, $include_additional = array()) {
|
| 793 |
$instances = field_read_instances(array('object_type' => $obj_type, 'field_name' => $field_name, 'bundle' => $bundle), $include_additional);
|
| 794 |
return $instances ? current($instances) : FALSE;
|
| 795 |
}
|
| 796 |
|
| 797 |
/**
|
| 798 |
* Read in field instances that match an array of conditions.
|
| 799 |
*
|
| 800 |
* @param $param
|
| 801 |
* An array of properties to use in selecting a field
|
| 802 |
* instance. Valid keys include any column of the
|
| 803 |
* field_config_instance table. If NULL, all instances will be returned.
|
| 804 |
* @param $include_additional
|
| 805 |
* The default behavior of this function is to not return field
|
| 806 |
* instances that have been marked deleted, or whose field is inactive.
|
| 807 |
* Setting $include_additional['include_inactive'] or
|
| 808 |
* $include_additional['include_deleted'] to TRUE will override this
|
| 809 |
* behavior.
|
| 810 |
* @return
|
| 811 |
* An array of instances matching the arguments.
|
| 812 |
*/
|
| 813 |
function field_read_instances($params = array(), $include_additional = array()) {
|
| 814 |
$query = db_select('field_config_instance', 'fci', array('fetch' => PDO::FETCH_ASSOC));
|
| 815 |
$query->join('field_config', 'fc', 'fc.id = fci.field_id');
|
| 816 |
$query->fields('fci');
|
| 817 |
|
| 818 |
// Turn the conditions into a query.
|
| 819 |
foreach ($params as $key => $value) {
|
| 820 |
$query->condition('fci.' . $key, $value);
|
| 821 |
}
|
| 822 |
if (!isset($include_additional['include_inactive']) || !$include_additional['include_inactive']) {
|
| 823 |
$query
|
| 824 |
->condition('fc.active', 1)
|
| 825 |
->condition('fc.storage_active', 1);
|
| 826 |
}
|
| 827 |
if (!isset($include_additional['include_deleted']) || !$include_additional['include_deleted']) {
|
| 828 |
$query->condition('fc.deleted', 0);
|
| 829 |
$query->condition('fci.deleted', 0);
|
| 830 |
}
|
| 831 |
|
| 832 |
$instances = array();
|
| 833 |
$results = $query->execute();
|
| 834 |
|
| 835 |
foreach ($results as $record) {
|
| 836 |
$instance = unserialize($record['data']);
|
| 837 |
$instance['id'] = $record['id'];
|
| 838 |
$instance['field_id'] = $record['field_id'];
|
| 839 |
$instance['field_name'] = $record['field_name'];
|
| 840 |
$instance['object_type'] = $record['object_type'];
|
| 841 |
$instance['bundle'] = $record['bundle'];
|
| 842 |
$instance['deleted'] = $record['deleted'];
|
| 843 |
|
| 844 |
module_invoke_all('field_read_instance', $instance);
|
| 845 |
$instances[] = $instance;
|
| 846 |
}
|
| 847 |
return $instances;
|
| 848 |
}
|
| 849 |
|
| 850 |
/**
|
| 851 |
* Mark a field instance for deletion, including all data associated with
|
| 852 |
* it.
|
| 853 |
*
|
| 854 |
* @param $instance
|
| 855 |
* An instance structure.
|
| 856 |
*/
|
| 857 |
function field_delete_instance($instance) {
|
| 858 |
// Mark the field instance for deletion.
|
| 859 |
db_update('field_config_instance')
|
| 860 |
->fields(array('deleted' => 1))
|
| 861 |
->condition('field_name', $instance['field_name'])
|
| 862 |
->condition('object_type', $instance['object_type'])
|
| 863 |
->condition('bundle', $instance['bundle'])
|
| 864 |
->execute();
|
| 865 |
|
| 866 |
// Mark instance data for deletion.
|
| 867 |
$field = field_info_field($instance['field_name']);
|
| 868 |
module_invoke($field['storage']['module'], 'field_storage_delete_instance', $instance);
|
| 869 |
|
| 870 |
// Clear the cache.
|
| 871 |
field_cache_clear();
|
| 872 |
|
| 873 |
module_invoke_all('field_delete_instance', $instance);
|
| 874 |
}
|
| 875 |
|
| 876 |
/**
|
| 877 |
* @} End of "defgroup field_crud".
|
| 878 |
*/
|
| 879 |
|
| 880 |
/*
|
| 881 |
* @defgroup field_purge Field API bulk data deletion
|
| 882 |
* @{
|
| 883 |
* Clean up after Field API bulk deletion operations.
|
| 884 |
*
|
| 885 |
* Field API provides functions for deleting data attached to individual
|
| 886 |
* objects as well as deleting entire fields or field instances in a single
|
| 887 |
* operation.
|
| 888 |
*
|
| 889 |
* Deleting field data items for an object with field_attach_delete() involves
|
| 890 |
* three separate operations:
|
| 891 |
* - Invoking the Field Type API hook_field_delete() for each field on the
|
| 892 |
* object. The hook for each field type receives the object and the specific
|
| 893 |
* field being deleted. A file field module might use this hook to delete
|
| 894 |
* uploaded files from the filesystem.
|
| 895 |
* - Invoking the Field Storage API hook_field_storage_delete() to remove
|
| 896 |
* data from the primary field storage. The hook implementation receives the
|
| 897 |
* object being deleted and deletes data for all of the object's bundle's
|
| 898 |
* fields.
|
| 899 |
* - Invoking the global Field Attach API hook_field_attach_delete() for all
|
| 900 |
* modules that implement it. Each hook implementation receives the object
|
| 901 |
* being deleted and can operate on whichever subset of the object's bundle's
|
| 902 |
* fields it chooses to.
|
| 903 |
*
|
| 904 |
* These hooks are invoked immediately when field_attach_delete() is
|
| 905 |
* called. Similar operations are performed for field_attach_delete_revision().
|
| 906 |
*
|
| 907 |
* When a field, bundle, or field instance is deleted, it is not practical to
|
| 908 |
* invoke these hooks immediately on every affected object in a single page
|
| 909 |
* request; there could be thousands or millions of them. Instead, the
|
| 910 |
* appropriate field data items, instances, and/or fields are marked as deleted
|
| 911 |
* so that subsequent load or query operations will not return them. Later, a
|
| 912 |
* separate process cleans up, or "purges", the marked-as-deleted data by going
|
| 913 |
* through the three-step process described above and, finally, removing
|
| 914 |
* deleted field and instance records.
|
| 915 |
*
|
| 916 |
* Purging field data is made somewhat tricky by the fact that, while
|
| 917 |
* field_attach_delete() has a complete object to pass to the various deletion
|
| 918 |
* hooks, the Field API purge process only has the field data it has previously
|
| 919 |
* stored. It cannot reconstruct complete original objects to pass to the
|
| 920 |
* deletion hooks. It is even possible that the original object to which some
|
| 921 |
* Field API data was attached has been itself deleted before the field purge
|
| 922 |
* operation takes place.
|
| 923 |
*
|
| 924 |
* Field API resolves this problem by using "pseudo-objects" during purge
|
| 925 |
* operations. A pseudo-object contains only the information from the original
|
| 926 |
* object that Field API knows about: entity type, id, revision id, and
|
| 927 |
* bundle. It also contains the field data for whichever field instance is
|
| 928 |
* currently being purged. For example, suppose that the node type 'story' used
|
| 929 |
* to contain a field called 'subtitle' but the field was deleted. If node 37
|
| 930 |
* was a story with a subtitle, the pseudo-object passed to the purge hooks
|
| 931 |
* would look something like this:
|
| 932 |
*
|
| 933 |
* @code
|
| 934 |
* $obj = stdClass Object(
|
| 935 |
* [nid] => 37,
|
| 936 |
* [vid] => 37,
|
| 937 |
* [type] => 'story',
|
| 938 |
* [subtitle] => array(
|
| 939 |
* [0] => array(
|
| 940 |
* 'value' => 'subtitle text',
|
| 941 |
* ),
|
| 942 |
* ),
|
| 943 |
* );
|
| 944 |
* @endcode
|
| 945 |
*/
|
| 946 |
|
| 947 |
/**
|
| 948 |
* Purge some deleted Field API data, instances, or fields.
|
| 949 |
*
|
| 950 |
* This function will purge deleted field data on up to a specified maximum
|
| 951 |
* number of objects and then return. If a deleted field instance with no
|
| 952 |
* remaining data records is found, the instance itself will be purged.
|
| 953 |
* If a deleted field with no remaining field instances is found, the field
|
| 954 |
* itself will be purged.
|
| 955 |
*
|
| 956 |
* @param $batch_size
|
| 957 |
* The maximum number of field data records to purge before returning.
|
| 958 |
*/
|
| 959 |
function field_purge_batch($batch_size) {
|
| 960 |
// Retrieve all deleted field instances. We cannot use field_info_instances()
|
| 961 |
// because that function does not return deleted instances.
|
| 962 |
$instances = field_read_instances(array('deleted' => 1), array('include_deleted' => 1));
|
| 963 |
|
| 964 |
foreach ($instances as $instance) {
|
| 965 |
$field = field_info_field_by_id($instance['field_id']);
|
| 966 |
|
| 967 |
// Retrieve some pseudo-objects.
|
| 968 |
$obj_types = field_attach_query($instance['field_id'], array(array('bundle', $instance['bundle']), array('deleted', 1)), array('limit' => $batch_size));
|
| 969 |
|
| 970 |
if (count($obj_types) > 0) {
|
| 971 |
// Field data for the instance still exists.
|
| 972 |
foreach ($obj_types as $obj_type => $objects) {
|
| 973 |
field_attach_load($obj_type, $objects, FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1));
|
| 974 |
|
| 975 |
foreach ($objects as $id => $object) {
|
| 976 |
// field_attach_query() may return more results than we asked for.
|
| 977 |
// Stop when he have done our batch size.
|
| 978 |
if ($batch_size-- <= 0) {
|
| 979 |
return;
|
| 980 |
}
|
| 981 |
|
| 982 |
// Purge the data for the object.
|
| 983 |
field_purge_data($obj_type, $object, $field, $instance);
|
| 984 |
}
|
| 985 |
}
|
| 986 |
}
|
| 987 |
else {
|
| 988 |
// No field data remains for the instance, so we can remove it.
|
| 989 |
field_purge_instance($instance);
|
| 990 |
}
|
| 991 |
}
|
| 992 |
|
| 993 |
// Retrieve all deleted fields. Any that have no bundles can be purged.
|
| 994 |
$fields = field_read_fields(array('deleted' => 1), array('include_deleted' => 1));
|
| 995 |
foreach ($fields as $field) {
|
| 996 |
// field_read_fields() does not return $field['bundles'] which we need.
|
| 997 |
$field = field_info_field_by_id($field['id']);
|
| 998 |
if (!isset($field['bundles']) || count($field['bundles']) == 0) {
|
| 999 |
field_purge_field($field);
|
| 1000 |
}
|
| 1001 |
}
|
| 1002 |
}
|
| 1003 |
|
| 1004 |
/**
|
| 1005 |
* Purge the field data for a single field on a single pseudo-object.
|
| 1006 |
*
|
| 1007 |
* This is basically the same as field_attach_delete() except it only applies
|
| 1008 |
* to a single field. The object itself is not being deleted, and it is quite
|
| 1009 |
* possible that other field data will remain attached to it.
|
| 1010 |
*
|
| 1011 |
* @param $obj_type
|
| 1012 |
* The type of $object; e.g. 'node' or 'user'.
|
| 1013 |
* @param $object
|
| 1014 |
* The pseudo-object whose field data to delete.
|
| 1015 |
* @param $field
|
| 1016 |
* The (possibly deleted) field whose data is being purged.
|
| 1017 |
* @param $instance
|
| 1018 |
* The deleted field instance whose data is being purged.
|
| 1019 |
*/
|
| 1020 |
function field_purge_data($obj_type, $object, $field, $instance) {
|
| 1021 |
// Each field type's hook_field_delete() only expects to operate on a single
|
| 1022 |
// field at a time, so we can use it as-is for purging.
|
| 1023 |
$options = array('field_id' => $instance['field_id'], 'deleted' => TRUE);
|
| 1024 |
_field_invoke('delete', $obj_type, $object, $dummy, $dummy, $options);
|
| 1025 |
|
| 1026 |
// Tell the field storage system to purge the data.
|
| 1027 |
module_invoke($field['storage']['module'], 'field_storage_purge', $obj_type, $object, $field, $instance);
|
| 1028 |
|
| 1029 |
// Let other modules act on purging the data.
|
| 1030 |
foreach (module_implements('field_attach_purge') as $module) {
|
| 1031 |
$function = $module . '_field_attach_purge';
|
| 1032 |
$function($obj_type, $object, $field, $instance);
|
| 1033 |
}
|
| 1034 |
}
|
| 1035 |
|
| 1036 |
/**
|
| 1037 |
* Purge a field instance record from the database.
|
| 1038 |
*
|
| 1039 |
* This function assumes all data for the instance has already been purged, and
|
| 1040 |
* should only be called by field_purge_batch().
|
| 1041 |
*
|
| 1042 |
* @param $instance
|
| 1043 |
* The instance record to purge.
|
| 1044 |
*/
|
| 1045 |
function field_purge_instance($instance) {
|
| 1046 |
db_delete('field_config_instance')
|
| 1047 |
->condition('id', $instance['id'])
|
| 1048 |
->execute();
|
| 1049 |
|
| 1050 |
// Notify the storage engine.
|
| 1051 |
$field = field_info_field_by_id($instance['field_id']);
|
| 1052 |
module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance);
|
| 1053 |
|
| 1054 |
// Clear the cache.
|
| 1055 |
field_info_cache_clear();
|
| 1056 |
|
| 1057 |
// Invoke external hooks after the cache is cleared for API consistency.
|
| 1058 |
module_invoke_all('field_purge_instance', $instance);
|
| 1059 |
}
|
| 1060 |
|
| 1061 |
/**
|
| 1062 |
* Purge a field record from the database.
|
| 1063 |
*
|
| 1064 |
* This function assumes all instances for the field has already been purged,
|
| 1065 |
* and should only be called by field_purge_batch().
|
| 1066 |
*
|
| 1067 |
* @param $field
|
| 1068 |
* The field record to purge.
|
| 1069 |
*/
|
| 1070 |
function field_purge_field($field) {
|
| 1071 |
$instances = field_read_instances(array('field_id' => $field['id']), array('include_deleted' => 1));
|
| 1072 |
if (count($instances) > 0) {
|
| 1073 |
throw new FieldException("Attempt to purge a field that still has instances.");
|
| 1074 |
}
|
| 1075 |
|
| 1076 |
db_delete('field_config')
|
| 1077 |
->condition('id', $field['id'])
|
| 1078 |
->execute();
|
| 1079 |
|
| 1080 |
// Notify the storage engine.
|
| 1081 |
module_invoke($field['storage']['module'], 'field_storage_purge_field', $field);
|
| 1082 |
|
| 1083 |
// Clear the cache.
|
| 1084 |
field_info_cache_clear();
|
| 1085 |
|
| 1086 |
// Invoke external hooks after the cache is cleared for API consistency.
|
| 1087 |
module_invoke_all('field_purge_field', $field);
|
| 1088 |
}
|
| 1089 |
|
| 1090 |
/**
|
| 1091 |
* @} End of "defgroup field_purge".
|
| 1092 |
*/
|
| 1093 |
|