Issue #1777956 by fago, klausi, sun, dasjo: Provide a way to define default values for entity fields.

8.0.x
Alex Pott 2013-06-19 10:31:20 +02:00
parent 3f196cab2e
commit 851d7d0ff2
29 changed files with 283 additions and 111 deletions

View File

@ -105,21 +105,25 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
$bundle = FALSE;
if ($this->bundleKey) {
if (!isset($values[$this->bundleKey])) {
throw new EntityStorageException(t('Missing bundle for entity type @type', array('@type' => $this->entityType)));
throw new EntityStorageException(format_string('Missing bundle for entity type @type', array('@type' => $this->entityType)));
}
$bundle = $values[$this->bundleKey];
}
$entity = new $this->entityClass(array(), $this->entityType, $bundle);
// Set all other given values.
foreach ($values as $name => $value) {
$entity->$name = $value;
foreach ($entity as $name => $field) {
if (isset($values[$name])) {
$entity->$name = $values[$name];
}
elseif (!array_key_exists($name, $values)) {
$entity->get($name)->applyDefaultValue();
}
unset($values[$name]);
}
// Assign a new UUID if there is none yet.
if ($this->uuidKey && !isset($entity->{$this->uuidKey}->value)) {
$uuid = new Uuid();
$entity->{$this->uuidKey} = $uuid->generate();
// Set any passed values for non-defined fields also.
foreach ($values as $name => $value) {
$entity->$name = $value;
}
$entity->postCreate($this);

View File

@ -479,6 +479,15 @@ class Entity implements IteratorAggregate, EntityInterface {
// http://drupal.org/node/1868004.
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
foreach ($this->getProperties() as $property) {
$property->applyDefaultValue(FALSE);
}
}
/**
* Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange().
*/

View File

@ -535,6 +535,13 @@ class EntityBCDecorator implements IteratorAggregate, EntityInterface {
}
/**
* Forwards the call to the decorated entity.
*/
public function applyDefaultValue($notify = TRUE) {
return $this->decorated->applyDefaultValue($notify);
}
/*
* {@inheritdoc}
*/
public function preSave(EntityStorageControllerInterface $storage_controller) {

View File

@ -9,7 +9,6 @@ namespace Drupal\Core\Entity;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Component\Uuid\Uuid;
use ArrayIterator;
use InvalidArgumentException;
@ -42,13 +41,10 @@ class EntityNG extends Entity {
*
* @todo: Add methods for getting original fields and for determining
* changes.
* @todo: Provide a better way for defining default values.
*
* @var array
*/
protected $values = array(
'langcode' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => Language::LANGCODE_NOT_SPECIFIED))),
);
protected $values = array();
/**
* The array of fields, each being an instance of FieldInterface.
@ -549,8 +545,7 @@ class EntityNG extends Entity {
// Check if the entity type supports UUIDs and generate a new one if so.
if (!empty($entity_info['entity_keys']['uuid'])) {
$uuid = new Uuid();
$duplicate->{$entity_info['entity_keys']['uuid']}->value = $uuid->generate();
$duplicate->{$entity_info['entity_keys']['uuid']}->applyDefaultValue();
}
// Check whether the entity type supports revisions and initialize it if so.

View File

@ -87,15 +87,19 @@ class EntityReferenceItem extends FieldItemBase {
* Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the entity property, if no array is
// given.
// Treat the values as value of the entity property, if no array is
// given as this handles entity IDs and objects.
if (isset($values) && !is_array($values)) {
$values = array('entity' => $values);
// Directly update the property instead of invoking the parent, so that
// the entity property can take care of updating the ID property.
$this->properties['entity']->setValue($values, $notify);
}
// Make sure that the 'entity' property gets set as 'target_id'.
if (isset($values['target_id']) && !isset($values['entity'])) {
$values['entity'] = $values['target_id'];
else {
// Make sure that the 'entity' property gets set as 'target_id'.
if (isset($values['target_id']) && !isset($values['entity'])) {
$values['entity'] = $values['target_id'];
}
parent::setValue($values, $notify);
}
parent::setValue($values, $notify);
}
}

View File

@ -21,6 +21,9 @@ use Drupal\Core\TypedData\ItemList;
* contained item the entity field delegates __get() and __set() calls
* directly to the first item.
*
* Supported settings (below the definition's 'settings' key) are:
* - default_value: (optional) If set, the default value to apply to the field.
*
* @see \Drupal\Core\Entity\Field\FieldInterface
*/
class Field extends ItemList implements FieldInterface {
@ -195,6 +198,20 @@ class Field extends ItemList implements FieldInterface {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
if (isset($this->definition['settings']['default_value'])) {
$this->setValue($this->definition['settings']['default_value'], $notify);
}
else {
// Create one field item and apply defaults.
$this->offsetGet(0)->applyDefaultValue(FALSE);
}
return $this;
}
/**
* {@inheritdoc}
*/

View File

@ -8,7 +8,7 @@
namespace Drupal\Core\Entity\Field\Type;
use Drupal\Core\Entity\Field\FieldItemBase;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\Language\Language;
/**
* Defines the 'language_field' entity field item.
@ -51,14 +51,28 @@ class LanguageItem extends FieldItemBase {
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the language property, if no array
// is given.
// is given as this handles language codes and objects.
if (isset($values) && !is_array($values)) {
$values = array('language' => $values);
// Directly update the property instead of invoking the parent, so that
// the language property can take care of updating the language code
// property.
$this->properties['language']->setValue($values, $notify);
}
// Make sure that the 'language' property gets set as 'value'.
if (isset($values['value']) && !isset($values['language'])) {
$values['language'] = $values['value'];
else {
// Make sure that the 'language' property gets set as 'value'.
if (isset($values['value']) && !isset($values['language'])) {
$values['language'] = $values['value'];
}
parent::setValue($values, $notify);
}
parent::setValue($values, $notify);
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Default to LANGCODE_NOT_SPECIFIED.
$this->setValue(array('value' => Language::LANGCODE_NOT_SPECIFIED), $notify);
return $this;
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @file
* Contains \Drupal\Core\Entity\Field\Type\UuidItem.
*/
namespace Drupal\Core\Entity\Field\Type;
use Drupal\Component\Uuid\Uuid;
/**
* Defines the 'uuid_field' entity field item.
*
* The field uses a newly generated UUID as default value.
*/
class UuidItem extends StringItem {
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Default to one field item with a generated UUID.
$uuid = new Uuid();
$this->setValue(array('value' => $uuid->generate()), $notify);
return $this;
}
}

View File

@ -230,4 +230,15 @@ class Map extends TypedData implements \IteratorAggregate, ComplexDataInterface
$this->parent->onChange($this->name);
}
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Apply the default value of all properties.
foreach ($this->getProperties() as $property) {
$property->applyDefaultValue(FALSE);
}
return $this;
}
}

View File

@ -127,6 +127,15 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
return \Drupal::typedData()->getValidator()->validate($this);
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Default to no default value.
$this->setValue(NULL, $notify);
return $this;
}
/**
* Implements \Drupal\Core\TypedData\TypedDataInterface::setContext().
*/

View File

@ -78,6 +78,19 @@ interface TypedDataInterface {
*/
public function validate();
/**
* Applies the default value.
*
* @param bool $notify
* (optional) Whether to notify the parent object of the change. Defaults to
* TRUE. If a property is updated from a parent object, set it to FALSE to
* avoid being notified again.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
* Returns itself to allow for chaining.
*/
public function applyDefaultValue($notify = TRUE);
/**
* Returns the name of a property or item.
*

View File

@ -49,7 +49,7 @@ class CustomBlockStorageController extends DatabaseStorageControllerNG implement
$properties['uuid'] = array(
'label' => t('UUID'),
'description' => t('The custom block UUID.'),
'type' => 'string_field',
'type' => 'uuid_field',
);
$properties['revision_id'] = array(
'label' => t('Revision ID'),

View File

@ -113,7 +113,7 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
$properties['uuid'] = array(
'label' => t('UUID'),
'description' => t('The comment UUID.'),
'type' => 'string_field',
'type' => 'uuid_field',
);
$properties['pid'] = array(
'label' => t('Parent ID'),
@ -142,12 +142,16 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
'label' => t('User ID'),
'description' => t('The user ID of the comment author.'),
'type' => 'entity_reference_field',
'settings' => array('target_type' => 'user'),
'settings' => array(
'target_type' => 'user',
'default_value' => 0,
),
);
$properties['name'] = array(
'label' => t('Name'),
'description' => t("The comment author's name."),
'type' => 'string_field',
'settings' => array('default_value' => ''),
);
$properties['mail'] = array(
'label' => t('e-mail'),

View File

@ -97,7 +97,6 @@ class Comment extends EntityNG implements CommentInterface {
*/
public $subject;
/**
* The comment author ID.
*
@ -181,19 +180,6 @@ class Comment extends EntityNG implements CommentInterface {
*/
public $new;
/**
* The plain data values of the contained properties.
*
* Define default values.
*
* @var array
*/
protected $values = array(
'langcode' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => Language::LANGCODE_NOT_SPECIFIED))),
'name' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => ''))),
'uid' => array(Language::LANGCODE_DEFAULT => array(0 => array('target_id' => 0))),
);
/**
* Initialize the object. Invoked upon construction and wake up.
*/

View File

@ -61,15 +61,19 @@ class FileItem extends FieldItemBase {
* Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the entity property, if no array is
// given.
// Treat the values as value of the entity property, if no array is
// given as this handles entity IDs and objects.
if (isset($values) && !is_array($values)) {
$values = array('entity' => $values);
// Directly update the property instead of invoking the parent, so that
// the entity property can take care of updating the ID property.
$this->properties['entity']->setValue($values, $notify);
}
// Make sure that the 'entity' property gets set as 'fid'.
if (isset($values['fid']) && !isset($values['entity'])) {
$values['entity'] = $values['fid'];
else {
// Make sure that the 'entity' property gets set as 'target_id'.
if (isset($values['fid']) && !isset($values['entity'])) {
$values['entity'] = $values['fid'];
}
parent::setValue($values, $notify);
}
parent::setValue($values, $notify);
}
}

View File

@ -68,15 +68,19 @@ class ImageItem extends FieldItemBase {
* Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the entity property, if no array is
// given.
// Treat the values as value of the entity property, if no array is
// given as this handles entity IDs and objects.
if (isset($values) && !is_array($values)) {
$values = array('entity' => $values);
// Directly update the property instead of invoking the parent, so that
// the entity property can take care of updating the ID property.
$this->properties['entity']->setValue($values, $notify);
}
// Make sure that the 'entity' property gets set as 'fid'.
if (isset($values['fid']) && !isset($values['entity'])) {
$values['entity'] = $values['fid'];
else {
// Make sure that the 'entity' property gets set as 'target_id'.
if (isset($values['fid']) && !isset($values['entity'])) {
$values['entity'] = $values['fid'];
}
parent::setValue($values, $notify);
}
parent::setValue($values, $notify);
}
}

View File

@ -131,7 +131,7 @@ class NodeStorageController extends DatabaseStorageControllerNG {
$properties['uuid'] = array(
'label' => t('UUID'),
'description' => t('The node UUID.'),
'type' => 'string_field',
'type' => 'uuid_field',
'read-only' => TRUE,
);
$properties['vid'] = array(

View File

@ -0,0 +1,61 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\EntityFieldDefaultValueTest.
*/
namespace Drupal\system\Tests\Entity;
use Drupal\Component\Uuid\Uuid;
use Drupal\Core\Language\Language;
/**
* Tests Entity API default field value functionality.
*/
class EntityFieldDefaultValueTest extends EntityUnitTestBase {
/**
* The UUID object to be used for generating UUIDs.
*
* @var \Drupal\Component\Uuid\UuidInterface
*/
protected $uuid;
public static function getInfo() {
return array(
'name' => 'Entity Field Default Value',
'description' => 'Tests default values for entity fields.',
'group' => 'Entity API',
);
}
public function setUp() {
parent::setUp();
// Initiate the generator object.
$this->uuid = new Uuid();
}
/**
* Tests default values on entities and fields.
*/
public function testDefaultValues() {
// All entity variations have to have the same results.
foreach (entity_test_entity_types() as $entity_type) {
$this->assertDefaultValues($entity_type);
}
}
/**
* Executes a test set for a defined entity type.
*
* @param string $entity_type
* The entity type to run the tests with.
*/
protected function assertDefaultValues($entity_type) {
$entity = entity_create($entity_type, array());
$this->assertEqual($entity->langcode->value, Language::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Default language', array('%entity_type' => $entity_type)));
$this->assertTrue($this->uuid->isValid($entity->uuid->value), format_string('%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.');
}
}

View File

@ -31,7 +31,7 @@ class UuidUnitTest extends UnitTestBase {
}
public function setUp() {
// Initiate the generator. This will lazy-load uuid.inc.
// Initiate the generator object.
$this->uuid = new Uuid();
parent::setUp();
}

View File

@ -2301,6 +2301,17 @@ function system_data_type_info() {
'class' => '\Drupal\Core\Entity\Field\Type\UriItem',
'list class' => '\Drupal\Core\Entity\Field\Type\Field',
),
'uuid_field' => array(
'label' => t('UUID field item'),
'description' => t('An entity field containing a UUID.'),
'class' => '\Drupal\Core\Entity\Field\Type\UuidItem',
'list class' => '\Drupal\Core\Entity\Field\Type\Field',
'constraints' => array(
'ComplexData' => array(
'value' => array('Length' => array('max' => 128)),
),
),
),
);
}

View File

@ -40,10 +40,7 @@ class EntityTestStorageController extends DatabaseStorageControllerNG {
$fields['uuid'] = array(
'label' => t('UUID'),
'description' => t('The UUID of the test entity.'),
'type' => 'string_field',
'property_constraints' => array(
'value' => array('Length' => array('max' => 128)),
),
'type' => 'uuid_field',
);
$fields['langcode'] = array(
'label' => t('Language code'),

View File

@ -121,16 +121,6 @@ class Term extends EntityNG implements TermInterface {
*/
public $parent;
/**
* Default values for the term.
*
* @var array
*/
protected $values = array(
'langcode' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => Language::LANGCODE_NOT_SPECIFIED))),
'weight' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => 0))),
);
/**
* Implements Drupal\Core\Entity\EntityInterface::id().
*/

View File

@ -70,7 +70,7 @@ class TermStorageController extends DatabaseStorageControllerNG implements TermS
$properties['uuid'] = array(
'label' => t('UUID'),
'description' => t('The term UUID.'),
'type' => 'string_field',
'type' => 'uuid_field',
'read-only' => TRUE,
);
$properties['vid'] = array(
@ -103,11 +103,14 @@ class TermStorageController extends DatabaseStorageControllerNG implements TermS
'label' => t('Weight'),
'description' => t('The weight of this term in relation to other terms.'),
'type' => 'integer_field',
'settings' => array('default_value' => 0),
);
$properties['parent'] = array(
'label' => t('Term Parents'),
'description' => t('The parents of this term.'),
'type' => 'integer_field',
// Save new terms with no parents by default.
'settings' => array('default_value' => 0),
'computed' => TRUE,
);
return $properties;

View File

@ -53,15 +53,19 @@ class TaxonomyTermReferenceItem extends FieldItemBase {
* Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the entity property, if no array is
// given.
// Treat the values as value of the entity property, if no array is
// given as this handles entity IDs and objects.
if (isset($values) && !is_array($values)) {
$values = array('entity' => $values);
// Directly update the property instead of invoking the parent, so that
// the entity property can take care of updating the ID property.
$this->properties['entity']->setValue($values, $notify);
}
// Make sure that the 'entity' property gets set as 'tid'.
if (isset($values['tid']) && !isset($values['entity'])) {
$values['entity'] = $values['tid'];
else {
// Make sure that the 'entity' property gets set as 'target_id'.
if (isset($values['tid']) && !isset($values['entity'])) {
$values['entity'] = $values['tid'];
}
parent::setValue($values, $notify);
}
parent::setValue($values, $notify);
}
}

View File

@ -86,11 +86,4 @@ class TextProcessed extends TypedData {
throw new ReadOnlyException('Unable to set a computed property.');
}
}
/**
* Implements \Drupal\Core\TypedData\TypedDataInterface::validate().
*/
public function validate() {
// @todo: Implement.
}
}

View File

@ -51,6 +51,16 @@ class TextItem extends FieldItemBase {
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Default to a simple check_plain().
// @todo: Add in the filter default format here.
$this->setValue(array('format' => NULL), $notify);
return $this;
}
/**
* {@inheritdoc}
*/

View File

@ -180,25 +180,6 @@ class User extends EntityNG implements UserInterface {
*/
public $roles;
/**
* The plain data values of the contained properties.
*
* Define default values.
*
* @var array
*/
protected $values = array(
'langcode' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => Language::LANGCODE_NOT_SPECIFIED))),
'preferred_langcode' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => Language::LANGCODE_NOT_SPECIFIED))),
'admin_preffered_langcode' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => Language::LANGCODE_NOT_SPECIFIED))),
'name' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => ''))),
'mail' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => ''))),
'init' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => ''))),
'access' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => 0))),
'login' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => 0))),
'status' => array(Language::LANGCODE_DEFAULT => array(0 => array('value' => 1))),
);
/**
* {@inheritdoc}
*/

View File

@ -184,7 +184,7 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
$properties['uuid'] = array(
'label' => t('UUID'),
'description' => t('The user UUID.'),
'type' => 'string_field',
'type' => 'uuid_field',
'read-only' => TRUE,
);
$properties['langcode'] = array(
@ -206,6 +206,7 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
'label' => t('Name'),
'description' => t('The name of this user'),
'type' => 'string_field',
'settings' => array('default_value' => ''),
);
$properties['pass'] = array(
'label' => t('Name'),
@ -216,6 +217,7 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
'label' => t('Name'),
'description' => t('The e-mail of this user'),
'type' => 'string_field',
'settings' => array('default_value' => ''),
);
$properties['signature'] = array(
'label' => t('Name'),
@ -241,6 +243,7 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
'label' => t('User status'),
'description' => t('Whether the user is active (1) or blocked (0).'),
'type' => 'boolean_field',
'settings' => array('default_value' => 1),
);
$properties['created'] = array(
'label' => t('Created'),
@ -251,16 +254,19 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
'label' => t('Last access'),
'description' => t('The time that the user last accessed the site.'),
'type' => 'integer_field',
'settings' => array('default_value' => 0),
);
$properties['login'] = array(
'label' => t('Last login'),
'description' => t('The time that the user last logged in.'),
'type' => 'integer_field',
'settings' => array('default_value' => 0),
);
$properties['init'] = array(
'label' => t('Init'),
'description' => t('The email address used for initial account creation.'),
'type' => 'string_field',
'settings' => array('default_value' => ''),
);
$properties['roles'] = array(
'label' => t('Roles'),

View File

@ -1137,6 +1137,13 @@ class ViewUI implements ViewStorageInterface {
$this->storage->onChange($property_name);
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
return $this->storage->applyDefaultValue($notify);
}
/**
* {@inheritdoc}
*/