diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index 6b172dd..60be42d 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -6,6 +6,187 @@ */ /** + * Extends DrupalCacheArray to allow dynamic building of the field info cache. + */ +class FieldInfoFieldCache extends DrupalCacheArray { + protected $fields_by_id; + protected $field_ids; + + public function __construct() { + // We explicitely do not call the parent constructor, because we handle + // persistent caching differently. + $this->field_ids = array(); + $this->fields_by_id = array(); + $this->bin = 'cache_field'; + } + + public function offsetGet($offset) { + if (isset($this->storage[$offset]) || array_key_exists($offset, $this->storage)) { + $value = $this->storage[$offset]; + } + elseif ($cached = cache_get('info_fields:' . $offset, $this->bin)) { + $value = $cached->data; + // Extract the 'fields' part and store them globally. + $this->fields_by_id += $value['fields_by_id']; + // @todo : kind of absurd, we're readding it below. + unset($value['fields']); + $this->storage[$offset] = $value; + } + else { + $value = $this->resolveCacheMiss($offset); + } + + // Additionally, populate actual fields from ids. + foreach ($value['field_ids'] as $field_id) { + $value['fields_by_id'][$field_id] = $this->fields_by_id[$field_id]; + } + + return $value; + } + + protected function resolveCacheMiss($offset) { + list($entity_type, $bundle) = explode(':', $offset); + $value = array( + 'instances' => array(), + 'field_ids' => array(), + ); + + $fields = field_read_fields(array(), array('include_deleted' => 1)); + $field_bundles = array(); + + foreach (field_read_instances() as $instance) { + $field_id = $instance['field_id']; + $field_name = $instance['field_name']; + + if ($instance['entity_type'] == $entity_type && $instance['bundle'] == $bundle) { + + // Populate the global array of fields and field ids. + if (!isset($this->fields_by_id[$field_id])) { + $field = _field_info_prepare_field($fields[$field_id]); + $this->fields_by_id[$field_id] = $field; + if (!$field['deleted']) { + $this->field_ids[$field_name] = $field_id; + } + } + else { + $field = $this->fields_by_id[$field_id]; + } + + $instance = _field_info_prepare_instance($instance, $field); + $value['instances'][$field_name] = $instance; + // @todo : key by field name ? + $value['field_ids'][] = $field_id; + } + + // Collect the list of bundles for all fields. + $field_bundles[$field_id][$instance['entity_type']][] = $instance['bundle']; + } + + // Enrich field definitions with the list of bundles where they have + // instances. Note: Deleted fields are not enriched because all of their + // instances are deleted, too, and are thus not iterated here. + foreach ($value['field_ids'] as $field_id) { + $this->fields_by_id[$field_id]['bundles'] = $field_bundles[$field_id]; + } + + $this->storage[$offset] = $value; + $this->persist($offset); + + return $value; + } + + public function __destruct() { + foreach ($this->keysToPersist as $offset => $persist) { + if ($persist) { + $value = $this->storage[$offset]; + // Add field definitions from the global list of fields. + foreach ($value['field_ids'] as $field_id) { + $value['fields_by_id'][$field_id] = $this->fields_by_id[$field_id]; + // @todo : persist $this->field_ids ? + } + $this->set('info_fields:' . $offset, $value, $this->bin); + } + } + } + + /** + * Don't look at that, this is the code from current _field_info_collate_fields(). + */ + function dummy_old_code() { + $definitions = array( + 'field_ids' => field_read_fields(array(), array('include_deleted' => 1)), + 'instances' => field_read_instances(), + ); + + // Populate 'fields' with all fields, keyed by ID. + $info['fields'] = array(); + foreach ($definitions['field_ids'] as $key => $field) { + $info['fields'][$key] = $definitions['field_ids'][$key] = _field_info_prepare_field($field); + } + + // Build an array of field IDs for non-deleted fields, keyed by name. + $info['field_ids'] = array(); + foreach ($info['fields'] as $key => $field) { + if (!$field['deleted']) { + $info['field_ids'][$field['field_name']] = $key; + } + } + + // Populate 'instances'. Only non-deleted instances are considered. + $info['instances'] = array(); + foreach (field_info_bundles() as $entity_type => $bundles) { + foreach ($bundles as $bundle => $bundle_info) { + $info['instances'][$entity_type][$bundle] = array(); + } + } + foreach ($definitions['instances'] as $instance) { + $field = $info['fields'][$instance['field_id']]; + $instance = _field_info_prepare_instance($instance, $field); + $info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance; + // Enrich field definitions with the list of bundles where they have + // instances. NOTE: Deleted fields in $info['field_ids'] are not + // enriched because all of their instances are deleted, too, and + // are thus not in $definitions['instances']. + $info['fields'][$instance['field_id']]['bundles'][$instance['entity_type']][] = $instance['bundle']; + } + + // Populate 'extra_fields'. + $extra = module_invoke_all('field_extra_fields'); + drupal_alter('field_extra_fields', $extra); + // Merge in saved settings. + foreach ($extra as $entity_type => $bundles) { + foreach ($bundles as $bundle => $extra_fields) { + $extra_fields = _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle); + $info['extra_fields'][$entity_type][$bundle] = $extra_fields; + } + } + } +} + +function _field_info_field_cache($reset = FALSE) { + static $info; + + if ($reset) { + $info = NULL; + } + elseif (!isset($info)) { + $info = new FieldInfoFieldCache(); + } + + return $info; +} + +function dummy_test() { + $info = _field_info_field_cache(); + dsm($info['node:article'], '$info[node:article]'); + dsm(cache_get('info_fields:node:article', 'cache_field'), 'cache node:article'); + dsm($info, '$info'); + dsm($info['node:page'], '$info[node:page]'); + dsm(cache_get('info_fields:node:page', 'cache_field'), 'cache node:page'); + dsm($info, '$info'); +} + +/** * @defgroup field_info Field Info API * @{ * Obtain information about Field API configuration. @@ -178,6 +359,7 @@ function _field_info_collate_types($reset = FALSE) { */ function _field_info_collate_fields($reset = FALSE) { static $info; + static $schema; if ($reset) { $info = NULL; @@ -587,6 +769,7 @@ function field_info_bundles($entity_type = NULL) { * which this field belongs keyed by entity type. */ function field_info_fields() { + // Do not persist anything ? $fields = array(); $info = _field_info_collate_fields(); foreach ($info['fields'] as $key => $field) { @@ -613,6 +796,8 @@ function field_info_fields() { * @see field_info_field_by_id() */ function field_info_field($field_name) { + // Persist ? + $info = _field_info_collate_fields(); if (isset($info['field_ids'][$field_name])) { return $info['fields'][$info['field_ids'][$field_name]]; @@ -634,6 +819,8 @@ function field_info_field($field_name) { * @see field_info_field() */ function field_info_field_by_id($field_id) { + // Persist ? + $info = _field_info_collate_fields(); if (isset($info['fields'][$field_id])) { return $info['fields'][$field_id]; @@ -655,6 +842,8 @@ function field_info_field_by_id($field_id) { * @see field_info_field_by_id() */ function field_info_field_by_ids() { + // Do not persist anything ? + $info = _field_info_collate_fields(); return $info['fields']; } @@ -674,6 +863,8 @@ function field_info_field_by_ids() { * all instances for that bundle. */ function field_info_instances($entity_type = NULL, $bundle_name = NULL) { + // If entity type + bundle : persist in the cache. + $info = _field_info_collate_fields(); if (!isset($entity_type)) { return $info['instances']; @@ -698,6 +889,8 @@ function field_info_instances($entity_type = NULL, $bundle_name = NULL) { * The bundle name for the instance. */ function field_info_instance($entity_type, $field_name, $bundle_name) { + // Persist the instance and field definition. + $info = _field_info_collate_fields(); if (isset($info['instances'][$entity_type][$bundle_name][$field_name])) { return $info['instances'][$entity_type][$bundle_name][$field_name]; diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc index 9cd5ee1..e51f38a 100644 --- a/modules/field_ui/field_ui.admin.inc +++ b/modules/field_ui/field_ui.admin.inc @@ -595,6 +595,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle // Add settings for the update selects behavior. $js_fields = array(); foreach ($existing_field_options as $field_name => $fields) { + // @todo : we probably want to avoid filling the field / instance caches here + // -> use field_info_fields() / field_info_instances() ? $field = field_info_field($field_name); $instance = field_info_instance($form['#entity_type'], $field_name, $form['#bundle']); $js_fields[$field_name] = array('label' => $instance['label'], 'type' => $field['type'], 'widget' => $instance['widget']['type']); @@ -1486,6 +1488,7 @@ function field_ui_existing_field_options($entity_type, $bundle) { // - fields that that shoud not be added via user interface. if (empty($field['locked']) + // @todo watch this. && !field_info_instance($entity_type, $field['field_name'], $bundle) && (empty($field['entity_types']) || in_array($entity_type, $field['entity_types'])) && empty($field_types[$field['type']]['no_ui'])) {