$field_config['entity_type'] . '.' . $field_config['name'], 'uuid' => $uuid->generate(), 'cardinality' => 1, 'translatable' => FALSE, 'locked' => FALSE, 'settings' => array(), 'indexes' => array(), 'active' => TRUE, 'status' => 1, 'langcode' => 'und', ); // Save in config. \Drupal::config('field.field.' . $field_config['id']) ->setData($field_config) ->save(); // Create storage for the field. This requires a field entity, but cannot use // the regular entity_create() function here. $schema = FieldableDatabaseStorageController::_fieldSqlSchema(new Field($field_config), $field_config['schema']); foreach ($schema as $name => $table) { db_create_table($name, $table); } } /** * Writes a field instance directly to configuration. * * Upgrades using this function need to use hook_update_dependencies() to ensure * they get executed after field_update_8003(). * * @param array $field_config * An array of field properties. * @param array $instance_config * An array of field instance properties. * * @ingroup update_api */ function _update_8003_field_create_instance(array $field_config, array &$instance_config) { $uuid = \Drupal::service('uuid'); // Merge in defaults. $instance_config += array( 'id' => $instance_config['entity_type'] . '.' . $instance_config['bundle'] . '.' . $field_config['name'], 'description' => '', 'required' => FALSE, 'uuid' => $uuid->generate(), 'field_uuid' => $field_config['uuid'], 'field_type' => $field_config['type'], 'default_value' => array(), 'default_value_function' => '', 'settings' => array(), 'widget' => array(), 'status' => 1, 'langcode' => 'und', ); // Save in config. \Drupal::config('field.instance.' . $instance_config['id']) ->setData($instance_config) ->save(); } /** * Writes field data directly to SQL storage. * * @param string $entity_type * The entity type. * @param string $bundle * The bundle. * @param int $entity_id * The entity ID. * @param int $revision_id * The entity revision ID. * @param string $field_name * The field name. * @param array $data * The field values, as an array keyed by langcode, delta and property name. * * @ingroup update_api */ function _update_8006_field_write_data_sql($entity_type, $bundle, $entity_id, $revision_id, $field_name, array $data) { if (!isset($revision_id)) { $revision_id = $entity_id; } $field_config = \Drupal::config("field.field.$entity_type.$field_name"); $field = new Field($field_config); $table_name = FieldableDatabaseStorageController::_fieldTableName($field); $revision_name = FieldableDatabaseStorageController::_fieldRevisionTableName($field); db_delete($table_name) ->condition('entity_id', $entity_id) ->execute(); db_delete($revision_name) ->condition('entity_id', $entity_id) ->condition('revision_id', $revision_id) ->execute(); $columns = array(); foreach ($data as $langcode => $items) { foreach ($items as $delta => $item) { $record = array( 'entity_id' => $entity_id, 'revision_id' => $revision_id, 'bundle' => $bundle, 'delta' => $delta, 'langcode' => $langcode, ); foreach ($item as $column => $value) { $record[FieldableDatabaseStorageController::_fieldColumnName($field_name, $column)] = $value; } $records[] = $record; // Record the columns used. $columns += $record; } } if ($columns) { $query = db_insert($table_name)->fields(array_keys($columns)); $revision_query = db_insert($revision_name)->fields(array_keys($columns)); foreach ($records as $record) { $query->values($record); $revision_query->values($record); } $query->execute(); $revision_query->execute(); } } /** * @addtogroup updates-7.x-to-8.x * @{ */ /** * Empty update - moved into field_update_8003(). */ function field_update_8001() { } /** * Migrate all instance widget and display settings to configuration. * * @ingroup config_upgrade */ function field_update_8002() { $form_displays = $displays = array(); module_load_install('entity'); $query = db_select('field_config_instance', 'fc')->fields('fc'); foreach ($query->execute() as $record) { // Unserialize the data array and start investigating the display key // which holds the configuration of this instance for all view modes. $data = unserialize($record->data); // Skip field instances that were created directly with the new API earlier // in the upgrade path. if (!isset($data['widget']) && !isset($data['display'])) { continue; } // Do not place deleted fields in entity displays. if (empty($record->deleted)) { // Migrate 'widget' settings. if (isset($data['widget'])) { $widget_options = $data['widget']; // Determine name and create initial entry in the $form_displays array. $form_display_id = $record->entity_type . '.' . $record->bundle . '.default'; if (!isset($form_displays[$form_display_id])) { $form_displays[$form_display_id] = _update_8000_entity_get_form_display($record->entity_type, $record->bundle, 'default'); } // We do not need the 'module' key anymore. unset($widget_options['module']); $form_displays[$form_display_id]->set("content.$record->field_name", $widget_options); } // Migrate 'display' settings. if (isset($data['display'])) { foreach ($data['display'] as $view_mode => $display_options) { // Determine name and create initial entry in the $displays array if it // does not exist yet. $display_id = $record->entity_type . '.' . $record->bundle . '.' . $view_mode; if (!isset($displays[$display_id])) { $displays[$display_id] = _update_8000_entity_get_display($record->entity_type, $record->bundle, $view_mode); } // The display object does not store hidden fields. if ($display_options['type'] != 'hidden') { // We do not need the 'module' key anymore. unset($display_options['module']); $displays[$display_id]->set("content.$record->field_name", $display_options); } } } } // Remove the 'widget' and 'display' keys and save the record back into the // table. unset($data['display'], $data['widget']); db_update('field_config_instance') ->condition('id', $record->id) ->fields(array( 'data' => serialize($data), )) ->execute(); } // Migration of the content of the old 'bundle_settings_*'' variables. // Avoid calling entity_get_info() by fetching the relevant variables // directly in the variable table. $variables = array_map('unserialize', db_query("SELECT name, value FROM {variable} WHERE name LIKE '%field_bundle_settings_%'")->fetchAllKeyed()); foreach ($variables as $variable_name => $variable_value) { if (preg_match('/field_bundle_settings_(.*)__(.*)/', $variable_name, $matches)) { $entity_type = $matches[1]; $bundle = $matches[2]; // Migrate form settings for extra fields. if (isset($variable_value['extra_fields']['form'])) { foreach ($variable_value['extra_fields']['form'] as $field_name => $field_settings) { // Determine name and create initial entry in the $form_displays // array if it does not exist yet. $form_display_id = $entity_type . '.' . $bundle . '.default'; if (!isset($form_displays[$form_display_id])) { $form_displays[$form_display_id] = _update_8000_entity_get_form_display($entity_type, $bundle, 'default'); } $form_displays[$form_display_id]->set("content.$field_name", $field_settings); } } // Migrate display settings for extra fields. if (isset($variable_value['extra_fields']['display'])) { foreach ($variable_value['extra_fields']['display'] as $field_name => $field_settings) { foreach ($field_settings as $view_mode => $display_options) { // Determine name and create initial entry in the $displays array // if it does not exist yet. $display_id = $entity_type . '.' . $bundle . '.' . $view_mode; if (!isset($displays[$display_id])) { $displays[$display_id] = _update_8000_entity_get_display($entity_type, $bundle, $view_mode); } // Set options in the display. $new_options = array('visible' => $display_options['visible']); // The display object only stores the weight for 'visible' extra // fields. if ($display_options['visible']) { $new_options['weight'] = $display_options['weight']; } $displays[$display_id]->set("content.$field_name", $new_options); } } } // Migrate view mode settings. if (isset($variable_value['view_modes'])) { foreach ($variable_value['view_modes'] as $view_mode => $view_mode_settings) { // Determine name and create initial entry in the $displays array // if it does not exist yet. $display_id = $entity_type . '.' . $bundle . '.' . $view_mode; if (!isset($displays[$display_id])) { $displays[$display_id] = _update_8000_entity_get_display($entity_type, $bundle, $view_mode); } $displays[$display_id]->set('status', $view_mode_settings['custom_settings']); } } // Remove the variable. update_variable_del($variable_name); } } // Save the form displays to configuration. foreach ($form_displays as $config) { $config->save(); } // Save the displays to configuration. foreach ($displays as $config) { $config->save(); } } /** * Convert fields and instances to config. */ function field_update_8003() { $uuid = \Drupal::service('uuid'); $state = \Drupal::state(); $deleted_fields = $state->get('field.field.deleted') ?: array(); $deleted_instances = $state->get('field.instance.deleted') ?: array(); $field_data = array(); // Migrate field definitions. $records = db_query("SELECT DISTINCT entity_type, fc.* FROM {field_config} fc INNER JOIN {field_config_instance} fci ON fc.id = fci.field_id")->fetchAll(PDO::FETCH_ASSOC); foreach ($records as $record) { $record['data'] = unserialize($record['data']); $config = array( 'id' => $record['entity_type'] . '.' . $record['field_name'], 'name' => $record['field_name'], 'uuid' => $uuid->generate(), 'type' => $record['type'], 'module' => $record['module'], 'active' => $record['active'], 'entity_type' => $record['entity_type'], 'settings' => $record['data']['settings'], 'locked' => $record['locked'], 'cardinality' => $record['cardinality'], 'translatable' => $record['translatable'], 'indexes' => $record['data']['indexes'] ?: array(), 'status' => 1, 'langcode' => 'und', ); // Reassign all list.module fields to be controlled by options.module. if ($config['module'] == 'list') { $config['module'] = 'options'; } // Save in either config or state. if (!$record['deleted']) { \Drupal::config('field.field.' . $config['id']) ->setData($config) ->save(); } else { $config['deleted'] = TRUE; $deleted_fields[$config['uuid']] = $config; // This will not be saved but the DatabaseStorageController helpers need // the field object. $field = new Field($config); // Additionally, rename the data tables for deleted fields. $tables = array( "field_deleted_data_{$record['id']}" => 'old_' . FieldableDatabaseStorageController::_fieldTableName($field), "field_deleted_revision_{$record['id']}" => 'old_' . FieldableDatabaseStorageController::_fieldRevisionTableName($field), ); foreach ($tables as $table_old => $table_new) { if (db_table_exists($table_old)) { db_rename_table($table_old, $table_new); } } } // Store the UUID and field type, they will be used when processing // instances. $field_data[$record['entity_type'] . ':' . $record['id']] = array( 'uuid' => $config['uuid'], 'type' => $record['type'], ); } // Migrate instance definitions. $records = db_query("SELECT * FROM {field_config_instance}")->fetchAll(PDO::FETCH_ASSOC); foreach ($records as $record) { $record['data'] = unserialize($record['data']); $field_data_key = $record['entity_type'] . ':' . $record['field_id']; $config = array( 'id' => $record['entity_type'] . '.' . $record['bundle'] . '.' . $record['field_name'], 'uuid' => $uuid->generate(), 'field_uuid' => $field_data[$field_data_key]['uuid'], 'field_type' => $field_data[$field_data_key]['type'], 'entity_type' => $record['entity_type'], 'bundle' => $record['bundle'], 'label' => $record['data']['label'], 'description' => $record['data']['description'], 'required' => $record['data']['required'], 'default_value' => isset($record['data']['default_value']) ? $record['data']['default_value'] : array(), 'default_value_function' => isset($record['data']['default_value_function']) ? $record['data']['default_value_function'] : '', 'settings' => $record['data']['settings'], 'status' => 1, 'langcode' => 'und', ); // Save in either config or state. if (!$record['deleted']) { \Drupal::config('field.instance.' . $config['id']) ->setData($config) ->save(); } else { $config['deleted'] = TRUE; $deleted_instances[$config['uuid']] = $config; } // Update {file_usage} table in case this instance has a default image. if (!empty($config['settings']['default_image'])) { db_update('file_usage') ->fields(array('id' => $config['field_uuid'])) ->condition('type', 'default_image') ->condition('module', 'image') ->condition('id', $record['field_id']) ->condition('fid', $config['settings']['default_image']) ->execute(); } } // Save the deleted fields and instances in state. $state->set('field.field.deleted', $deleted_fields); $state->set('field.instance.deleted', $deleted_instances); } /** * Moves field_language_fallback to config. * * @ingroup config_upgrade */ function field_update_8004() { // Do nothing: the former update code has been moved to locale_update_8018(). } /** * Removes the 'user_register_form' setting from field instances and populates * the user.user.register form display. */ function field_update_8005() { $uuid = \Drupal::service('uuid'); $user_default_form_display = \Drupal::config('entity.form_display.user.user.default'); $user_register_config_data = array( 'id' => 'user.user.register', 'uuid' => $uuid->generate(), 'targetEntityType' => 'user', 'bundle' => 'user', 'mode' => 'register', 'content' => array(), ); foreach (config_get_storage_names_with_prefix('field.instance') as $config_id) { $instance_config = \Drupal::config($config_id); if ($instance_config->get('entity_type') == 'user' && $instance_config->get('settings.user_register_form')) { list(, , $field_id) = explode('.', $instance_config->get('id')); $user_register_config_data['content'][$field_id] = $user_default_form_display->get('content.' . $field_id); } $settings = $instance_config->get('settings'); unset($settings['user_register_form']); $instance_config->set('settings', $settings)->save(); } // Save the new 'register' form display. \Drupal::config('entity.form_display.user.user.register') ->setData($user_register_config_data) ->save(); } /** * Splits the field storage tables by entity type and also migrate langcode. */ function field_update_8006(&$sandbox) { // Get field definitions from config, and deleted fields from state system. $config_names = config_get_storage_names_with_prefix('field.field'); $deleted_fields = \Drupal::state()->get('field.field.deleted') ?: array(); // Ditch UUID keys, we will iterate through deleted fields using a numeric // index. $deleted_fields = array_values($deleted_fields); if (empty($config_names) && empty($deleted_fields)) { return; } if (!isset($sandbox['index'])) { $sandbox['index'] = 0; $sandbox['max'] = count($config_names) + count($deleted_fields); } // Retrieve the next field definition. When the index exceeds the number of // 'configuration' fields, use it to iterate on deleted fields. if (isset($config_names[$sandbox['index']])) { $field_config = \Drupal::config($config_names[$sandbox['index']])->get(); } else { $field_config = $deleted_fields[$sandbox['index'] - count($config_names)]; } // Prepare updated schema data structures. $field = new Field($field_config); $tables = array( array( 'old_table' => 'field_data_' . $field_config['name'], 'new_table' => FieldableDatabaseStorageController::_fieldTableName($field), 'primary_key' => array( 'entity_id', 'deleted', 'delta', 'langcode', ), ), array( 'old_table' => 'field_revision_' . $field_config['name'], 'new_table' => FieldableDatabaseStorageController::_fieldRevisionTableName($field), 'primary_key' => array( 'entity_id', 'revision_id', 'deleted', 'delta', 'langcode', ), ), ); // Move the field data to the new table. foreach ($tables as $table_data) { // Split data from the old "per field" table to the new "per entity type and // field" table. $new_table = $table_data['new_table']; $original_table = empty($field_config['deleted']) ? $table_data['old_table'] : 'old_' . $new_table; if (db_table_exists($original_table)) { // Create the new table, with the same schema as the old one. if (!db_table_exists($new_table)) { db_copy_table_schema($original_table, $new_table); } // Copy relevant rows. $from_query = db_select($original_table, 'original') ->fields('original') ->condition('entity_type', $field_config['entity_type']); db_insert($new_table) ->from($from_query) ->execute(); // Drop the 'entity_type' column and previous primary key. db_drop_primary_key($new_table); db_drop_field($new_table, 'entity_type'); // Rename 'language' to 'langcode'. Tables created during the upgrade // before this update function might already have the langcode column. if (db_field_exists($new_table, 'language')) { db_drop_index($new_table, 'language'); db_change_field($new_table, 'language', 'langcode', array( 'type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => '', )); db_add_index($new_table, 'langcode', array('langcode')); } // Create new primary key. db_add_primary_key($new_table, $table_data['primary_key']); } } $sandbox['index']++; $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']); } /** * @} End of "addtogroup updates-7.x-to-8.x". * The next series of updates should start at 9000. */