2009-02-05 03:42:58 +00:00
|
|
|
<?php
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
|
2013-01-07 11:22:28 +00:00
|
|
|
use Drupal\Core\Entity\EntityInterface;
|
2013-04-13 17:06:40 +00:00
|
|
|
use Drupal\field\Plugin\Core\Entity\Field;
|
|
|
|
use Drupal\field\Plugin\Core\Entity\FieldInstance;
|
2012-05-07 02:56:49 +00:00
|
|
|
use Drupal\field\FieldException;
|
|
|
|
|
2009-02-05 03:42:58 +00:00
|
|
|
/**
|
|
|
|
* @defgroup field_crud Field CRUD API
|
|
|
|
* @{
|
2012-09-27 15:44:17 +00:00
|
|
|
* Creates, updates, and deletes Field API fields, bundles, and instances.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* Modules use this API, often in hook_install(), to create custom data
|
|
|
|
* structures. UI modules will use it to create a user interface.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* The Field CRUD API uses @link field Field API data structures @endlink.
|
2011-11-30 02:42:42 +00:00
|
|
|
*
|
2011-12-22 09:32:53 +00:00
|
|
|
* See @link field Field API @endlink for information about the other parts of
|
|
|
|
* the Field API.
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Creates a field.
|
2009-05-20 09:48:47 +00:00
|
|
|
*
|
|
|
|
* This function does not bind the field to any bundle; use
|
|
|
|
* field_create_instance() for that.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @param array $field
|
|
|
|
* A field definition. The field_name and type properties are required.
|
2009-05-20 09:48:47 +00:00
|
|
|
* 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().
|
- Patch #443422 by yched, bjaspan | chx, merlinofchaos, Scott Reynolds, plach, profix898, mattyoung: added support for pluggable 'per field' storage engine. Comes with documentation and tests.
The Field Attach API uses the Field Storage API to perform all "database access". Each Field Storage API hook function defines a primitive database operation such as read, write, or delete. The default field storage module, field_sql_storage.module, uses the local SQL database to implement these operations, but alternative field storage backends can choose to represent the data in SQL differently or use a completely different storage mechanism such as a cloud-based database.
2009-09-27 12:52:55 +00:00
|
|
|
* - storage:
|
2013-04-22 21:57:38 +00:00
|
|
|
* - type: the storage backend specified in the
|
|
|
|
* 'field.settings.default_storage' configuration.
|
- Patch #443422 by yched, bjaspan | chx, merlinofchaos, Scott Reynolds, plach, profix898, mattyoung: added support for pluggable 'per field' storage engine. Comes with documentation and tests.
The Field Attach API uses the Field Storage API to perform all "database access". Each Field Storage API hook function defines a primitive database operation such as read, write, or delete. The default field storage module, field_sql_storage.module, uses the local SQL database to implement these operations, but alternative field storage backends can choose to represent the data in SQL differently or use a completely different storage mechanism such as a cloud-based database.
2009-09-27 12:52:55 +00:00
|
|
|
* - settings: each omitted setting is given the default value specified in
|
|
|
|
* hook_field_storage_info().
|
2011-12-09 15:32:17 +00:00
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @return \Drupal\field\Plugin\Core\Entity\Field
|
|
|
|
* The field entity.
|
2011-12-09 15:32:17 +00:00
|
|
|
*
|
2012-05-07 02:56:49 +00:00
|
|
|
* @throws Drupal\field\FieldException
|
2010-05-09 13:29:06 +00:00
|
|
|
*
|
2013-04-22 11:33:00 +00:00
|
|
|
* @deprecated as of Drupal 8.0. Use
|
|
|
|
* entity_create('field_entity', $definition)->save().
|
|
|
|
*
|
2011-01-02 17:26:40 +00:00
|
|
|
* See: @link field Field API data structures @endlink.
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
2013-04-13 17:06:40 +00:00
|
|
|
function field_create_field(array $field) {
|
|
|
|
$field = entity_create('field_entity', $field);
|
|
|
|
$field->save();
|
2009-05-28 10:05:32 +00:00
|
|
|
return $field;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
2009-12-03 02:30:18 +00:00
|
|
|
/**
|
|
|
|
* Updates a field.
|
2009-09-26 15:57:39 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @param mixed $field
|
|
|
|
* Either the \Drupal\field\Plugin\Core\Entity\Field object to update, or a
|
|
|
|
* field array structure. If the latter, $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.
|
2012-05-07 02:56:49 +00:00
|
|
|
*
|
|
|
|
* @throws Drupal\field\FieldException
|
|
|
|
*
|
2013-04-22 11:33:00 +00:00
|
|
|
* @deprecated as of Drupal 8.0. Use $field->save().
|
|
|
|
*
|
2009-09-26 15:57:39 +00:00
|
|
|
* @see field_create_field()
|
|
|
|
*/
|
|
|
|
function field_update_field($field) {
|
2013-04-13 17:06:40 +00:00
|
|
|
// Module developers can still pass in an array of properties.
|
|
|
|
if (is_array($field)) {
|
|
|
|
$field_loaded = entity_load('field_entity', $field['field_name']);
|
|
|
|
if (empty($field_loaded)) {
|
|
|
|
throw new FieldException('Attempt to update a non-existent field.');
|
|
|
|
}
|
|
|
|
// Merge incoming values.
|
|
|
|
foreach ($field as $key => $value) {
|
|
|
|
$field_loaded[$key] = $value;
|
|
|
|
}
|
|
|
|
$field = $field_loaded;
|
2009-09-26 15:57:39 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
$field->save();
|
2009-09-26 15:57:39 +00:00
|
|
|
}
|
|
|
|
|
2009-02-05 03:42:58 +00:00
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Reads a single field record directly from the database.
|
|
|
|
*
|
|
|
|
* Generally, you should use the field_info_field() instead.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* This function will not return deleted fields. Use field_read_fields() instead
|
|
|
|
* for this purpose.
|
2009-05-28 10:05:32 +00:00
|
|
|
*
|
2009-02-05 03:42:58 +00:00
|
|
|
* @param $field_name
|
|
|
|
* The field name to read.
|
|
|
|
* @param array $include_additional
|
2012-09-27 15:44:17 +00:00
|
|
|
* The default behavior of this function is to not return a field that is
|
|
|
|
* inactive. Setting $include_additional['include_inactive'] to TRUE will
|
|
|
|
* override this behavior.
|
|
|
|
*
|
2009-02-05 03:42:58 +00:00
|
|
|
* @return
|
2010-01-03 23:31:17 +00:00
|
|
|
* A field definition array, or FALSE.
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Reads in fields that match an array of conditions.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @param array $conditions
|
|
|
|
* An array of conditions to match against. Keys are names of properties found
|
|
|
|
* in field configuration files, and values are conditions to match.
|
2009-02-05 03:42:58 +00:00
|
|
|
* @param array $include_additional
|
2012-09-27 15:44:17 +00:00
|
|
|
* The default behavior of this function is to not return fields that are
|
|
|
|
* inactive or have been deleted. Setting
|
2009-02-05 03:42:58 +00:00
|
|
|
* $include_additional['include_inactive'] or
|
2012-09-27 15:44:17 +00:00
|
|
|
* $include_additional['include_deleted'] to TRUE will override this behavior.
|
|
|
|
*
|
2009-02-05 03:42:58 +00:00
|
|
|
* @return
|
2009-05-28 10:05:32 +00:00
|
|
|
* An array of fields matching $params. If
|
2012-09-27 15:44:17 +00:00
|
|
|
* $include_additional['include_deleted'] is TRUE, the array is keyed by
|
|
|
|
* field ID, otherwise it is keyed by field name.
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
2013-04-13 17:06:40 +00:00
|
|
|
function field_read_fields($conditions = array(), $include_additional = array()) {
|
|
|
|
// Include inactive fields if specified in the $include_additional parameter.
|
|
|
|
$include_inactive = isset($include_additional['include_inactive']) && $include_additional['include_inactive'];
|
|
|
|
// Include deleted fields if specified either in the $include_additional or
|
|
|
|
// the $conditions parameters.
|
|
|
|
$include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']) || (isset($conditions['deleted']) && $conditions['deleted']);
|
2012-09-28 22:11:59 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Get fields stored in configuration.
|
|
|
|
if (isset($conditions['field_name'])) {
|
|
|
|
// Optimize for the most frequent case where we do have a specific ID.
|
|
|
|
$fields = entity_load_multiple('field_entity', array($conditions['field_name']));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// No specific ID, we need to examine all existing fields.
|
|
|
|
$fields = entity_load_multiple('field_entity');
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
2012-09-28 22:11:59 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Merge deleted fields (stored in state) if needed.
|
|
|
|
if ($include_deleted) {
|
|
|
|
$deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
|
|
|
|
foreach ($deleted_fields as $id => $config) {
|
|
|
|
$fields[$id] = entity_create('field_entity', $config);
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
// Translate "do not include inactive instances" into actual conditions.
|
|
|
|
if (!$include_inactive) {
|
2013-04-20 03:47:11 +00:00
|
|
|
$conditions['active'] = TRUE;
|
|
|
|
$conditions['storage.active'] = TRUE;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Collect matching fields.
|
|
|
|
$matching_fields = array();
|
|
|
|
foreach ($fields as $field) {
|
|
|
|
foreach ($conditions as $key => $value) {
|
|
|
|
// Extract the actual value against which the condition is checked.
|
|
|
|
switch ($key) {
|
|
|
|
case 'storage.active':
|
|
|
|
$checked_value = $field->storage['active'];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'field_name';
|
|
|
|
$checked_value = $field->id;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
$checked_value = $field->$key;
|
|
|
|
break;
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Skip to the next field as soon as one condition does not match.
|
|
|
|
if ($checked_value != $value) {
|
|
|
|
continue 2;
|
|
|
|
}
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
module_invoke_all('field_read_field', $field);
|
2009-02-05 03:42:58 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// When returning deleted fields, key the results by UUID since they can
|
|
|
|
// include several fields with the same ID.
|
|
|
|
$key = $include_deleted ? $field->uuid : $field->id;
|
|
|
|
$matching_fields[$key] = $field;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
return $matching_fields;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Marks a field and its instances and data for deletion.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
|
|
|
* @param $field_name
|
|
|
|
* The field name to delete.
|
2013-04-22 11:33:00 +00:00
|
|
|
*
|
|
|
|
* @deprecated as of Drupal 8.0. Use $field->delete().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
function field_delete_field($field_name) {
|
2013-04-13 17:06:40 +00:00
|
|
|
if ($field = field_info_field($field_name)) {
|
|
|
|
$field->delete();
|
2009-09-09 11:37:34 +00:00
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates an instance of a field, binding it to a bundle.
|
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @param array $instance
|
2010-03-27 05:52:50 +00:00
|
|
|
* A field instance definition array. The field_name, entity_type and
|
2009-10-15 12:44:36 +00:00
|
|
|
* bundle properties are required. Other properties, if omitted,
|
|
|
|
* will be given the following default values:
|
2009-05-20 09:48:47 +00:00
|
|
|
* - label: the field name
|
|
|
|
* - description: empty string
|
|
|
|
* - 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().
|
2010-05-23 19:10:23 +00:00
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @return \Drupal\field\Plugin\Core\Entity\FieldInstance
|
|
|
|
* The field instance entity.
|
2011-12-09 15:32:17 +00:00
|
|
|
*
|
2012-05-07 02:56:49 +00:00
|
|
|
* @throws Drupal\field\FieldException
|
2010-05-09 13:29:06 +00:00
|
|
|
*
|
2013-04-22 11:33:00 +00:00
|
|
|
* @deprecated as of Drupal 8.0. Use
|
|
|
|
* entity_create('field_instance', $definition)->save().
|
|
|
|
*
|
2011-01-02 17:26:40 +00:00
|
|
|
* See: @link field Field API data structures @endlink.
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
2013-04-13 17:06:40 +00:00
|
|
|
function field_create_instance(array $instance) {
|
|
|
|
$instance = entity_create('field_instance', $instance);
|
|
|
|
$instance->save();
|
2009-05-28 10:05:32 +00:00
|
|
|
return $instance;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
2009-12-03 02:30:18 +00:00
|
|
|
/**
|
|
|
|
* Updates an instance of a field.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @param mixed $instance
|
|
|
|
* Either the \Drupal\field\Plugin\Core\Entity\FieldInstance to update, or an
|
|
|
|
* associative array representing an instance structure. If the latter, the
|
|
|
|
* required keys and values are:
|
2010-10-25 00:09:12 +00:00
|
|
|
* - entity_type: The type of the entity the field is attached to.
|
|
|
|
* - bundle: The bundle this field belongs to.
|
|
|
|
* - field_name: The name of an existing field.
|
2013-04-23 14:42:44 +00:00
|
|
|
* The other array elements represent properties of the instance, and all
|
|
|
|
* properties must be specified or their default values will be used (except
|
|
|
|
* internal-use properties, which are assigned automatically). To avoid losing
|
|
|
|
* the previously stored properties of the instance when making a change,
|
|
|
|
* first load the instance with field_info_instance(), then override the
|
|
|
|
* values you want to override, and finally save using this function. Example:
|
|
|
|
* @code
|
|
|
|
* // Fetch an instance info array.
|
|
|
|
* $instance_info = field_info_instance($entity_type, $field_name, $bundle_name);
|
|
|
|
* // Change a single property in the instance definition.
|
|
|
|
* $instance_info['definition']['required'] = TRUE;
|
|
|
|
* // Write the changed definition back.
|
|
|
|
* field_update_instance($instance_info['definition']);
|
|
|
|
* @endcode
|
2010-10-25 00:09:12 +00:00
|
|
|
*
|
2012-05-07 02:56:49 +00:00
|
|
|
* @throws Drupal\field\FieldException
|
2010-10-25 00:09:12 +00:00
|
|
|
*
|
2013-04-22 11:33:00 +00:00
|
|
|
* @deprecated as of Drupal 8.0. Use $instance->save().
|
|
|
|
*
|
2013-04-23 14:42:44 +00:00
|
|
|
* @see field_info_instance()
|
2009-02-05 03:42:58 +00:00
|
|
|
* @see field_create_instance()
|
|
|
|
*/
|
|
|
|
function field_update_instance($instance) {
|
2013-04-13 17:06:40 +00:00
|
|
|
// Module developers can still pass in an array of properties.
|
|
|
|
if (is_array($instance)) {
|
|
|
|
$instance_loaded = entity_load('field_instance', $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name']);
|
|
|
|
if (empty($instance_loaded)) {
|
|
|
|
throw new FieldException('Attempt to update a non-existent instance.');
|
|
|
|
}
|
|
|
|
// Merge incoming values.
|
|
|
|
foreach ($instance as $key => $value) {
|
|
|
|
$instance_loaded[$key] = $value;
|
|
|
|
}
|
|
|
|
$instance = $instance_loaded;
|
2012-09-26 19:39:39 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
$instance->save();
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-02-19 01:09:31 +00:00
|
|
|
* Reads a single instance record from the database.
|
2009-12-03 02:30:18 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* Generally, you should use field_info_instance() instead, as it provides
|
|
|
|
* caching and allows other modules the opportunity to append additional
|
|
|
|
* formatters, widgets, and other information.
|
2009-05-28 10:05:32 +00:00
|
|
|
*
|
2010-02-11 17:44:47 +00:00
|
|
|
* @param $entity_type
|
2010-02-12 05:38:10 +00:00
|
|
|
* The type of entity to which the field is bound.
|
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
|
2012-09-27 15:44:17 +00:00
|
|
|
* The default behavior of this function is to not return an instance that has
|
|
|
|
* been deleted, or whose field is inactive. Setting
|
2009-10-01 19:16:57 +00:00
|
|
|
* $include_additional['include_inactive'] or
|
2012-09-27 15:44:17 +00:00
|
|
|
* $include_additional['include_deleted'] to TRUE will override this behavior.
|
|
|
|
*
|
2009-02-05 03:42:58 +00:00
|
|
|
* @return
|
|
|
|
* An instance structure, or FALSE.
|
|
|
|
*/
|
2010-02-11 17:44:47 +00:00
|
|
|
function field_read_instance($entity_type, $field_name, $bundle, $include_additional = array()) {
|
2010-03-27 05:52:50 +00:00
|
|
|
$instances = field_read_instances(array('entity_type' => $entity_type, 'field_name' => $field_name, 'bundle' => $bundle), $include_additional);
|
2009-02-05 03:42:58 +00:00
|
|
|
return $instances ? current($instances) : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Reads in field instances that match an array of conditions.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
|
|
|
* @param $param
|
2013-04-13 17:06:40 +00:00
|
|
|
* An array of properties to use in selecting a field instance. Keys are names
|
|
|
|
* of properties found in field instance configuration files, and values are
|
|
|
|
* conditions to match.
|
2009-02-05 03:42:58 +00:00
|
|
|
* @param $include_additional
|
2012-09-27 15:44:17 +00:00
|
|
|
* The default behavior of this function is to not return field instances that
|
|
|
|
* have been marked deleted, or whose field is inactive. Setting
|
|
|
|
* $include_additional['include_inactive'] or
|
|
|
|
* $include_additional['include_deleted'] to TRUE will override this behavior.
|
|
|
|
*
|
2009-02-05 03:42:58 +00:00
|
|
|
* @return
|
|
|
|
* An array of instances matching the arguments.
|
|
|
|
*/
|
2013-04-13 17:06:40 +00:00
|
|
|
function field_read_instances($conditions = array(), $include_additional = array()) {
|
|
|
|
// Include instances of inactive fields if specified in the
|
|
|
|
// $include_additional parameter.
|
2010-02-07 05:12:35 +00:00
|
|
|
$include_inactive = isset($include_additional['include_inactive']) && $include_additional['include_inactive'];
|
2013-04-13 17:06:40 +00:00
|
|
|
// Include deleted instances if specified either in the $include_additional
|
|
|
|
// or the $conditions parameters.
|
|
|
|
$include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']) || (isset($conditions['deleted']) && $conditions['deleted']);
|
2010-02-07 05:12:35 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Get instances stored in configuration.
|
|
|
|
if (isset($conditions['entity_type']) && isset($conditions['bundle']) && isset($conditions['field_name'])) {
|
|
|
|
// Optimize for the most frequent case where we do have a specific ID.
|
|
|
|
$instances = entity_load_multiple('field_instance', array($conditions['entity_type'] . '.' . $conditions['bundle'] . '.' . $conditions['field_name']));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// No specific ID, we need to examine all existing instances.
|
|
|
|
$instances = entity_load_multiple('field_instance');
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Merge deleted instances (stored in state) if needed.
|
|
|
|
if ($include_deleted) {
|
|
|
|
$state = Drupal::state();
|
|
|
|
$deleted_fields = $state->get('field.field.deleted');
|
|
|
|
$deleted_instances = $state->get('field.instance.deleted') ?: array();
|
|
|
|
foreach ($deleted_instances as $id => $config) {
|
|
|
|
$instances[$id] = entity_create('field_instance', $config);
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
// Translate "do not include inactive fields" into actual conditions.
|
2010-02-07 05:12:35 +00:00
|
|
|
if (!$include_inactive) {
|
2013-04-20 03:47:11 +00:00
|
|
|
$conditions['field.active'] = TRUE;
|
|
|
|
$conditions['field.storage.active'] = TRUE;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
// Collect matching instances.
|
|
|
|
$matching_instances = array();
|
|
|
|
foreach ($instances as $instance) {
|
|
|
|
// Only include instances on unknown entity types if 'include_inactive'.
|
|
|
|
if (!$include_inactive && !entity_get_info($instance->entity_type)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-04-21 19:21:47 +00:00
|
|
|
// Some conditions are checked against the field.
|
|
|
|
$field = $instance->getField();
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
// Only keep the instance if it matches all conditions.
|
|
|
|
foreach ($conditions as $key => $value) {
|
|
|
|
// Extract the actual value against which the condition is checked.
|
|
|
|
switch ($key) {
|
2013-04-21 19:21:47 +00:00
|
|
|
case 'field_name':
|
|
|
|
$checked_value = $field->id;
|
|
|
|
break;
|
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
case 'field.active':
|
|
|
|
$checked_value = $field->active;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'field.storage.active':
|
|
|
|
$checked_value = $field->storage['active'];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'field_id':
|
|
|
|
$checked_value = $instance->field_uuid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
$checked_value = $instance->$key;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip to the next instance as soon as one condition does not match.
|
|
|
|
if ($checked_value != $value) {
|
|
|
|
continue 2;
|
|
|
|
}
|
2010-02-07 05:12:35 +00:00
|
|
|
}
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
module_invoke_all('field_read_instance', $instance);
|
|
|
|
|
|
|
|
$matching_instances[] = $instance;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
return $matching_instances;
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Marks a field instance and its data for deletion.
|
2009-02-05 03:42:58 +00:00
|
|
|
*
|
2013-04-13 17:06:40 +00:00
|
|
|
* @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance
|
|
|
|
* The field instance.
|
2010-12-15 04:13:48 +00:00
|
|
|
* @param $field_cleanup
|
|
|
|
* If TRUE, the field will be deleted as well if its last instance is being
|
2011-09-24 20:44:06 +00:00
|
|
|
* deleted. If FALSE, it is the caller's responsibility to handle the case of
|
2010-12-15 04:13:48 +00:00
|
|
|
* fields left without instances. Defaults to TRUE.
|
2013-04-22 11:33:00 +00:00
|
|
|
*
|
|
|
|
* @deprecated as of Drupal 8.0. Use $instance->delete().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
2013-04-13 17:06:40 +00:00
|
|
|
function field_delete_instance(FieldInstance $instance, $field_cleanup = TRUE) {
|
|
|
|
$instance->delete($field_cleanup);
|
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
|
|
|
|
2009-12-03 02:30:18 +00:00
|
|
|
/**
|
2009-08-11 14:59:40 +00:00
|
|
|
* @defgroup field_purge Field API bulk data deletion
|
|
|
|
* @{
|
2012-09-27 15:44:17 +00:00
|
|
|
* Cleans up after Field API bulk deletion operations.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
|
|
|
* Field API provides functions for deleting data attached to individual
|
2010-02-12 05:38:10 +00:00
|
|
|
* entities as well as deleting entire fields or field instances in a single
|
2009-08-11 14:59:40 +00:00
|
|
|
* operation.
|
|
|
|
*
|
2010-02-12 05:38:10 +00:00
|
|
|
* Deleting field data items for an entity with field_attach_delete() involves
|
2009-08-11 14:59:40 +00:00
|
|
|
* three separate operations:
|
|
|
|
* - Invoking the Field Type API hook_field_delete() for each field on the
|
2012-09-27 15:44:17 +00:00
|
|
|
* entity. The hook for each field type receives the entity 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 entity
|
|
|
|
* being deleted and deletes data for all of the entity's bundle's fields.
|
2009-08-11 14:59:40 +00:00
|
|
|
* - Invoking the global Field Attach API hook_field_attach_delete() for all
|
2012-09-27 15:44:17 +00:00
|
|
|
* modules that implement it. Each hook implementation receives the entity
|
|
|
|
* being deleted and can operate on whichever subset of the entity's bundle's
|
|
|
|
* fields it chooses to.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* These hooks are invoked immediately when field_attach_delete() is called.
|
|
|
|
* Similar operations are performed for field_attach_delete_revision().
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
|
|
|
* When a field, bundle, or field instance is deleted, it is not practical to
|
2010-02-12 05:38:10 +00:00
|
|
|
* invoke these hooks immediately on every affected entity in a single page
|
2009-08-11 14:59:40 +00:00
|
|
|
* 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
|
2012-09-27 15:44:17 +00:00
|
|
|
* through the three-step process described above and, finally, removing deleted
|
|
|
|
* field and instance records.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
|
|
|
* Purging field data is made somewhat tricky by the fact that, while
|
2010-02-12 05:38:10 +00:00
|
|
|
* field_attach_delete() has a complete entity to pass to the various deletion
|
2009-08-11 14:59:40 +00:00
|
|
|
* hooks, the Field API purge process only has the field data it has previously
|
2010-02-12 05:38:10 +00:00
|
|
|
* stored. It cannot reconstruct complete original entities to pass to the
|
|
|
|
* deletion hooks. It is even possible that the original entity to which some
|
2009-08-11 14:59:40 +00:00
|
|
|
* Field API data was attached has been itself deleted before the field purge
|
|
|
|
* operation takes place.
|
|
|
|
*
|
2010-02-12 05:38:10 +00:00
|
|
|
* Field API resolves this problem by using "pseudo-entities" during purge
|
|
|
|
* operations. A pseudo-entity contains only the information from the original
|
2012-09-27 15:44:17 +00:00
|
|
|
* entity 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-entity passed to the purge hooks would look
|
|
|
|
* something like this:
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
|
|
|
* @code
|
2010-02-12 05:38:10 +00:00
|
|
|
* $entity = stdClass Object(
|
2009-08-11 14:59:40 +00:00
|
|
|
* [nid] => 37,
|
|
|
|
* [vid] => 37,
|
|
|
|
* [type] => 'story',
|
|
|
|
* [subtitle] => array(
|
|
|
|
* [0] => array(
|
|
|
|
* 'value' => 'subtitle text',
|
|
|
|
* ),
|
|
|
|
* ),
|
|
|
|
* );
|
|
|
|
* @endcode
|
2011-11-30 02:42:42 +00:00
|
|
|
*
|
2011-12-22 09:32:53 +00:00
|
|
|
* See @link field Field API @endlink for information about the other parts of
|
|
|
|
* the Field API.
|
2009-08-11 14:59:40 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Purges a batch of deleted Field API data, instances, or fields.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
2012-03-23 19:45:06 +00:00
|
|
|
* This function will purge deleted field data in batches. The batch size
|
|
|
|
* is defined as an argument to the function, and once each batch is finished,
|
|
|
|
* it continues with the next batch until all have completed. 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.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
|
|
|
* @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.
|
2013-04-20 03:47:11 +00:00
|
|
|
$instances = field_read_instances(array('deleted' => TRUE), array('include_deleted' => TRUE));
|
2013-04-11 12:55:05 +00:00
|
|
|
$factory = Drupal::service('entity.query');
|
2012-10-30 10:41:42 +00:00
|
|
|
$info = entity_get_info();
|
2009-08-11 14:59:40 +00:00
|
|
|
foreach ($instances as $instance) {
|
2012-10-30 10:41:42 +00:00
|
|
|
$entity_type = $instance['entity_type'];
|
2013-04-13 17:06:40 +00:00
|
|
|
|
|
|
|
// EntityFieldQuery currently fails on conditions on comment bundle.
|
|
|
|
// Remove when http://drupal.org/node/731724 is fixed.
|
|
|
|
if ($entity_type == 'comment') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-10-30 10:41:42 +00:00
|
|
|
$ids = (object) array(
|
|
|
|
'entity_type' => $entity_type,
|
|
|
|
'bundle' => $instance['bundle'],
|
|
|
|
);
|
2010-06-14 15:41:03 +00:00
|
|
|
// field_purge_data() will need the field array.
|
2009-08-11 14:59:40 +00:00
|
|
|
$field = field_info_field_by_id($instance['field_id']);
|
2010-06-14 15:41:03 +00:00
|
|
|
// Retrieve some entities.
|
2013-01-11 02:12:12 +00:00
|
|
|
$query = $factory->get($entity_type)
|
2013-04-13 17:06:40 +00:00
|
|
|
->condition('id:' . $field['uuid'] . '.deleted', 1)
|
2013-01-11 02:12:12 +00:00
|
|
|
->range(0, $batch_size);
|
|
|
|
// If there's no bundle key, all results will have the same bundle.
|
|
|
|
if (!empty($info[$entity_type]['entity_keys']['bundle'])) {
|
|
|
|
$query->condition($info[$entity_type]['entity_keys']['bundle'], $ids->bundle);
|
|
|
|
}
|
|
|
|
$results = $query->execute();
|
2009-08-11 14:59:40 +00:00
|
|
|
|
2010-06-14 15:41:03 +00:00
|
|
|
if ($results) {
|
2012-10-30 10:41:42 +00:00
|
|
|
$entities = array();
|
|
|
|
foreach ($results as $revision_id => $entity_id) {
|
|
|
|
// This might not be the revision id if the entity type does not support
|
|
|
|
// revisions but _field_create_entity_from_ids() checks that and
|
|
|
|
// disregards this value so there is no harm setting it.
|
|
|
|
$ids->revision_id = $revision_id;
|
|
|
|
$ids->entity_id = $entity_id;
|
|
|
|
$entities[$entity_id] = _field_create_entity_from_ids($ids);
|
|
|
|
}
|
2013-04-13 17:06:40 +00:00
|
|
|
field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('field_id' => $field['uuid'], 'deleted' => 1));
|
2012-10-30 10:41:42 +00:00
|
|
|
foreach ($entities as $entity) {
|
|
|
|
// Purge the data for the entity.
|
2013-01-07 11:22:28 +00:00
|
|
|
field_purge_data($entity, $field, $instance);
|
2009-08-11 14:59:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// No field data remains for the instance, so we can remove it.
|
|
|
|
field_purge_instance($instance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-15 04:13:48 +00:00
|
|
|
// Retrieve all deleted fields. Any that have no instances can be purged.
|
2013-04-13 17:06:40 +00:00
|
|
|
$deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
|
|
|
|
foreach ($deleted_fields as $field) {
|
|
|
|
$field = new Field($field);
|
|
|
|
$instances = field_read_instances(array('field_id' => $field['uuid']), array('include_deleted' => 1));
|
2010-12-15 04:13:48 +00:00
|
|
|
if (empty($instances)) {
|
2009-08-11 14:59:40 +00:00
|
|
|
field_purge_field($field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-02-12 05:38:10 +00:00
|
|
|
* Purges the field data for a single field on a single pseudo-entity.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* This is basically the same as field_attach_delete() except it only applies to
|
|
|
|
* a single field. The entity itself is not being deleted, and it is quite
|
2009-08-11 14:59:40 +00:00
|
|
|
* possible that other field data will remain attached to it.
|
|
|
|
*
|
2013-01-07 11:22:28 +00:00
|
|
|
* @param \Drupal\Core\Entity\EntityInterface $entity
|
2010-04-24 07:19:09 +00:00
|
|
|
* The pseudo-entity whose field data is being purged.
|
2009-08-11 14:59:40 +00:00
|
|
|
* @param $field
|
|
|
|
* The (possibly deleted) field whose data is being purged.
|
|
|
|
* @param $instance
|
|
|
|
* The deleted field instance whose data is being purged.
|
|
|
|
*/
|
2013-01-07 11:22:28 +00:00
|
|
|
function field_purge_data(EntityInterface $entity, $field, $instance) {
|
2009-08-11 14:59:40 +00:00
|
|
|
// 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);
|
2013-01-07 11:22:28 +00:00
|
|
|
_field_invoke('delete', $entity, $dummy, $dummy, $options);
|
2009-08-11 14:59:40 +00:00
|
|
|
|
|
|
|
// Tell the field storage system to purge the data.
|
2013-01-07 11:22:28 +00:00
|
|
|
module_invoke($field['storage']['module'], 'field_storage_purge', $entity, $field, $instance);
|
2009-08-11 14:59:40 +00:00
|
|
|
|
|
|
|
// Let other modules act on purging the data.
|
|
|
|
foreach (module_implements('field_attach_purge') as $module) {
|
|
|
|
$function = $module . '_field_attach_purge';
|
2013-01-07 11:22:28 +00:00
|
|
|
$function($entity, $field, $instance);
|
2009-08-11 14:59:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Purges a field instance record from the database.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
2012-09-27 15:44:17 +00:00
|
|
|
* This function assumes all data for the instance has already been purged and
|
2009-08-11 14:59:40 +00:00
|
|
|
* should only be called by field_purge_batch().
|
|
|
|
*
|
|
|
|
* @param $instance
|
|
|
|
* The instance record to purge.
|
|
|
|
*/
|
|
|
|
function field_purge_instance($instance) {
|
|
|
|
// Notify the storage engine.
|
- Patch #443422 by yched, bjaspan | chx, merlinofchaos, Scott Reynolds, plach, profix898, mattyoung: added support for pluggable 'per field' storage engine. Comes with documentation and tests.
The Field Attach API uses the Field Storage API to perform all "database access". Each Field Storage API hook function defines a primitive database operation such as read, write, or delete. The default field storage module, field_sql_storage.module, uses the local SQL database to implement these operations, but alternative field storage backends can choose to represent the data in SQL differently or use a completely different storage mechanism such as a cloud-based database.
2009-09-27 12:52:55 +00:00
|
|
|
$field = field_info_field_by_id($instance['field_id']);
|
|
|
|
module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance);
|
2009-08-11 14:59:40 +00:00
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
$state = Drupal::state();
|
|
|
|
$deleted_instances = $state->get('field.instance.deleted');
|
|
|
|
unset($deleted_instances[$instance['uuid']]);
|
|
|
|
$state->set('field.instance.deleted', $deleted_instances);
|
|
|
|
|
2009-08-11 14:59:40 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-03 02:30:18 +00:00
|
|
|
* Purges a field record from the database.
|
2009-08-11 14:59:40 +00:00
|
|
|
*
|
|
|
|
* 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) {
|
2013-04-13 17:06:40 +00:00
|
|
|
$instances = field_read_instances(array('field_id' => $field['uuid']), array('include_deleted' => 1));
|
2009-08-11 14:59:40 +00:00
|
|
|
if (count($instances) > 0) {
|
2010-06-05 12:05:25 +00:00
|
|
|
throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name'])));
|
2009-08-11 14:59:40 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 17:06:40 +00:00
|
|
|
$state = Drupal::state();
|
|
|
|
$deleted_fields = $state->get('field.field.deleted');
|
|
|
|
unset($deleted_fields[$field['uuid']]);
|
|
|
|
$state->set('field.field.deleted', $deleted_fields);
|
2009-08-11 14:59:40 +00:00
|
|
|
|
|
|
|
// Notify the storage engine.
|
- Patch #443422 by yched, bjaspan | chx, merlinofchaos, Scott Reynolds, plach, profix898, mattyoung: added support for pluggable 'per field' storage engine. Comes with documentation and tests.
The Field Attach API uses the Field Storage API to perform all "database access". Each Field Storage API hook function defines a primitive database operation such as read, write, or delete. The default field storage module, field_sql_storage.module, uses the local SQL database to implement these operations, but alternative field storage backends can choose to represent the data in SQL differently or use a completely different storage mechanism such as a cloud-based database.
2009-09-27 12:52:55 +00:00
|
|
|
module_invoke($field['storage']['module'], 'field_storage_purge_field', $field);
|
2009-08-11 14:59:40 +00:00
|
|
|
|
|
|
|
// 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".
|
|
|
|
*/
|