diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php index 262781c..df456ea 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php @@ -262,122 +262,169 @@ public function getExportProperties() { } /** - * {@inheritdoc} + * Overrides \Drupal\Core\Entity\Entity::save(). + * + * @return + * Either SAVED_NEW or SAVED_UPDATED, depending on the operation performed. + * + * @throws \Drupal\field\FieldException + * If the field definition is invalid. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures at the configuration storage level. */ public function save() { - $module_handler = \Drupal::moduleHandler(); - $storage_controller = \Drupal::entityManager()->getStorageController($this->entityType); - // Clear the derived data about the field. unset($this->schema, $this->storageDetails); if ($this->isNew()) { - // Field name cannot be longer than Field::ID_MAX_LENGTH characters. We - // use drupal_strlen() because the DB layer assumes that column widths - // are given in characters rather than bytes. - if (drupal_strlen($this->id) > static::ID_MAX_LENGTH) { - throw new FieldException(format_string( - 'Attempt to create a field with an ID longer than @max characters: %id', array( - '@max' => static::ID_MAX_LENGTH, - '%id' => $this->id, - ) - )); - } + return $this->saveNew(); + } + else { + return $this->saveUpdated(); + } + } - // Ensure the field name is unique (we do not care about deleted fields). - if ($prior_field = current($storage_controller->load(array($this->id)))) { - $message = $prior_field->active ? - 'Attempt to create field name %id which already exists and is active.' : - 'Attempt to create field name %id which already exists, although it is inactive.'; - throw new FieldException(format_string($message, array('%id' => $this->id))); - } + /** + * Saves a new field definition. + * + * @return + * SAVED_NEW if the definition was saved. + * + * @throws \Drupal\field\FieldException + * If the field definition is invalid. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures at the configuration storage level. + */ + protected function saveNew() { + $module_handler = \Drupal::moduleHandler(); + $entity_manager = \Drupal::entityManager(); + $storage_controller = $entity_manager->getStorageController($this->entityType); + + // Field name cannot be longer than Field::ID_MAX_LENGTH characters. We + // use drupal_strlen() because the DB layer assumes that column widths + // are given in characters rather than bytes. + if (drupal_strlen($this->id) > static::ID_MAX_LENGTH) { + throw new FieldException(format_string( + 'Attempt to create a field with an ID longer than @max characters: %id', array( + '@max' => static::ID_MAX_LENGTH, + '%id' => $this->id, + ) + )); + } - // Disallow reserved field names. This can't prevent all field name - // collisions with existing entity properties, but some is better than - // none. - foreach (\Drupal::entityManager()->getDefinitions() as $type => $info) { - if (in_array($this->id, $info['entity_keys'])) { - throw new FieldException(format_string('Attempt to create field %id which is reserved by entity type %type.', array('%id' => $this->id, '%type' => $type))); - } - } + // Ensure the field name is unique (we do not care about deleted fields). + if ($prior_field = current($storage_controller->load(array($this->id)))) { + $message = $prior_field->active ? + 'Attempt to create field name %id which already exists and is active.' : + 'Attempt to create field name %id which already exists, although it is inactive.'; + throw new FieldException(format_string($message, array('%id' => $this->id))); + } - // Check that the field type is known. - $field_type = field_info_field_types($this->type); - if (!$field_type) { - throw new FieldException(format_string('Attempt to create a field of unknown type %type.', array('%type' => $this->type))); + // Disallow reserved field names. This can't prevent all field name + // collisions with existing entity properties, but some is better than + // none. + foreach ($entity_manager->getDefinitions() as $type => $info) { + if (in_array($this->id, $info['entity_keys'])) { + throw new FieldException(format_string('Attempt to create field %id which is reserved by entity type %type.', array('%id' => $this->id, '%type' => $type))); } - $this->module = $field_type['module']; - $this->active = TRUE; - - // Make sure all settings are present, so that a complete field - // definition is passed to the various hooks and written to config. - $this->settings += $field_type['settings']; - - // Provide default storage. - $this->storage += array( - 'type' => config('field.settings')->get('default_storage'), - 'settings' => array(), - ); - // Check that the storage type is known. - $storage_type = field_info_storage_types($this->storage['type']); - if (!$storage_type) { - throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array('%type' => $this->storage['type']))); - } - $this->storage['module'] = $storage_type['module']; - $this->storage['active'] = TRUE; - // Provide default storage settings. - $this->storage['settings'] += $storage_type['settings']; + } - // Invoke the storage backend's hook_field_storage_create_field(). - $module_handler->invoke($this->storage['module'], 'field_storage_create_field', array($this)); + // Check that the field type is known. + $field_type = field_info_field_types($this->type); + if (!$field_type) { + throw new FieldException(format_string('Attempt to create a field of unknown type %type.', array('%type' => $this->type))); + } + $this->module = $field_type['module']; + $this->active = TRUE; - $hook = 'field_create_field'; - $hook_args = array($this); + // Make sure all settings are present, so that a complete field + // definition is passed to the various hooks and written to config. + $this->settings += $field_type['settings']; + + // Provide default storage. + $this->storage += array( + 'type' => variable_get('field_storage_default', 'field_sql_storage'), + 'settings' => array(), + ); + // Check that the storage type is known. + $storage_type = field_info_storage_types($this->storage['type']); + if (!$storage_type) { + throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array('%type' => $this->storage['type']))); } - // Otherwise, the field is being updated. - else { - $original = $storage_controller->loadUnchanged($this->id()); + $this->storage['module'] = $storage_type['module']; + $this->storage['active'] = TRUE; + // Provide default storage settings. + $this->storage['settings'] += $storage_type['settings']; - // Some updates are always disallowed. - if ($this->type != $original->type) { - throw new FieldException("Cannot change an existing field's type."); - } - if ($this->entity_types != $original->entity_types) { - throw new FieldException("Cannot change an existing field's entity_types property."); - } - if ($this->storage['type'] != $original->storage['type']) { - throw new FieldException("Cannot change an existing field's storage type."); - } + // Invoke the storage backend's hook_field_storage_create_field(). + $module_handler->invoke($this->storage['module'], 'field_storage_create_field', array($this)); - // Make sure all settings are present, so that a complete field - // definition is saved. This allows calling code to perform partial - // updates on a field object. - $this->settings += $original->settings; + // Save the configuration. + $result = parent::save(); + field_cache_clear(); - $has_data = field_has_data($this); + // Invoke hook_field_create_field() after the cache is cleared for API + // consistency. + $module_handler->invokeAll('field_create_field', array($this)); - // See if any module forbids the update by throwing an exception. This - // invokes hook_field_update_forbid(). - $module_handler->invokeAll('field_update_forbid', array($this, $original, $has_data)); + return $result; + } - // Tell the storage engine to update the field by invoking the - // hook_field_storage_update_field(). The storage engine can reject the - // definition update as invalid by raising an exception, which stops - // execution before the definition is written to config. - $module_handler->invoke($this->storage['module'], 'field_storage_update_field', array($this, $original, $has_data)); + /** + * Saves an updated field definition. + * + * @return + * SAVED_UPDATED if the definition was saved. + * + * @throws \Drupal\field\FieldException + * If the field definition is invalid. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures at the configuration storage level. + */ + protected function saveUpdated() { + $module_handler = \Drupal::moduleHandler(); + $storage_controller = \Drupal::entityManager()->getStorageController($this->entityType); + + $original = $storage_controller->loadUnchanged($this->id()); - $hook = 'field_update_field'; - $hook_args = array($this, $original, $has_data); + // Some updates are always disallowed. + if ($this->type != $original->type) { + throw new FieldException("Cannot change an existing field's type."); } + if ($this->entity_types != $original->entity_types) { + throw new FieldException("Cannot change an existing field's entity_types property."); + } + if ($this->storage['type'] != $original->storage['type']) { + throw new FieldException("Cannot change an existing field's storage type."); + } + + // Make sure all settings are present, so that a complete field + // definition is saved. This allows calling code to perform partial + // updates on a field object. + $this->settings += $original->settings; + + $has_data = field_has_data($this); + + // See if any module forbids the update by throwing an exception. This + // invokes hook_field_update_forbid(). + $module_handler->invokeAll('field_update_forbid', array($this, $original, $has_data)); + + // Tell the storage engine to update the field by invoking the + // hook_field_storage_update_field(). The storage engine can reject the + // definition update as invalid by raising an exception, which stops + // execution before the definition is written to config. + $module_handler->invoke($this->storage['module'], 'field_storage_update_field', array($this, $original, $has_data)); // Save the configuration. $result = parent::save(); field_cache_clear(); - // Invoke external hooks after the cache is cleared for API consistency. - // This invokes either hook_field_create_field() or - // hook_field_update_field() depending on whether the field is new. - $module_handler->invokeAll($hook, $hook_args); + // Invoke hook_field_update_field() after the cache is cleared for API + // consistency. + $module_handler->invokeAll('field_update_field', array($this, $original, $has_data)); return $result; } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php index 66d8cbf..90adb76 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php @@ -291,69 +291,126 @@ public function getExportProperties() { } /** - * {@inheritdoc} + * Overrides \Drupal\Core\Entity\Entity::save(). + * + * @return + * Either SAVED_NEW or SAVED_UPDATED, depending on the operation performed. + * + * @throws \Drupal\field\FieldException + * If the field instance definition is invalid. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures at the configuration storage level. */ public function save() { - $module_handler = \Drupal::moduleHandler(); - $entity_manager = \Drupal::entityManager(); - $instance_controller = $entity_manager->getStorageController($this->entityType); - if ($this->isNew()) { - // Check that the field can be attached to this entity type. - if (!empty($this->field->entity_types) && !in_array($this->entity_type, $this->field->entity_types)) { - throw new FieldException(format_string('Attempt to create an instance of field @field_id on forbidden entity type @entity_type.', array('@field_id' => $this->field->id, '@entity_type' => $this->entity_type))); - } - - // Assign the ID. - $this->id = $this->id(); + return $this->saveNew(); + } + else { + return $this->saveUpdated(); + } + } - // Ensure the field instance is unique within the bundle. - if ($prior_instance = current($instance_controller->load(array($this->id)))) { - throw new FieldException(format_string('Attempt to create an instance of field @field_id on bundle @bundle that already has an instance of that field.', array('@field_id' => $this->field->id, '@bundle' => $this->bundle))); - } + /** + * Saves a new field instance definition. + * + * @return + * SAVED_NEW if the definition was saved. + * + * @throws \Drupal\field\FieldException + * If the field instance definition is invalid. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures at the configuration storage level. + */ + protected function saveNew() { + $module_handler = \Drupal::moduleHandler(); + $instance_controller = \Drupal::entityManager()->getStorageController($this->entityType); - $hook = 'field_create_instance'; - $hook_args = array($this); + // Check that the field can be attached to this entity type. + if (!empty($this->field->entity_types) && !in_array($this->entity_type, $this->field->entity_types)) { + throw new FieldException(format_string('Attempt to create an instance of field @field_id on forbidden entity type @entity_type.', array('@field_id' => $this->field->id, '@entity_type' => $this->entity_type))); } - // Otherwise, the field instance is being updated. - else { - $original = \Drupal::service('plugin.manager.entity') - ->getStorageController($this->entityType) - ->loadUnchanged($this->getOriginalID()); - // Some updates are always disallowed. - if ($this->entity_type != $original->entity_type) { - throw new FieldException("Cannot change an existing instance's entity_type."); - } - if ($this->bundle != $original->bundle && empty($this->bundle_rename_allowed)) { - throw new FieldException("Cannot change an existing instance's bundle."); - } - if ($this->field_uuid != $original->field_uuid) { - throw new FieldException("Cannot change an existing instance's field."); - } + // Assign the ID. + $this->id = $this->id(); - $hook = 'field_update_instance'; - $hook_args = array($this, $original); + // Ensure the field instance is unique within the bundle. + if ($prior_instance = current($instance_controller->load(array($this->id)))) { + throw new FieldException(format_string('Attempt to create an instance of field @field_id on bundle @bundle that already has an instance of that field.', array('@field_id' => $this->field->id, '@bundle' => $this->bundle))); } - $field_type_info = field_info_field_types($this->field->type); + // Set the field UUID. + $this->field_uuid = $this->field->uuid; - // Set the default instance settings. - $this->settings += $field_type_info['instance_settings']; + // Ensure default values are present. + $this->prepareSave(); // Save the configuration. $result = parent::save(); field_cache_clear(); - // Invoke external hooks after the cache is cleared for API consistency. - // This invokes hook_field_create_instance() or hook_field_update_instance() - // depending on whether the field is new. - $module_handler->invokeAll($hook, $hook_args); + // Invoke hook_field_create_instance() after the cache is cleared for API + // consistency. + $module_handler->invokeAll('field_create_instance', array($this)); return $result; } /** + * Saves an updated field instance definition. + * + * @return + * SAVED_UPDATED if the definition was saved. + * + * @throws \Drupal\field\FieldException + * If the field instance definition is invalid. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures at the configuration storage level. + */ + protected function saveUpdated() { + $module_handler = \Drupal::moduleHandler(); + $instance_controller = \Drupal::entityManager()->getStorageController($this->entityType); + + $original = $instance_controller->loadUnchanged($this->getOriginalID()); + + // Some updates are always disallowed. + if ($this->entity_type != $original->entity_type) { + throw new FieldException("Cannot change an existing instance's entity_type."); + } + if ($this->bundle != $original->bundle && empty($this->bundle_rename_allowed)) { + throw new FieldException("Cannot change an existing instance's bundle."); + } + if ($this->field_uuid != $original->field_uuid) { + throw new FieldException("Cannot change an existing instance's field."); + } + + // Ensure default values are present. + $this->prepareSave(); + + // Save the configuration. + $result = parent::save(); + field_cache_clear(); + + // Invoke hook_field_update_instance() after the cache is cleared for API + // consistency. + $module_handler->invokeAll('field_update_instance', array($this, $original)); + + return $result; + } + + /** + * Prepares the instance definition for saving. + */ + protected function prepareSave() { + $field_type_info = field_info_field_types($this->field->type); + + // Set the default instance settings. + $this->settings += $field_type_info['instance_settings']; + } + + /** * Overrides \Drupal\Core\Entity\Entity::delete(). * * @param bool $field_cleanup