Commit 4831e2ee authored by alexpott's avatar alexpott

Issue #2226267 by fago, xjm, Berdir: Improve default value handling of fields to be consistent.

parent 81d8d75a
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
namespace Drupal\Core\Field; namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition; use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
use Drupal\Core\TypedData\ListDataDefinition; use Drupal\Core\TypedData\ListDataDefinition;
use Drupal\field\FieldException; use Drupal\field\FieldException;
...@@ -378,8 +378,56 @@ public function isDisplayConfigurable($display_context) { ...@@ -378,8 +378,56 @@ public function isDisplayConfigurable($display_context) {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getDefaultValue(EntityInterface $entity) { public function getDefaultValue(ContentEntityInterface $entity) {
return $this->getSetting('default_value'); // Allow custom default values function.
if (isset($this->definition['default_value_callback'])) {
$value = call_user_func($this->definition['default_value_callback'], $entity, $this);
}
else {
$value = isset($this->definition['default_value']) ? $this->definition['default_value'] : NULL;
}
// Allow the field type to process default values.
$field_item_list_class = $this->getClass();
return $field_item_list_class::processDefaultValue($value, $entity, $this);
}
/**
* Sets a custom default value callback.
*
* If set, the callback overrides any set default value.
*
* @param string|array $callback
* The callback to invoke for getting the default value. The callback will
* be invoked with the following arguments:
* - \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being created.
* - \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field definition.
* It should return the default value as documented by
* \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
*
* @return $this
*/
public function setDefaultValueCallback($callback) {
$this->definition['default_value_callback'] = $callback;
return $this;
}
/**
* Sets a default value.
*
* Note that if a default value callback is set, it will take precedence over
* any value set here.
*
* @param mixed $value
* The default value in the format as returned by
* \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
*
* @return $this
*/
public function setDefaultValue($value) {
$this->definition['default_value'] = $value;
return $this;
} }
/** /**
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
namespace Drupal\Core\Field; namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\TypedData\ListDataDefinitionInterface; use Drupal\Core\TypedData\ListDataDefinitionInterface;
/** /**
...@@ -114,19 +114,19 @@ public function isRequired(); ...@@ -114,19 +114,19 @@ public function isRequired();
/** /**
* Returns the default value for the field in a newly created entity. * Returns the default value for the field in a newly created entity.
* *
* @param \Drupal\Core\Entity\EntityInterface $entity * @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being created. * The entity for which the default value is generated.
* *
* @return mixed * @return mixed
* The default value for the field, as accepted by * The default value for the field, as accepted by
* Drupal\field\Plugin\Core\Entity\FieldConfig::setValue(). This can be * \Drupal\field\Plugin\Core\Entity\FieldItemListInterface::setValue(). This
* either: * can be either:
* - a literal, in which case it will be assigned to the first property of * - a literal, in which case it will be assigned to the first property of
* the first item. * the first item.
* - a numerically indexed array of items, each item being a property/value * - a numerically indexed array of items, each item being a property/value
* array. * array.
* - NULL or array() for no default value. * - NULL or array() for no default value.
*/ */
public function getDefaultValue(EntityInterface $entity); public function getDefaultValue(ContentEntityInterface $entity);
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\Core\Field; namespace Drupal\Core\Field;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Core\TypedData\TypedDataInterface;
...@@ -202,7 +203,7 @@ public function defaultAccess($operation = 'view', AccountInterface $account = N ...@@ -202,7 +203,7 @@ public function defaultAccess($operation = 'view', AccountInterface $account = N
* {@inheritdoc} * {@inheritdoc}
*/ */
public function applyDefaultValue($notify = TRUE) { public function applyDefaultValue($notify = TRUE) {
$value = $this->getDefaultValue(); $value = $this->getFieldDefinition()->getDefaultValue($this->getEntity());
// NULL or array() mean "no default value", but 0, '0' and the empty string // NULL or array() mean "no default value", but 0, '0' and the empty string
// are valid default values. // are valid default values.
...@@ -216,16 +217,6 @@ public function applyDefaultValue($notify = TRUE) { ...@@ -216,16 +217,6 @@ public function applyDefaultValue($notify = TRUE) {
return $this; return $this;
} }
/**
* Returns the default value for the field.
*
* @return array
* The default value for the field.
*/
protected function getDefaultValue() {
return $this->getFieldDefinition()->getDefaultValue($this->getEntity());
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -347,6 +338,13 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo ...@@ -347,6 +338,13 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo
return $this->getValue(); return $this->getValue();
} }
/**
* {@inheritdoc}
*/
public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
return $default_value;
}
/** /**
* Returns the widget object used in default value form. * Returns the widget object used in default value form.
* *
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\Core\Field; namespace Drupal\Core\Field;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessibleInterface; use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\TypedData\ListInterface; use Drupal\Core\TypedData\ListInterface;
...@@ -228,4 +229,30 @@ public function defaultValuesFormValidate(array $element, array &$form, array &$ ...@@ -228,4 +229,30 @@ public function defaultValuesFormValidate(array $element, array &$form, array &$
*/ */
public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state); public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state);
/**
* Processes the default value before being applied.
*
* Defined or configured default values of a field might need some processing
* in order to be a valid value for the field type; e.g., a date field could
* process the defined value of 'NOW' to a valid date.
*
* @param mixed
* The default value as defined for the field.
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity for which the default value is generated.
* @param \Drupal\Core\Field\FieldDefinitionInterface $definition
* The definition of the field.
*
* @return mixed
* The default value for the field, as accepted by
* \Drupal\field\Plugin\Core\Entity\FieldItemListInterface::setValue(). This
* can be either:
* - a literal, in which case it will be assigned to the first property of
* the first item.
* - a numerically indexed array of items, each item being a property/value
* array.
* - NULL or array() for no default value.
*/
public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition);
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/** /**
* @file * @file
* Contains \Drupal\Core\Entity\Plugin\Field\FieldType\StringItem. * Contains \Drupal\Core\Field\Plugin\Field\FieldType\StringItem.
*/ */
namespace Drupal\Core\Field\Plugin\Field\FieldType; namespace Drupal\Core\Field\Plugin\Field\FieldType;
......
...@@ -176,12 +176,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -176,12 +176,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['checked'] = FieldDefinition::create('timestamp') $fields['checked'] = FieldDefinition::create('timestamp')
->setLabel(t('Checked')) ->setLabel(t('Checked'))
->setDescription(t('Last time feed was checked for new items, as Unix timestamp.')) ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'))
->setSetting('default_value', 0); ->setDefaultValue(0);
$fields['queued'] = FieldDefinition::create('timestamp') $fields['queued'] = FieldDefinition::create('timestamp')
->setLabel(t('Queued')) ->setLabel(t('Queued'))
->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.')) ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.'))
->setSetting('default_value', 0); ->setDefaultValue(0);
$fields['link'] = FieldDefinition::create('uri') $fields['link'] = FieldDefinition::create('uri')
->setLabel(t('Link')) ->setLabel(t('Link'))
......
...@@ -233,18 +233,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -233,18 +233,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['uid'] = FieldDefinition::create('entity_reference') $fields['uid'] = FieldDefinition::create('entity_reference')
->setLabel(t('User ID')) ->setLabel(t('User ID'))
->setDescription(t('The user ID of the comment author.')) ->setDescription(t('The user ID of the comment author.'))
->setSettings(array( ->setSetting('target_type', 'user')
'target_type' => 'user', ->setDefaultValue(0);
'default_value' => 0,
));
$fields['name'] = FieldDefinition::create('string') $fields['name'] = FieldDefinition::create('string')
->setLabel(t('Name')) ->setLabel(t('Name'))
->setDescription(t("The comment author's name.")) ->setDescription(t("The comment author's name."))
->setSettings(array( ->setSetting('max_length', 60)
'default_value' => '', ->setDefaultValue('')
'max_length' => 60,
))
->addConstraint('CommentName', array()); ->addConstraint('CommentName', array());
$fields['mail'] = FieldDefinition::create('email') $fields['mail'] = FieldDefinition::create('email')
...@@ -274,7 +270,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -274,7 +270,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['status'] = FieldDefinition::create('boolean') $fields['status'] = FieldDefinition::create('boolean')
->setLabel(t('Publishing status')) ->setLabel(t('Publishing status'))
->setDescription(t('A boolean indicating whether the comment is published.')) ->setDescription(t('A boolean indicating whether the comment is published.'))
->setSetting('default_value', TRUE); ->setDefaultValue(TRUE);
$fields['thread'] = FieldDefinition::create('string') $fields['thread'] = FieldDefinition::create('string')
->setLabel(t('Thread place')) ->setLabel(t('Thread place'))
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
namespace Drupal\datetime\Plugin\Field\FieldType; namespace Drupal\datetime\Plugin\Field\FieldType;
use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemList; use Drupal\Core\Field\FieldItemList;
/** /**
...@@ -61,14 +63,14 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo ...@@ -61,14 +63,14 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getDefaultValue() { public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
$default_value = parent::getDefaultValue(); $default_value = parent::processDefaultValue($default_value, $entity, $definition);
if (isset($default_value[0]['default_date']) && $default_value[0]['default_date'] == static::DEFAULT_VALUE_NOW) { if (isset($default_value[0]['default_date']) && $default_value[0]['default_date'] == static::DEFAULT_VALUE_NOW) {
// A default value should be in the format and timezone used for date // A default value should be in the format and timezone used for date
// storage. // storage.
$date = new DrupalDateTime('now', DATETIME_STORAGE_TIMEZONE); $date = new DrupalDateTime('now', DATETIME_STORAGE_TIMEZONE);
$storage_format = $this->getFieldDefinition()->getSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE ? DATETIME_DATE_STORAGE_FORMAT: DATETIME_DATETIME_STORAGE_FORMAT; $storage_format = $definition->getSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE ? DATETIME_DATE_STORAGE_FORMAT: DATETIME_DATETIME_STORAGE_FORMAT;
$value = $date->format($storage_format); $value = $date->format($storage_format);
// We only provide a default value for the first item, as do all fields. // We only provide a default value for the first item, as do all fields.
// Otherwise, there is no way to clear out unwanted values on multiple value // Otherwise, there is no way to clear out unwanted values on multiple value
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
namespace Drupal\entity_reference\Plugin\Field\FieldType; namespace Drupal\entity_reference\Plugin\Field\FieldType;
use Drupal\Core\Field\EntityReferenceFieldItemList; use Drupal\Core\Field\EntityReferenceFieldItemList;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
/** /**
* Represents a configurable entity_reference entity field. * Represents a configurable entity_reference entity field.
...@@ -17,8 +19,8 @@ class ConfigurableEntityReferenceFieldItemList extends EntityReferenceFieldItemL ...@@ -17,8 +19,8 @@ class ConfigurableEntityReferenceFieldItemList extends EntityReferenceFieldItemL
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function getDefaultValue() { public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
$default_value = parent::getDefaultValue(); $default_value = parent::processDefaultValue($default_value, $entity, $definition);
if ($default_value) { if ($default_value) {
// Convert UUIDs to numeric IDs. // Convert UUIDs to numeric IDs.
...@@ -33,7 +35,7 @@ protected function getDefaultValue() { ...@@ -33,7 +35,7 @@ protected function getDefaultValue() {
} }
} }
if ($uuids) { if ($uuids) {
$target_type = $this->getSetting('target_type'); $target_type = $definition->getSetting('target_type');
$entity_ids = \Drupal::entityQuery($target_type) $entity_ids = \Drupal::entityQuery($target_type)
->condition('uuid', $uuids, 'IN') ->condition('uuid', $uuids, 'IN')
->execute(); ->execute();
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
use Drupal\Component\Utility\String; use Drupal\Component\Utility\String;
use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldDefinition; use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition; use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
...@@ -154,14 +154,10 @@ class FieldInstanceConfig extends ConfigEntityBase implements FieldInstanceConfi ...@@ -154,14 +154,10 @@ class FieldInstanceConfig extends ConfigEntityBase implements FieldInstanceConfi
* The name of a callback function that returns default values. * The name of a callback function that returns default values.
* *
* The function will be called with the following arguments: * The function will be called with the following arguments:
* - \Drupal\Core\Entity\EntityInterface $entity * - \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being created. * The entity being created.
* - \Drupal\field\Entity\FieldConfig $field * - \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field object. * The field definition.
* - \Drupal\field\Entity\FieldInstanceConfig $instance
* The field instance object.
* - string $langcode
* The language of the entity being created.
* It should return an array of default values, in the same format as the * It should return an array of default values, in the same format as the
* $default_value property. * $default_value property.
* *
...@@ -588,14 +584,17 @@ public function isMultiple() { ...@@ -588,14 +584,17 @@ public function isMultiple() {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getDefaultValue(EntityInterface $entity) { public function getDefaultValue(ContentEntityInterface $entity) {
if (!empty($this->default_value_function)) { // Allow custom default values function.
$function = $this->default_value_function; if ($function = $this->default_value_function) {
return $function($entity, $this->getField(), $this, $entity->language()->id); $value = call_user_func($function, $entity, $this);
} }
elseif (!empty($this->default_value)) { else {
return $this->default_value; $value = $this->default_value;
} }
// Allow the field type to process default values.
$field_item_list_class = $this->getClass();
return $field_item_list_class::processDefaultValue($value, $entity, $this);
} }
/** /**
......
...@@ -220,7 +220,7 @@ function testFieldAttachSaveEmptyDataDefaultValue() { ...@@ -220,7 +220,7 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
// Verify that fields are populated with default values. // Verify that fields are populated with default values.
$entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1)); $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
$default = field_test_default_value($entity_init, $this->field, $this->instance); $default = field_test_default_value($entity_init, $this->instance);
$this->assertEqual($entity_init->{$this->field_name}->getValue(), $default, 'Default field value correctly populated.'); $this->assertEqual($entity_init->{$this->field_name}->getValue(), $default, 'Default field value correctly populated.');
// Insert: Field is NULL. // Insert: Field is NULL.
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Defines a field type and its formatters and widgets. * Defines a field type and its formatters and widgets.
*/ */
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
...@@ -31,7 +31,7 @@ function field_test_field_config_update_forbid(FieldConfigInterface $field, Fiel ...@@ -31,7 +31,7 @@ function field_test_field_config_update_forbid(FieldConfigInterface $field, Fiel
/** /**
* Sample 'default value' callback. * Sample 'default value' callback.
*/ */
function field_test_default_value(EntityInterface $entity, $field, $instance) { function field_test_default_value(ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
return array(array('value' => 99)); return array(array('value' => 99));
} }
......
...@@ -357,7 +357,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -357,7 +357,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setRequired(TRUE) ->setRequired(TRUE)
->setTranslatable(TRUE) ->setTranslatable(TRUE)
->setRevisionable(TRUE) ->setRevisionable(TRUE)
->setSetting('default_value', '') ->setDefaultValue('')
->setSetting('max_length', 255) ->setSetting('max_length', 255)
->setDisplayOptions('view', array( ->setDisplayOptions('view', array(
'label' => 'hidden', 'label' => 'hidden',
...@@ -453,11 +453,11 @@ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, ...@@ -453,11 +453,11 @@ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type,
$options = $node_type->getModuleSettings('node')['options']; $options = $node_type->getModuleSettings('node')['options'];
$fields['status'] = clone $base_field_definitions['status']; $fields['status'] = clone $base_field_definitions['status'];
$fields['status']->setSetting('default_value', !empty($options['status']) ? NODE_PUBLISHED : NODE_NOT_PUBLISHED); $fields['status']->setDefaultValue(!empty($options['status']) ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
$fields['promote'] = clone $base_field_definitions['promote']; $fields['promote'] = clone $base_field_definitions['promote'];
$fields['promote']->setSetting('default_value', !empty($options['promote']) ? NODE_PROMOTED : NODE_NOT_PROMOTED); $fields['promote']->setDefaultValue(!empty($options['promote']) ? NODE_PROMOTED : NODE_NOT_PROMOTED);
$fields['sticky'] = clone $base_field_definitions['sticky']; $fields['sticky'] = clone $base_field_definitions['sticky'];
$fields['sticky']->setSetting('default_value', !empty($options['sticky']) ? NODE_STICKY : NODE_NOT_STICKY); $fields['sticky']->setDefaultValue(!empty($options['sticky']) ? NODE_STICKY : NODE_NOT_STICKY);
return $fields; return $fields;
} }
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
namespace Drupal\serialization\Tests; namespace Drupal\serialization\Tests;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
use Symfony\Component\Serializer\Serializer;
use Drupal\Component\Utility\String; use Drupal\Component\Utility\String;
/** /**
......
...@@ -174,7 +174,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -174,7 +174,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setDescription(t('The name of the shortcut.')) ->setDescription(t('The name of the shortcut.'))
->setRequired(TRUE) ->setRequired(TRUE)
->setTranslatable(TRUE) ->setTranslatable(TRUE)
->setSetting('default_value', '') ->setDefaultValue('')
->setSetting('max_length', 255) ->setSetting('max_length', 255)
->setDisplayOptions('form', array( ->setDisplayOptions('form', array(
'type' => 'string', 'type' => 'string',
......
...@@ -59,4 +59,15 @@ protected function assertDefaultValues($entity_type) { ...@@ -59,4 +59,15 @@ protected function assertDefaultValues($entity_type) {
$this->assertTrue(Uuid::isValid($entity->uuid->value), String::format('%entity_type: Default UUID', array('%entity_type' => $entity_type))); $this->assertTrue(Uuid::isValid($entity->uuid->value), String::format('%entity_type: Default UUID', array('%entity_type' => $entity_type)));
$this->assertEqual($entity->name->getValue(), array(0 => array('value' => NULL)), 'Field has one empty value by default.'); $this->assertEqual($entity->name->getValue(), array(0 => array('value' => NULL)), 'Field has one empty value by default.');
} }
/**