2009-02-05 03:42:58 +00:00
< ? php
2009-02-08 21:22:59 +00:00
// $Id$
2009-02-05 03:42:58 +00:00
2009-02-08 21:22:59 +00:00
/**
* @ file
2009-02-10 03:16:15 +00:00
* Field CRUD API , handling field and field instance creation and deletion .
2009-02-08 21:22:59 +00:00
*/
2009-02-05 03:42:58 +00:00
/**
* @ defgroup field_structs Field API data structures
* @ {
* Represent Field API fields and instances .
*
* The Field API defines two primary data structures , Field and
2009-02-10 03:16:15 +00:00
* Instance , and the concept of a Bundle . A Field defines a
* particular type of data that can be attached to objects . A Field
* Instance is a Field attached to a single Bundle . A Bundle is a set
2009-02-05 03:42:58 +00:00
* of fields that are treated as a group by the Field Attach API .
*
* For example , suppose a site administrator wants Article nodes to
2009-02-10 03:16:15 +00:00
* have a subtitle and photo . Using the Field API or some UI module ,
2009-02-05 03:42:58 +00:00
* the administrator creates a field named 'subtitle' of type 'text'
2009-02-10 03:16:15 +00:00
* and a field named 'photo' of type 'image' . The administrator
2009-02-05 03:42:58 +00:00
* ( again , via a UI ) creates two Field Instances , one attaching the
* field 'subtitle' to the bundle 'article' and one attaching the field
2009-02-10 03:16:15 +00:00
* 'photo' to the bundle 'article' . When the node system uses the
2009-02-05 03:42:58 +00:00
* Field Attach API to load all fields for an Article node , it passes
* the node 's content type (which is ' article ') as the node' s bundle .
* field_attach_load () then loads the 'subtitle' and 'photo' fields
* because they are both attached to the bundle 'article' .
*
* Field objects are ( currently ) represented as an array of key / value
2009-02-10 03:16:15 +00:00
* pairs . The object properties are :
2009-02-05 03:42:58 +00:00
*
* @ param array $field :
2009-03-10 09:45:32 +00:00
* - id ( integer , read - only )
2009-03-13 14:34:58 +00:00
* The primary identifier of the field . It is assigned automatically
* by field_create_field () .
2009-02-05 03:42:58 +00:00
* - field_name ( string )
2009-02-10 03:16:15 +00:00
* The name of the field . Each field name is unique within Field API .
2009-02-05 03:42:58 +00:00
* When a field is attached to an object , the field ' s data is stored
* in $object -> $field_name .
* - type ( string )
2009-02-10 03:16:15 +00:00
* The type of the field , such as 'text' or 'image' . Field types
2009-04-24 03:44:17 +00:00
* are defined by modules that implement hook_field_info () .
2009-02-05 03:42:58 +00:00
* - cardinality ( integer )
2009-02-10 03:16:15 +00:00
* The number of values the field can hold . Legal values are any
2009-02-05 03:42:58 +00:00
* positive integer or FIELD_CARDINALITY_UNLIMITED .
2009-08-22 00:58:55 +00:00
* - translatable ( integer )
* Whether the field is translatable .
2009-02-05 03:42:58 +00:00
* - locked ( integer )
* TODO : undefined .
* - module ( string , read - only )
* The name of the module that implements the field type .
* - active ( integer , read - only )
* TRUE if the module that implements the field type is currently
* enabled , FALSE otherwise .
* - deleted ( integer , read - only )
2009-02-10 03:16:15 +00:00
* TRUE if this field has been deleted , FALSE otherwise . Deleted
2009-02-05 03:42:58 +00:00
* fields are ignored by the Field Attach API . This property exists
* because fields can be marked for deletion but only actually
* destroyed by a separate garbage - collection process .
* - columns ( array , read - only ) .
* An array of the Field API columns used to store each value of
2009-02-10 03:16:15 +00:00
* this field . The column list may depend on field settings ; it is
* not constant per field type . Field API column specifications are
2009-02-05 03:42:58 +00:00
* exactly like Schema API column specifications but , depending on
* the field storage module in use , the name of the column may not
* represent an actual column in an SQL database .
2009-05-20 09:48:47 +00:00
* - indexes ( array ) .
* An array of indexes on data columns , using the same definition format
* as Schema API index specifications . Only columns that appear in the
* 'columns' setting are allowed . Note that field types can specify
* default indexes , which can be modified or added to when
2009-05-24 17:39:35 +00:00
* creating a field .
2009-02-05 03:42:58 +00:00
* - settings ( array )
2009-02-10 03:16:15 +00:00
* A sub - array of key / value pairs of field - type - specific settings . Each
2009-02-05 03:42:58 +00:00
* field type module defines and documents its own field settings .
*
* Field Instance objects are ( currently ) represented as an array of
2009-02-10 03:16:15 +00:00
* key / value pairs . The object properties are :
2009-02-05 03:42:58 +00:00
*
* @ param array $instance :
2009-03-10 09:45:32 +00:00
* - id ( integer , read - only )
2009-03-13 14:34:58 +00:00
* The primary identifier of this field instance . It is assigned
* automatically by field_create_instance () .
2009-03-10 09:45:32 +00:00
* - field_id ( integer , read - only )
* The foreign key of the field attached to the bundle by this instance .
2009-03-13 14:34:58 +00:00
* It is populated automatically by field_create_instance () .
2009-02-05 03:42:58 +00:00
* - field_name ( string )
2009-03-10 09:45:32 +00:00
* The name of the field attached to the bundle by this instance .
2009-02-05 03:42:58 +00:00
* - bundle ( string )
* The name of the bundle that the field is attached to .
* - label ( string )
* A human - readable label for the field when used with this
2009-02-10 03:16:15 +00:00
* bundle . For example , the label will be the title of Form API
2009-02-05 03:42:58 +00:00
* elements for this instance .
* - description ( string )
* A human - readable description for the field when used with this
2009-02-10 03:16:15 +00:00
* bundle . For example , the description will be the help text of
2009-02-05 03:42:58 +00:00
* Form API elements for this instance .
* - required ( integer )
* TRUE if a value for this field is required when used with this
2009-02-10 03:16:15 +00:00
* bundle , FALSE otherwise . Currently , required - ness is only enforced
2009-02-05 03:42:58 +00:00
* during Form API operations , not by field_attach_load (),
* field_attach_insert (), or field_attach_update () .
* - default_value_function ( string )
* The name of the function , if any , that will provide a default value .
* - deleted ( integer , read - only )
* TRUE if this instance has been deleted , FALSE otherwise .
* Deleted instances are ignored by the Field Attach API .
* This property exists because instances can be marked for deletion but
* only actually destroyed by a separate garbage - collection process .
* - settings ( array )
* A sub - array of key / value pairs of field - type - specific instance
2009-02-10 03:16:15 +00:00
* settings . Each field type module defines and documents its own
2009-02-05 03:42:58 +00:00
* instance settings .
* - widget ( array )
* A sub - array of key / value pairs identifying the Form API input widget
* for the field when used by this bundle .
* - type ( string )
2009-02-10 03:16:15 +00:00
* The type of the widget , such as text_textfield . Widget types
2009-02-05 03:42:58 +00:00
* are defined by modules that implement hook_field_widget_info () .
2009-08-13 01:50:00 +00:00
* - settings ( array )
* A sub - array of key / value pairs of widget - type - specific settings .
* Each field widget type module defines and documents its own
* widget settings .
* - weight ( float )
* The weight of the widget relative to the other elements in object
* edit forms .
2009-02-05 03:42:58 +00:00
* - module ( string , read - only )
* The name of the module that implements the widget type .
* - active ( integer , read - only )
* TRUE if the module that implements the widget type is currently
* enabled , FALSE otherwise .
* - display ( array )
2009-07-02 20:37:03 +00:00
* A sub - array of key / value pairs identifying build modes and the way the
* field values should be displayed in each build mode .
* - full ( array )
2009-04-30 14:40:03 +00:00
* A sub - array of key / value pairs of the display options to be used
2009-07-02 20:37:03 +00:00
* when the field is being displayed in the " full " build mode .
2009-04-30 14:40:03 +00:00
* - label ( string )
* Position of the label . 'inline' , 'above' and 'hidden' are the
* values recognized by the default 'field' theme implementation .
* - type ( string )
2009-07-02 20:37:03 +00:00
* The type of the display formatter , or 'hidden' for no display .
2009-04-30 14:40:03 +00:00
* - settings ( array )
* A sub - array of key / value pairs of display options specific to
2009-07-02 20:37:03 +00:00
* the formatter .
2009-08-13 01:50:00 +00:00
* - weight ( float )
* The weight of the field relative to the other object components
* displayed in this build mode .
2009-04-30 14:40:03 +00:00
* - module ( string , read - only )
* The name of the module which implements the display formatter .
2009-07-02 20:37:03 +00:00
* - teaser
2009-02-05 03:42:58 +00:00
* - ...
2009-07-02 20:37:03 +00:00
* - other_build_mode
* - ...
2009-02-05 03:42:58 +00:00
*
* TODO D7 : document max length for field types , widget types ,
* formatter names ...
*/
/**
* @ } End of " defgroup field_structs " .
*/
/**
* @ defgroup field_crud Field CRUD API
* @ {
* Create , update , and delete Field API fields , bundles , and instances .
*
* Modules use this API , often in hook_install (), to create custom
2009-02-10 03:16:15 +00:00
* data structures . UI modules will use it to create a user interface .
2009-02-05 03:42:58 +00:00
*
* The Field CRUD API uses
* @ link field_structs Field API data structures @ endlink .
*/
/**
2009-05-20 09:48:47 +00:00
* Create a field .
*
* This function does not bind the field to any bundle ; use
* field_create_instance () for that .
2009-02-05 03:42:58 +00:00
*
* @ param $field
2009-05-20 09:48:47 +00:00
* A field structure . The field_name and type properties are required .
* Other properties , if omitted , will be given the following default values :
* - cardinality : 1
* - locked : FALSE
* - indexes : the field - type indexes , specified by the field type ' s
* hook_field_schema () . The indexes specified in $field are added
* to those default indexes . It is possible to override the
* definition of a field - type index by providing an index with the
* same name , or to remove it by redefining it as an empty array
* of columns . Overriding field - type indexes should be done
2009-05-24 17:39:35 +00:00
* carefully , for it might seriously affect the site ' s performance .
2009-05-20 09:48:47 +00:00
* - settings : each omitted setting is given the default value defined in
* hook_field_info () .
2009-05-28 10:05:32 +00:00
* @ return
* The $field structure with the id property filled in .
2009-02-05 03:42:58 +00:00
* @ throw
* FieldException
*/
function field_create_field ( $field ) {
// Field name is required.
if ( empty ( $field [ 'field_name' ])) {
throw new FieldException ( 'Attempt to create an unnamed field.' );
}
2009-03-30 05:24:38 +00:00
// Field type is required.
if ( empty ( $field [ 'type' ])) {
throw new FieldException ( 'Attempt to create a field with no type.' );
}
2009-02-05 03:42:58 +00:00
// Field name cannot contain invalid characters.
2009-08-10 21:19:42 +00:00
if ( ! preg_match ( '/^[_a-z]+[_a-z0-9]*$/' , $field [ 'field_name' ])) {
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' );
2009-02-05 03:42:58 +00:00
}
2009-08-10 21:19:42 +00:00
// Field name cannot be longer than 32 characters. We use drupal_strlen()
// because the DB layer assumes that column widths are given in characters,
// not bytes.
if ( drupal_strlen ( $field [ 'field_name' ]) > 32 ) {
throw new FieldException ( t ( 'Attempt to create a field with a name longer than 32 characters: %name' ,
array ( '%name' => $field [ 'field_name' ])));
}
2009-02-05 03:42:58 +00:00
// Check that the field type is known.
$field_type = field_info_field_types ( $field [ 'type' ]);
if ( ! $field_type ) {
throw new FieldException ( t ( 'Attempt to create a field of unknown type %type.' , array ( '%type' => $field [ 'type' ])));
}
2009-05-28 10:05:32 +00:00
// Ensure the field name is unique over active and disabled fields.
// We do not care about deleted fields.
$prior_field = field_read_field ( $field [ 'field_name' ], array ( 'include_inactive' => TRUE ));
2009-02-05 03:42:58 +00:00
if ( ! empty ( $prior_field )) {
2009-09-07 15:50:52 +00:00
$message = $prior_field [ 'active' ] ?
t ( 'Attempt to create field name %name which already exists and is active.' , array ( '%name' => $field [ 'field_name' ])) :
t ( 'Attempt to create field name %name which already exists, although it is inactive.' , array ( '%name' => $field [ 'field_name' ]));
throw new FieldException ( $message );
2009-02-05 03:42:58 +00:00
}
2009-09-11 03:42:34 +00:00
// Disallow reserved field names. This can't prevent all field name
// collisions with existing object properties, but some is better
// than none.
foreach ( field_info_fieldable_types () as $type => $info ) {
if ( in_array ( $field [ 'field_name' ], $info [ 'object keys' ])) {
throw new FieldException ( t ( 'Attempt to create field name %name which is reserved by entity type %type.' , array ( '%name' => $field [ 'field_name' ], '%type' => $type )));
}
}
2009-02-05 03:42:58 +00:00
$field += array (
'cardinality' => 1 ,
2009-08-22 00:58:55 +00:00
'translatable' => FALSE ,
2009-02-05 03:42:58 +00:00
'locked' => FALSE ,
'settings' => array (),
);
2009-08-10 21:19:42 +00:00
2009-02-05 03:42:58 +00:00
// 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' ]);
2009-05-20 09:48:47 +00:00
$field [ 'module' ] = $field_type [ 'module' ];
2009-02-05 03:42:58 +00:00
$field [ 'active' ] = 1 ;
$field [ 'deleted' ] = 0 ;
2009-05-20 09:48:47 +00:00
// 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 (
'indexes' => array (),
);
$field [ 'indexes' ] += $schema [ 'indexes' ];
// The serialized 'data' column contains everything from $field that does not
// have its own column and is not automatically populated when the field is
// read.
$data = $field ;
2009-07-04 06:19:46 +00:00
unset ( $data [ 'columns' ], $data [ 'field_name' ], $data [ 'type' ], $data [ 'locked' ], $data [ 'module' ], $data [ 'cardinality' ], $data [ 'active' ], $data [ 'deleted' ]);
2009-05-20 09:48:47 +00:00
$field [ 'data' ] = $data ;
2009-05-28 10:05:32 +00:00
// Store the field and create the id.
2009-02-05 03:42:58 +00:00
drupal_write_record ( 'field_config' , $field );
2009-08-11 14:59:40 +00:00
// The 'data' property is not part of the public field record.
unset ( $field [ 'data' ]);
2009-05-28 10:05:32 +00:00
// Invoke hook_field_storage_create_field after the field is
// complete (e.g. it has its id).
2009-09-22 08:44:04 +00:00
try {
module_invoke ( variable_get ( 'field_storage_module' , 'field_sql_storage' ), 'field_storage_create_field' , $field );
}
catch ( Exception $e ) {
// If storage creation failed, remove the field_config record before
// rethrowing the exception.
db_delete ( 'field_config' )
-> condition ( 'id' , $field [ 'id' ])
-> execute ();
throw $e ;
}
2009-05-28 10:05:32 +00:00
2009-02-05 03:42:58 +00:00
// Clear caches
field_cache_clear ( TRUE );
2009-05-28 10:05:32 +00:00
2009-07-02 20:19:48 +00:00
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all ( 'field_create_field' , $field );
2009-07-11 00:56:45 +00:00
2009-05-28 10:05:32 +00:00
return $field ;
2009-02-05 03:42:58 +00:00
}
2009-09-26 15:57:39 +00:00
/*
* Update a field .
*
* Any module may forbid any update for any reason . For example , the
* field ' s storage module might forbid an update if it would change
* the storage schema while data for the field exists . A field type
* module might forbid an update if it would change existing data ' s
* semantics , or if there are external dependencies on field settings
* that cannot be updated .
*
* @ param $field
* A field structure . $field [ 'field_name' ] must provided ; it
* identifies the field that will be updated to match this
* structure . Any other properties of the field that are not
* specified in $field will be left unchanged , so it is not
* necessary to pass in a fully populated $field structure .
* @ return
* Throws a FieldException if the update cannot be performed .
* @ see field_create_field ()
*/
function field_update_field ( $field ) {
// Check that the specified field exists.
$prior_field = field_read_field ( $field [ 'field_name' ]);
if ( empty ( $prior_field )) {
throw new FieldException ( 'Attempt to update a non-existent field.' );
}
// Use the prior field values for anything not specifically set by the new
// field to be sure that all values are set.
$field += $prior_field ;
$field [ 'settings' ] += $prior_field [ 'settings' ];
// Field type cannot be changed.
if ( $field [ 'type' ] != $prior_field [ 'type' ]) {
throw new FieldException ( " Cannot change an existing field's type. " );
}
// Collect the new storage information, since what is in
// $prior_field may no longer be right.
$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 (
'indexes' => array (),
);
$field [ 'indexes' ] += $schema [ 'indexes' ];
$has_data = field_attach_field_has_data ( $field );
// See if any module forbids the update by throwing an exception.
foreach ( module_implements ( 'field_update_forbid' ) as $module ) {
$function = $module . '_field_update_forbid' ;
$function ( $field , $prior_field , $has_data );
}
// Tell the storage engine to update the field. Do this before
// saving the new definition since it still might fail.
module_invoke ( variable_get ( 'field_storage_module' , 'field_sql_storage' ), 'field_storage_update_field' , $field , $prior_field , $has_data );
// Save the new field definition. @todo: refactor with
// field_create_field.
// The serialized 'data' column contains everything from $field that does not
// have its own column and is not automatically populated when the field is
// read.
$data = $field ;
unset ( $data [ 'columns' ], $data [ 'field_name' ], $data [ 'type' ], $data [ 'locked' ], $data [ 'module' ], $data [ 'cardinality' ], $data [ 'active' ], $data [ 'deleted' ]);
$field [ 'data' ] = $data ;
// Store the field and create the id.
$primary_key = array ( 'id' );
drupal_write_record ( 'field_config' , $field , $primary_key );
// Clear caches
field_cache_clear ( TRUE );
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all ( 'field_update_field' , $field , $prior_field , $has_data );
}
2009-02-05 03:42:58 +00:00
/**
2009-02-10 03:16:15 +00:00
* Read a single field record directly from the database . Generally ,
2009-02-05 03:42:58 +00:00
* you should use the field_info_field () instead .
*
2009-05-28 10:05:32 +00:00
* This function will not return deleted fields . Use
* field_read_fields () instead for this purpose .
*
2009-02-05 03:42:58 +00:00
* @ param $field_name
* The field name to read .
* @ param array $include_additional
* The default behavior of this function is to not return a field that
2009-05-28 10:05:32 +00:00
* is inactive . Setting
* $include_additional [ 'include_inactive' ] to TRUE will override this
2009-02-05 03:42:58 +00:00
* behavior .
* @ return
* A field structure , or FALSE .
*/
function field_read_field ( $field_name , $include_additional = array ()) {
$fields = field_read_fields ( array ( 'field_name' => $field_name ), $include_additional );
return $fields ? current ( $fields ) : FALSE ;
}
/**
* Read in fields that match an array of conditions .
*
* @ param array $params
* An array of conditions to match against .
* @ param array $include_additional
* The default behavior of this function is to not return fields that
* are inactive or have been deleted . Setting
* $include_additional [ 'include_inactive' ] or
* $include_additional [ 'include_deleted' ] to TRUE will override this
* behavior .
* @ return
2009-05-28 10:05:32 +00:00
* An array of fields matching $params . If
* $include_additional [ 'include_deletd' ] is TRUE , the array is keyed
* by field id , otherwise it is keyed by field name .
2009-02-05 03:42:58 +00:00
*/
function field_read_fields ( $params = array (), $include_additional = array ()) {
$query = db_select ( 'field_config' , 'fc' , array ( 'fetch' => PDO :: FETCH_ASSOC ));
$query -> fields ( 'fc' );
// Turn the conditions into a query.
foreach ( $params as $key => $value ) {
$query -> condition ( $key , $value );
}
if ( ! isset ( $include_additional [ 'include_inactive' ]) || ! $include_additional [ 'include_inactive' ]) {
$query -> condition ( 'fc.active' , 1 );
}
2009-05-28 10:05:32 +00:00
$include_deleted = ( isset ( $include_additional [ 'include_deleted' ]) && $include_additional [ 'include_deleted' ]);
if ( ! $include_deleted ) {
2009-02-05 03:42:58 +00:00
$query -> condition ( 'fc.deleted' , 0 );
}
$fields = array ();
$results = $query -> execute ();
foreach ( $results as $field ) {
2009-05-20 09:48:47 +00:00
// Extract serialized data.
$data = unserialize ( $field [ 'data' ]);
unset ( $field [ 'data' ]);
$field += $data ;
2009-02-05 03:42:58 +00:00
module_invoke_all ( 'field_read_field' , $field );
2009-05-20 09:48:47 +00:00
// Populate storage information.
$schema = ( array ) module_invoke ( $field [ 'module' ], 'field_schema' , $field );
$schema += array ( 'columns' => array (), 'indexes' => array ());
$field [ 'columns' ] = $schema [ 'columns' ];
2009-02-05 03:42:58 +00:00
2009-05-28 10:05:32 +00:00
$field_name = $field [ 'field_name' ];
if ( $include_deleted ) {
$field_name = $field [ 'id' ];
}
$fields [ $field_name ] = $field ;
2009-02-05 03:42:58 +00:00
}
return $fields ;
}
/**
* Mark a field for deletion , including all its instances and all data
* associated with it .
*
* @ param $field_name
* The field name to delete .
*/
function field_delete_field ( $field_name ) {
2009-09-09 11:37:34 +00:00
// Delete all non-deleted instances.
$field = field_info_field ( $field_name );
if ( isset ( $field [ 'bundles' ])) {
foreach ( $field [ 'bundles' ] as $bundle ) {
field_delete_instance ( $field_name , $bundle );
}
}
2009-05-28 10:05:32 +00:00
// Mark field storage for deletion.
module_invoke ( variable_get ( 'field_storage_module' , 'field_sql_storage' ), 'field_storage_delete_field' , $field_name );
// Mark the field for deletion.
db_update ( 'field_config' )
2009-02-05 03:42:58 +00:00
-> fields ( array ( 'deleted' => 1 ))
-> condition ( 'field_name' , $field_name )
-> execute ();
2009-05-28 10:05:32 +00:00
2009-02-05 03:42:58 +00:00
// Clear the cache.
field_cache_clear ( TRUE );
2009-09-09 11:37:34 +00:00
module_invoke_all ( 'field_delete_field' , $field );
2009-02-05 03:42:58 +00:00
}
/**
* Creates an instance of a field , binding it to a bundle .
*
* @ param $instance
2009-05-20 09:48:47 +00:00
* A field instance structure . The field_name and bundle properties are
* required .
* Other properties , if omitted , will be given the following default values :
* - label : the field name
* - description : empty string
* - weight : 0
* - required : FALSE
* - default_value_function : empty string
* - settings : each omitted setting is given the default value specified in
* hook_field_info () .
* - widget :
* - type : the default widget specified in hook_field_info () .
* - settings : each omitted setting is given the default value specified in
* hook_field_widget_info () .
* - display :
* Settings for the 'full' build mode will be added , and each build mode
* will be completed with the follwong default values :
* - label : 'above'
* - type : the default formatter specified in hook_field_info () .
* - settings : each omitted setting is given the default value specified in
* hook_field_formatter_info () .
2009-05-28 10:05:32 +00:00
* @ return
* The $instance structure with the id property filled in .
2009-02-05 03:42:58 +00:00
* @ throw
* FieldException
*/
function field_create_instance ( $instance ) {
// Check that the specified field exists.
$field = field_read_field ( $instance [ 'field_name' ]);
if ( empty ( $field )) {
throw new FieldException ( " Attempt to create an instance of a field that doesn't exist. " );
}
2009-03-10 09:45:32 +00:00
// Set the field id.
$instance [ 'field_id' ] = $field [ 'id' ];
2009-06-12 08:39:40 +00:00
// Note that we do *not* prevent creating a field on non-existing bundles,
// because that would break the 'Body as field' upgrade for contrib
// node types.
2009-02-05 03:42:58 +00:00
// TODO: Check that the widget type is known and can handle the field type ?
// TODO: Check that the formatters are known and can handle the field type ?
// TODO: Check that the display build modes are known for the object type ?
// Those checks should probably happen in _field_write_instance() ?
// Problem : this would mean that a UI module cannot update an instance with a disabled formatter.
// Ensure the field instance is unique.
2009-05-28 10:05:32 +00:00
$prior_instance = field_read_instance ( $instance [ 'field_name' ], $instance [ 'bundle' ], array ( 'include_inactive' => TRUE ));
2009-02-05 03:42:58 +00:00
if ( ! empty ( $prior_instance )) {
2009-09-07 15:50:52 +00:00
$message = $prior_instance [ 'widget' ][ 'active' ] ?
t ( 'Attempt to create a field instance %field_name,%bundle which already exists and is active.' , array ( '%field_name' => $instance [ 'field_name' ], '%bundle' => $instance [ 'bundle' ])) :
t ( 'Attempt to create a field instance %field_name,%bundle which already exists, although inactive.' , array ( '%field_name' => $instance [ 'field_name' ], '%bundle' => $instance [ 'bundle' ]));
throw new FieldException ( $message );
2009-02-05 03:42:58 +00:00
}
_field_write_instance ( $instance );
// Clear caches
field_cache_clear ();
2009-03-30 03:44:55 +00:00
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all ( 'field_create_instance' , $instance );
2009-05-28 10:05:32 +00:00
return $instance ;
2009-02-05 03:42:58 +00:00
}
/*
* Update an instance of a field .
*
* @ param $instance
* An associative array represeting an instance structure . The required
* keys and values are :
2009-02-10 03:16:15 +00:00
* field_name : The name of an existing field .
* bundle : The bundle this field belongs to .
2009-03-10 09:45:32 +00:00
* Read - only_id properties are assigned automatically . Any other
* properties specified in $instance overwrite the existing values for
* the instance .
2009-02-05 03:42:58 +00:00
* @ throw
* FieldException
* @ see field_create_instance ()
*/
function field_update_instance ( $instance ) {
// Check that the specified field exists.
$field = field_read_field ( $instance [ 'field_name' ]);
if ( empty ( $field )) {
throw new FieldException ( " Attempt to update an instance of a nonexistent field. " );
}
// Check that the field instance exists (even if it is inactive, since we
// want to be able to replace inactive widgets with new ones).
$prior_instance = field_read_instance ( $instance [ 'field_name' ], $instance [ 'bundle' ], array ( 'include_inactive' => TRUE ));
if ( empty ( $prior_instance )) {
throw new FieldException ( " Attempt to update a field instance that doesn't exist. " );
}
2009-03-10 09:45:32 +00:00
$instance [ 'id' ] = $prior_instance [ 'id' ];
$instance [ 'field_id' ] = $prior_instance [ 'field_id' ];
2009-02-05 03:42:58 +00:00
_field_write_instance ( $instance , TRUE );
// Clear caches.
field_cache_clear ();
}
/**
* Store an instance record in the field configuration database .
*
* @ param $instance
* An instance structure .
* @ param $update
* Whether this is a new or existing instance .
*/
function _field_write_instance ( $instance , $update = FALSE ) {
$field = field_read_field ( $instance [ 'field_name' ]);
$field_type = field_info_field_types ( $field [ 'type' ]);
// Set defaults.
$instance += array (
'settings' => array (),
'display' => array (),
'widget' => array (),
'required' => FALSE ,
'label' => $instance [ 'field_name' ],
'description' => '' ,
'weight' => 0 ,
'deleted' => 0 ,
);
// Set default instance settings.
$instance [ 'settings' ] += field_info_instance_settings ( $field [ 'type' ]);
// Set default widget and settings.
$instance [ 'widget' ] += array (
// TODO: what if no 'default_widget' specified ?
'type' => $field_type [ 'default_widget' ],
'settings' => array (),
2009-08-13 01:50:00 +00:00
'weight' => 0 ,
2009-02-05 03:42:58 +00:00
);
// Check widget module.
$widget_type = field_info_widget_types ( $instance [ 'widget' ][ 'type' ]);
$widget_module = $widget_type [ 'module' ];
$widget_active = module_exists ( $widget_module );
$instance [ 'widget' ][ 'settings' ] += field_info_widget_settings ( $instance [ 'widget' ][ 'type' ]);
$instance [ 'widget' ][ 'module' ] = $widget_module ;
$instance [ 'widget' ][ 'active' ] = $widget_active ;
2009-07-02 20:37:03 +00:00
// Make sure there is at least display info for the 'full' build mode.
2009-02-05 03:42:58 +00:00
$instance [ 'display' ] += array (
'full' => array (),
);
2009-07-02 20:37:03 +00:00
// Set default display settings for each build mode.
foreach ( $instance [ 'display' ] as $build_mode => $display ) {
$instance [ 'display' ][ $build_mode ] += array (
2009-02-05 03:42:58 +00:00
'label' => 'above' ,
// TODO: what if no 'default_formatter' specified ?
'type' => $field_type [ 'default_formatter' ],
'settings' => array (),
2009-08-13 01:50:00 +00:00
'weight' => 0 ,
2009-02-05 03:42:58 +00:00
);
2009-07-02 20:37:03 +00:00
$formatter_type = field_info_formatter_types ( $instance [ 'display' ][ $build_mode ][ 'type' ]);
2009-02-05 03:42:58 +00:00
// TODO : 'hidden' will raise PHP warnings.
2009-07-02 20:37:03 +00:00
$instance [ 'display' ][ $build_mode ][ 'module' ] = $formatter_type [ 'module' ];
$instance [ 'display' ][ $build_mode ][ 'settings' ] += field_info_formatter_settings ( $instance [ 'display' ][ $build_mode ][ 'type' ]);
2009-02-05 03:42:58 +00:00
}
2009-05-20 09:48:47 +00:00
// The serialized 'data' column contains everything from $instance that does
// not have its own column and is not automatically populated when the
// instance is read.
2009-02-05 03:42:58 +00:00
$data = $instance ;
2009-08-13 01:50:00 +00:00
unset ( $data [ 'id' ], $data [ 'field_id' ], $data [ 'field_name' ], $data [ 'bundle' ], $data [ 'widget' ][ 'type' ], $data [ 'deleted' ]);
2009-02-05 03:42:58 +00:00
$record = array (
2009-03-10 09:45:32 +00:00
'field_id' => $instance [ 'field_id' ],
2009-02-05 03:42:58 +00:00
'field_name' => $instance [ 'field_name' ],
'bundle' => $instance [ 'bundle' ],
'widget_type' => $instance [ 'widget' ][ 'type' ],
'widget_module' => $widget_module ,
'widget_active' => $widget_active ,
'data' => $data ,
'deleted' => $instance [ 'deleted' ],
);
// We need to tell drupal_update_record() the primary keys to trigger an
// update.
2009-03-10 09:45:32 +00:00
if ( $update ) {
$record [ 'id' ] = $instance [ 'id' ];
$primary_key = array ( 'id' );
2009-05-20 09:48:47 +00:00
}
else {
2009-03-10 09:45:32 +00:00
$primary_key = array ();
}
drupal_write_record ( 'field_config_instance' , $record , $primary_key );
2009-02-05 03:42:58 +00:00
}
/**
2009-02-10 03:16:15 +00:00
* Read a single instance record directly from the database . Generally ,
2009-02-05 03:42:58 +00:00
* you should use the field_info_instance () instead .
*
2009-05-28 10:05:32 +00:00
* This function will not return deleted instances . Use
* field_read_instances () instead for this purpose .
*
2009-02-05 03:42:58 +00:00
* @ param $field_name
* The field name to read .
* @ param $bundle
* The bundle to which the field is bound .
* @ param array $include_additional
* The default behavior of this function is to not return an instance that
2009-05-28 10:05:32 +00:00
* is inactive . Setting
* $include_additional [ 'include_inactive' ] to TRUE will override this
2009-02-05 03:42:58 +00:00
* behavior .
* @ return
* An instance structure , or FALSE .
*/
function field_read_instance ( $field_name , $bundle , $include_additional = array ()) {
$instances = field_read_instances ( array ( 'field_name' => $field_name , 'bundle' => $bundle ), $include_additional );
return $instances ? current ( $instances ) : FALSE ;
}
/**
* Read in field instances that match an array of conditions .
*
* @ param $param
* An array of properties to use in selecting a field
* instance . Valid keys include any column of the
2009-02-10 03:16:15 +00:00
* field_config_instance table . If NULL , all instances will be returned .
2009-02-05 03:42:58 +00:00
* @ param $include_additional
* The default behavior of this function is to not return field
* instances that are inactive or have been marked deleted . Setting
* $include_additional [ 'include_inactive' ] or
* $include_additional [ 'include_deleted' ] to TRUE will override this
* behavior .
* @ return
* An array of instances matching the arguments .
*/
function field_read_instances ( $params = array (), $include_additional = array ()) {
$query = db_select ( 'field_config_instance' , 'fci' , array ( 'fetch' => PDO :: FETCH_ASSOC ));
2009-03-10 09:45:32 +00:00
$query -> join ( 'field_config' , 'fc' , 'fc.id = fci.field_id' );
2009-02-05 03:42:58 +00:00
$query -> fields ( 'fci' );
// Turn the conditions into a query.
foreach ( $params as $key => $value ) {
$query -> condition ( 'fci.' . $key , $value );
}
if ( ! isset ( $include_additional [ 'include_inactive' ]) || ! $include_additional [ 'include_inactive' ]) {
2009-08-19 13:31:14 +00:00
$query -> condition ( 'fc.active' , 1 );
2009-02-05 03:42:58 +00:00
$query -> condition ( 'fci.widget_active' , 1 );
}
if ( ! isset ( $include_additional [ 'include_deleted' ]) || ! $include_additional [ 'include_deleted' ]) {
$query -> condition ( 'fc.deleted' , 0 );
$query -> condition ( 'fci.deleted' , 0 );
}
$instances = array ();
$results = $query -> execute ();
foreach ( $results as $record ) {
$instance = unserialize ( $record [ 'data' ]);
2009-03-10 09:45:32 +00:00
$instance [ 'id' ] = $record [ 'id' ];
$instance [ 'field_id' ] = $record [ 'field_id' ];
2009-02-05 03:42:58 +00:00
$instance [ 'field_name' ] = $record [ 'field_name' ];
$instance [ 'bundle' ] = $record [ 'bundle' ];
$instance [ 'deleted' ] = $record [ 'deleted' ];
$instance [ 'widget' ][ 'type' ] = $record [ 'widget_type' ];
$instance [ 'widget' ][ 'module' ] = $record [ 'widget_module' ];
$instance [ 'widget' ][ 'active' ] = $record [ 'widget_active' ];
module_invoke_all ( 'field_read_instance' , $instance );
$instances [] = $instance ;
}
return $instances ;
}
/**
* Mark a field instance for deletion , including all data associated with
* it .
*
* @ param $field_name
* The name of the field whose instance will be deleted .
* @ param $bundle
* The bundle for the instance which will be deleted .
*/
function field_delete_instance ( $field_name , $bundle ) {
2009-09-09 11:37:34 +00:00
// Save the instance for hook_field_delete_instance before it is deleted.
$instance = field_info_instance ( $field_name , $bundle );
2009-02-05 03:42:58 +00:00
// Mark the field instance for deletion.
db_update ( 'field_config_instance' )
-> fields ( array ( 'deleted' => 1 ))
-> condition ( 'field_name' , $field_name )
-> 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 );
// Clear the cache.
field_cache_clear ();
2009-09-09 11:37:34 +00:00
module_invoke_all ( 'field_delete_instance' , $instance );
2009-02-05 03:42:58 +00:00
}
/**
* @ } End of " defgroup field_crud " .
2009-02-08 21:22:59 +00:00
*/
2009-08-11 14:59:40 +00:00
/*
* @ defgroup field_purge Field API bulk data deletion
* @ {
* Clean up after Field API bulk deletion operations .
*
* Field API provides functions for deleting data attached to individual
* objects as well as deleting entire fields or field instances in a single
* operation .
*
* Deleting field data items for an object with field_attach_delete () involves
* three separate operations :
* - Invoking the Field Type API hook_field_delete () for each field on the
* object . The hook for each field type receives the object and the specific
* field being deleted . A file field module might use this hook to delete
* uploaded files from the filesystem .
* - Invoking the Field Storage API hook_field_storage_delete () to remove
* data from the primary field storage . The hook implementation receives the
* object being deleted and deletes data for all of the object 's bundle' s
* fields .
* - Invoking the global Field Attach API hook_field_attach_delete () for all
* modules that implement it . Each hook implementation receives the object
* being deleted and can operate on whichever subset of the object 's bundle' s
* fields it chooses to .
*
* These hooks are invoked immediately when field_attach_delete () is
* called . Similar operations are performed for field_attach_delete_revision () .
*
* When a field , bundle , or field instance is deleted , it is not practical to
* invoke these hooks immediately on every affected object in a single page
* request ; there could be thousands or millions of them . Instead , the
* appropriate field data items , instances , and / or fields are marked as deleted
* so that subsequent load or query operations will not return them . Later , a
* separate process cleans up , or " purges " , the marked - as - deleted data by going
* through the three - step process described above and , finally , removing
* deleted field and instance records .
*
* Purging field data is made somewhat tricky by the fact that , while
* field_attach_delete () has a complete object to pass to the various deletion
* hooks , the Field API purge process only has the field data it has previously
* stored . It cannot reconstruct complete original objects to pass to the
* deletion hooks . It is even possible that the original object to which some
* Field API data was attached has been itself deleted before the field purge
* operation takes place .
*
* Field API resolves this problem by using " pseudo-objects " during purge
* operations . A pseudo - object contains only the information from the original
* object that Field API knows about : entity type , id , revision id , and
* bundle . It also contains the field data for whichever field instance is
* currently being purged . For example , suppose that the node type 'story' used
* to contain a field called 'subtitle' but the field was deleted . If node 37
* was a story with a subtitle , the pseudo - object passed to the purge hooks
* would look something like this :
*
* @ code
* $obj = stdClass Object (
* [ nid ] => 37 ,
* [ vid ] => 37 ,
* [ type ] => 'story' ,
* [ subtitle ] => array (
* [ 0 ] => array (
* 'value' => 'subtitle text' ,
* ),
* ),
* );
* @ endcode
*/
/**
* Purge some deleted Field API data , instances , or fields .
*
* This function will purge deleted field data on up to a specified maximum
* number of objects and then return . If a deleted field instance with no
* remaining data records is found , the instance itself will be purged .
* If a deleted field with no remaining field instances is found , the field
* itself will be purged .
*
* @ param $batch_size
* The maximum number of field data records to purge before returning .
*/
function field_purge_batch ( $batch_size ) {
// Retrieve all deleted field instances. We cannot use field_info_instances()
// because that function does not return deleted instances.
$instances = field_read_instances ( array ( 'deleted' => 1 ), array ( 'include_deleted' => 1 ));
foreach ( $instances as $instance ) {
$field = field_info_field_by_id ( $instance [ 'field_id' ]);
// Retrieve some pseudo-objects.
$obj_types = field_attach_query ( $instance [ 'field_id' ], array ( array ( 'bundle' , $instance [ 'bundle' ]), array ( 'deleted' , 1 )), $batch_size );
if ( count ( $obj_types ) > 0 ) {
// Field data for the instance still exists.
foreach ( $obj_types as $obj_type => $objects ) {
field_attach_load ( $obj_type , $objects , FIELD_LOAD_CURRENT , array ( 'field_id' => $field [ 'id' ], 'deleted' => 1 ));
foreach ( $objects as $id => $object ) {
// field_attach_query() may return more results than we asked for.
// Stop when he have done our batch size.
if ( $batch_size -- <= 0 ) {
return ;
}
// Purge the data for the object.
field_purge_data ( $obj_type , $object , $field , $instance );
}
}
}
else {
// No field data remains for the instance, so we can remove it.
field_purge_instance ( $instance );
}
}
// Retrieve all deleted fields. Any that have no bundles can be purged.
$fields = field_read_fields ( array ( 'deleted' => 1 ), array ( 'include_deleted' => 1 ));
foreach ( $fields as $field ) {
// field_read_fields() does not return $field['bundles'] which we need.
$field = field_info_field_by_id ( $field [ 'id' ]);
if ( ! isset ( $field [ 'bundles' ]) || count ( $field [ 'bundles' ]) == 0 ) {
field_purge_field ( $field );
}
}
}
/**
* Purge the field data for a single field on a single pseudo - object .
*
* This is basically the same as field_attach_delete () except it only applies
* to a single field . The object itself is not being deleted , and it is quite
* possible that other field data will remain attached to it .
*
* @ param $obj_type
* The type of $object ; e . g . 'node' or 'user' .
* @ param $object
* The pseudo - object whose field data to delete .
* @ param $field
* The ( possibly deleted ) field whose data is being purged .
* @ param $instance
* The deleted field instance whose data is being purged .
*/
function field_purge_data ( $obj_type , $object , $field , $instance ) {
// Each field type's hook_field_delete() only expects to operate on a single
// field at a time, so we can use it as-is for purging.
$options = array ( 'field_id' => $instance [ 'field_id' ], 'deleted' => TRUE );
_field_invoke ( 'delete' , $obj_type , $object , $dummy , $dummy , $options );
// Tell the field storage system to purge the data.
module_invoke ( variable_get ( 'field_storage_module' , 'field_sql_storage' ), 'field_storage_purge' , $obj_type , $object , $field , $instance );
// Let other modules act on purging the data.
foreach ( module_implements ( 'field_attach_purge' ) as $module ) {
$function = $module . '_field_attach_purge' ;
$function ( $obj_type , $object , $field , $instance );
}
}
/**
* Purge a field instance record from the database .
*
* This function assumes all data for the instance has already been purged , and
* should only be called by field_purge_batch () .
*
* @ param $instance
* The instance record to purge .
*/
function field_purge_instance ( $instance ) {
db_delete ( 'field_config_instance' )
-> condition ( 'id' , $instance [ 'id' ])
-> execute ();
// Notify the storage engine.
module_invoke ( variable_get ( 'field_storage_module' , 'field_sql_storage' ), 'field_storage_purge_instance' , $instance );
// Clear the cache.
2009-09-07 15:49:01 +00:00
field_info_cache_clear ();
2009-08-11 14:59:40 +00:00
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all ( 'field_purge_instance' , $instance );
}
/**
* Purge a field record from the database .
*
* This function assumes all instances for the field has already been purged ,
* and should only be called by field_purge_batch () .
*
* @ param $field
* The field record to purge .
*/
function field_purge_field ( $field ) {
$instances = field_read_instances ( array ( 'field_id' => $field [ 'id' ]), array ( 'include_deleted' => 1 ));
if ( count ( $instances ) > 0 ) {
throw new FieldException ( " Attempt to purge a field that still has instances. " );
}
db_delete ( 'field_config' )
-> condition ( 'id' , $field [ 'id' ])
-> execute ();
// Notify the storage engine.
module_invoke ( variable_get ( 'field_storage_module' , 'field_sql_storage' ), 'field_storage_purge_field' , $field );
// Clear the cache.
2009-09-07 15:49:01 +00:00
field_info_cache_clear ();
2009-08-11 14:59:40 +00:00
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all ( 'field_purge_field' , $field );
}
/**
* @ } End of " defgroup field_purge " .
*/