Issue #2047229 by fago, smiletrl, Berdir, effulgentsia, amateescu: Make use of classes for entity field and data definitions.

8.0.x
webchick 2013-11-27 13:54:33 -08:00
parent 66f8fb8253
commit 9bcaedec15
49 changed files with 1292 additions and 460 deletions

View File

@ -399,7 +399,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
}
// Non-translatable fields are always stored with
// Language::LANGCODE_DEFAULT as key.
if ($langcode != Language::LANGCODE_DEFAULT && empty($definition['translatable'])) {
if ($langcode != Language::LANGCODE_DEFAULT && !$definition->isFieldTranslatable()) {
if (!isset($this->fields[$property_name][Language::LANGCODE_DEFAULT])) {
$this->fields[$property_name][Language::LANGCODE_DEFAULT] = $this->getTranslatedField($property_name, Language::LANGCODE_DEFAULT);
}
@ -437,7 +437,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
public function getProperties($include_computed = FALSE) {
$properties = array();
foreach ($this->getPropertyDefinitions() as $name => $definition) {
if ($include_computed || empty($definition['computed'])) {
if ($include_computed || !$definition->isComputed()) {
$properties[$name] = $this->get($name);
}
}
@ -710,7 +710,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$definitions = $translation->getPropertyDefinitions();
foreach ($values as $name => $value) {
if (isset($definitions[$name]) && !empty($definitions[$name]['translatable'])) {
if (isset($definitions[$name]) && $definitions[$name]->isFieldTranslatable()) {
$translation->$name = $value;
}
}
@ -724,7 +724,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
public function removeTranslation($langcode) {
if (isset($this->translations[$langcode]) && $langcode != Language::LANGCODE_DEFAULT && $langcode != $this->getDefaultLanguage()->id) {
foreach ($this->getPropertyDefinitions() as $name => $definition) {
if (!empty($definition['translatable'])) {
if ($definition->isFieldTranslatable()) {
unset($this->values[$name][$langcode]);
unset($this->fields[$name][$langcode]);
}
@ -779,7 +779,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
return;
}
foreach ($this->getPropertyDefinitions() as $name => $definition) {
if (empty($definition['computed']) && !empty($this->fields[$name])) {
if (!$definition->isComputed() && !empty($this->fields[$name])) {
foreach ($this->fields[$name] as $langcode => $field) {
$field->filterEmptyValues();
$this->values[$name][$langcode] = $field->getValue();
@ -911,7 +911,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// object keyed by language. To avoid creating different field objects
// we retain just the original value, as references will be recreated
// later as needed.
if (empty($definitions[$name]['translatable']) && count($values) > 1) {
if (!$definitions[$name]->isFieldTranslatable() && count($values) > 1) {
$values = array_intersect_key($values, array(Language::LANGCODE_DEFAULT => TRUE));
}
foreach ($values as $langcode => $items) {

View File

@ -42,14 +42,20 @@ interface ContentEntityInterface extends EntityInterface, RevisionableInterface,
/**
* Defines the base fields of the entity type.
*
* Implementations typically use the class \Drupal\Core\Field\FieldDefinition
* for creating the field definitions; for example a 'name' field could be
* defined as the following:
* @code
* $fields['name'] = FieldDefinition::create('string')
* ->setLabel(t('Name'));
* @endcode
*
* @param string $entity_type
* The entity type to return properties for. Useful when a single class is
* used for multiple, possibly dynamic entity types.
*
* @return array
* An array of entity field definitions as specified by
* \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions(), keyed by field
* name.
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of entity field definitions, keyed by field name.
*
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
*/

View File

@ -10,6 +10,7 @@ namespace Drupal\Core\Entity;
use Drupal\Component\Plugin\PluginManagerBase;
use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManager;
@ -333,6 +334,8 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
$this->entityFieldInfo[$entity_type] = $cache->data;
}
else {
// @todo: Refactor to allow for per-bundle overrides.
// See https://drupal.org/node/2114707.
$class = $this->factory->getPluginClass($entity_type, $this->getDefinition($entity_type));
$base_definitions = $class::baseFieldDefinitions($entity_type);
@ -357,22 +360,31 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
$result = $this->moduleHandler->invokeAll('entity_field_info', array($entity_type));
$this->entityFieldInfo[$entity_type] = NestedArray::mergeDeep($this->entityFieldInfo[$entity_type], $result);
// Enforce field definitions to be objects.
foreach (array('definitions', 'optional') as $key) {
foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
if (is_array($definition)) {
$definition = FieldDefinition::createFromOldStyleDefinition($definition);
}
// Automatically set the field name for non-configurable fields.
if ($definition instanceof FieldDefinition) {
$definition->setFieldName($field_name);
}
}
}
// Invoke alter hooks.
$hooks = array('entity_field_info', $entity_type . '_field_info');
$this->moduleHandler->alter($hooks, $this->entityFieldInfo[$entity_type], $entity_type);
// Enforce fields to be multiple and untranslatable by default.
// Ensure all basic fields are not defined as translatable.
$entity_info = $this->getDefinition($entity_type);
$keys = array_intersect_key(array_filter($entity_info['entity_keys']), array_flip(array('id', 'revision', 'uuid', 'bundle')));
$untranslatable_fields = array_flip(array('langcode') + $keys);
foreach (array('definitions', 'optional') as $key) {
foreach ($this->entityFieldInfo[$entity_type][$key] as $name => &$definition) {
$definition['list'] = TRUE;
// Ensure ids and langcode fields are never made translatable.
if (isset($untranslatable_fields[$name]) && !empty($definition['translatable'])) {
throw new \LogicException(format_string('The @field field cannot be translatable.', array('@field' => $definition['label'])));
}
if (!isset($definition['translatable'])) {
$definition['translatable'] = FALSE;
foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
if (isset($untranslatable_fields[$field_name]) && $definition->isFieldTranslatable()) {
throw new \LogicException(format_string('The @field field cannot be translatable.', array('@field' => $definition->getFieldLabel())));
}
}
}

View File

@ -25,9 +25,7 @@ interface EntityManagerInterface extends PluginManagerInterface {
/**
* Gets an array of content entity field definitions.
*
* If a bundle is passed, fields specific to this bundle are included. Entity
* fields are always multi-valued, so 'list' is TRUE for each returned field
* definition.
* If a bundle is passed, fields specific to this bundle are included.
*
* @param string $entity_type
* The entity type to get field definitions for. Only entity types that
@ -36,19 +34,11 @@ interface EntityManagerInterface extends PluginManagerInterface {
* (optional) The entity bundle for which to get field definitions. If NULL
* is passed, no bundle-specific fields are included. Defaults to NULL.
*
* @return array
* An array of field definitions of entity fields, keyed by field
* name. In addition to the typed data definition keys as described at
* \Drupal\Core\TypedData\TypedDataManager::create() the following keys are
* supported:
* - queryable: Whether the field is queryable via QueryInterface.
* Defaults to TRUE if 'computed' is FALSE or not set, to FALSE otherwise.
* - translatable: Whether the field is translatable. Defaults to FALSE.
* - configurable: A boolean indicating whether the field is configurable
* via field.module. Defaults to FALSE.
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of entity field definitions, keyed by field name.
*
* @see \Drupal\Core\TypedData\TypedDataManager::create()
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitionsByConstraints()
* @see \Drupal\Core\Entity\EntityManager::getFieldDefinitionsByConstraints()
*/
public function getFieldDefinitions($entity_type, $bundle = NULL);

View File

@ -263,9 +263,7 @@ abstract class FieldableEntityStorageControllerBase extends EntityStorageControl
*/
public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
if ($values = $this->readFieldItemsToPurge($entity, $instance)) {
$field = $instance->getField();
$definition = _field_generate_entity_field_definition($field, $instance);
$items = \Drupal::typedData()->create($definition, $values, $field->getFieldName(), $entity);
$items = \Drupal::typedData()->create($instance, $values, $instance->getFieldName(), $entity);
$items->delete();
}
$this->purgeFieldItems($entity, $instance);

View File

@ -7,6 +7,7 @@
namespace Drupal\Core\Field;
use Drupal\Core\Field\Plugin\DataType\FieldInstanceInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\field\Field;
@ -25,10 +26,11 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
/**
* {@inheritdoc}
*/
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
if (isset($definition['instance'])) {
$this->instance = $definition['instance'];
// Definition can be the field config or field instance.
if ($definition instanceof FieldInstanceInterface) {
$this->instance = $definition;
}
}
@ -36,6 +38,10 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
* {@inheritdoc}
*/
public function getFieldDefinition() {
// Configurable fields have the field_config entity injected as definition,
// but we want to return the more specific field instance here.
// @todo: Overhaul this once we have per-bundle field definitions injected,
// see https://drupal.org/node/2114707.
if (!isset($this->instance)) {
$entity = $this->getEntity();
$instances = Field::fieldInfo()->getBundleInstances($entity->entityType(), $entity->bundle());
@ -43,9 +49,7 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
$this->instance = $instances[$this->getName()];
}
else {
// For base fields, fall back to the parent implementation.
// @todo: Inject the field definition with
// https://drupal.org/node/2047229.
// For base fields, fall back to return the general definition.
return parent::getFieldDefinition();
}
}
@ -73,13 +77,6 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
return $constraints;
}
/**
* {@inheritdoc}
*/
protected function getDefaultValue() {
return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
}
/**
* {@inheritdoc}
*/

View File

@ -8,27 +8,25 @@
namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\ListDefinition;
/**
* A class for defining entity fields.
*/
class FieldDefinition implements FieldDefinitionInterface {
class FieldDefinition extends ListDefinition implements FieldDefinitionInterface, \ArrayAccess {
/**
* The array holding values for all definition keys.
* Creates a new field definition.
*
* @var array
*/
protected $definition = array();
/**
* Constructs a new FieldDefinition object.
* @param string $type
* The type of the field.
*
* @param array $definition
* (optional) If given, a definition represented as array.
* @return \Drupal\Core\Field\FieldDefinition
* A new field definition object.
*/
public function __construct(array $definition = array()) {
$this->definition = $definition;
public static function create($type) {
return new static(array(), DataDefinition::create('field_item:' . $type));
}
/**
@ -44,7 +42,7 @@ class FieldDefinition implements FieldDefinitionInterface {
* @param string $name
* The field name to set.
*
* @return \Drupal\Core\Field\FieldDefinition
* @return self
* The object itself for chaining.
*/
public function setFieldName($name) {
@ -56,58 +54,63 @@ class FieldDefinition implements FieldDefinitionInterface {
* {@inheritdoc}
*/
public function getFieldType() {
$data_type = $this->getItemDefinition()->getDataType();
// Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
$parts = explode(':', $this->definition['type']);
$parts = explode(':', $data_type);
return $parts[1];
}
/**
* Sets the field type.
*
* @param string $type
* The field type to set.
*
* @return \Drupal\Core\Field\FieldDefinition
* The object itself for chaining.
*/
public function setFieldType($type) {
$this->definition['type'] = 'field_item:' . $type;
return $this;
}
/**
* Sets a field setting.
*
* @param string $type
* The field type to set.
*
* @return \Drupal\Core\Field\FieldDefinition
* The object itself for chaining.
*/
public function setFieldSetting($setting_name, $value) {
$this->definition['settings'][$setting_name] = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function getFieldSettings() {
return $this->definition['settings'];
return $this->getItemDefinition()->getSettings();
}
/**
* Sets field settings.
*
* @param array $settings
* The value to set.
*
* @return self
* The object itself for chaining.
*/
public function setFieldSettings(array $settings) {
$this->getItemDefinition()->setSettings($settings);
return $this;
}
/**
* {@inheritdoc}
*/
public function getFieldSetting($setting_name) {
return isset($this->definition['settings'][$setting_name]) ? $this->definition['settings'][$setting_name] : NULL;
$settings = $this->getFieldSettings();
return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
}
/**
* Sets a field setting.
*
* @param string $setting_name
* The field setting to set.
* @param mixed $value
* The value to set.
*
* @return self
* The object itself for chaining.
*/
public function setFieldSetting($setting_name, $value) {
$settings = $this->getFieldSettings();
$settings[$setting_name] = $value;
return $this->setFieldSettings($settings);
}
/**
* {@inheritdoc}
*/
public function getFieldPropertyNames() {
return array_keys(\Drupal::typedData()->create($this->definition['type'])->getPropertyDefinitions());
return array_keys(\Drupal::typedData()->create($this->getItemDefinition())->getPropertyDefinitions());
}
/**
@ -123,7 +126,7 @@ class FieldDefinition implements FieldDefinitionInterface {
* @param bool $translatable
* Whether the field is translatable.
*
* @return \Drupal\Core\Field\FieldDefinition
* @return self
* The object itself for chaining.
*/
public function setTranslatable($translatable) {
@ -135,42 +138,28 @@ class FieldDefinition implements FieldDefinitionInterface {
* {@inheritdoc}
*/
public function getFieldLabel() {
return $this->definition['label'];
return $this->getLabel();
}
/**
* Sets the field label.
*
* @param string $label
* The field label to set.
*
* @return \Drupal\Core\Field\FieldDefinition
* The object itself for chaining.
* {@inheritdoc}
*/
public function setFieldLabel($label) {
$this->definition['label'] = $label;
return $this;
return $this->setLabel($label);
}
/**
* {@inheritdoc}
*/
public function getFieldDescription() {
return $this->definition['description'];
return $this->getDescription();
}
/**
* Sets the field label.
*
* @param string $description
* The field label to set.
*
* @return \Drupal\Core\Field\FieldDefinition
* The object itself for chaining.
* {@inheritdoc}
*/
public function setFieldDescription($description) {
$this->definition['description'] = $description;
return $this;
return $this->setDescription($description);
}
/**
@ -185,7 +174,7 @@ class FieldDefinition implements FieldDefinitionInterface {
* {@inheritdoc}
*/
public function isFieldRequired() {
return !empty($this->definition['required']);
return $this->isRequired();
}
/**
@ -200,13 +189,33 @@ class FieldDefinition implements FieldDefinitionInterface {
* Sets whether the field is required.
*
* @param bool $required
* TRUE if the field is required, FALSE otherwise.
* Whether the field is required.
*
* @return \Drupal\Core\Field\FieldDefinition
* @return self
* The object itself for chaining.
*/
public function setFieldRequired($required) {
$this->definition['required'] = $required;
return $this->setRequired($required);
}
/**
* {@inheritdoc}
*/
public function isFieldQueryable() {
return isset($this->definition['queryable']) ? $this->definition['queryable'] : !$this->isComputed();
}
/**
* Sets whether the field is queryable.
*
* @param bool $queryable
* Whether the field is queryable.
*
* @return self
* The object itself for chaining.
*/
public function setFieldQueryable($queryable) {
$this->definition['queryable'] = $queryable;
return $this;
}
@ -218,11 +227,13 @@ class FieldDefinition implements FieldDefinitionInterface {
* @param array $constraints
* The constraints to set.
*
* @return \Drupal\Core\Field\FieldDefinition
* @return self
* The object itself for chaining.
*/
public function setPropertyConstraints($name, array $constraints) {
$this->definition['item_definition']['constraints']['ComplexData'][$name] = $constraints;
$item_constraints = $this->getItemDefinition()->getConstraints();
$item_constraints['ComplexData'][$name] = $constraints;
$this->getItemDefinition()->setConstraints($item_constraints);
return $this;
}
@ -240,4 +251,77 @@ class FieldDefinition implements FieldDefinitionInterface {
return $this->getFieldSetting('default_value');
}
/**
* Allows creating field definition objects from old style definition arrays.
*
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public static function createFromOldStyleDefinition(array $definition) {
unset($definition['list']);
// Separate the list item definition from the list definition.
$list_definition = $definition;
unset($list_definition['type']);
// Constraints, class and settings apply to the list item.
unset($list_definition['constraints']);
unset($list_definition['class']);
unset($list_definition['settings']);
$field_definition = new FieldDefinition($list_definition);
if (isset($definition['list_class'])) {
$field_definition->setClass($definition['list_class']);
}
else {
$type_definition = \Drupal::typedData()->getDefinition($definition['type']);
if (isset($type_definition['list_class'])) {
$field_definition->setClass($type_definition['list_class']);
}
}
if (isset($definition['translatable'])) {
$field_definition->setTranslatable($definition['translatable']);
unset($definition['translatable']);
}
// Take care of the item definition now.
// Required applies to the field definition only.
unset($definition['required']);
$item_definition = new DataDefinition($definition);
$field_definition->setItemDefinition($item_definition);
return $field_definition;
}
/**
* {@inheritdoc}
*
* This is for BC support only.
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public function &offsetGet($offset) {
if ($offset == 'type') {
// What previously was "type" is now the type of the list item.
$type = &$this->itemDefinition->offsetGet('type');
return $type;
}
if (!isset($this->definition[$offset])) {
$this->definition[$offset] = NULL;
}
return $this->definition[$offset];
}
/**
* {@inheritdoc}
*
* This is for BC support only.
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public function offsetSet($offset, $value) {
if ($offset == 'type') {
// What previously was "type" is now the type of the list item.
$this->itemDefinition->setDataType($value);
}
else {
$this->definition[$offset] = $value;
}
}
}

View File

@ -8,6 +8,7 @@
namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\TypedData\ListDefinitionInterface;
/**
* Defines an interface for entity field definitions.
@ -36,10 +37,9 @@ use Drupal\Core\Entity\EntityInterface;
*
* However, entity base fields, such as $node->title, are not managed by
* field.module and its "field_entity"/"field_instance" configuration entities.
* Therefore, their definitions are provided by different objects that implement
* this interface.
* @todo That is still in progress: https://drupal.org/node/1949932. Update this
* documentation with details when that's implemented.
* Therefore, their definitions are provided by different objects based on the
* class \Drupal\Core\Field\FieldDefinition, which implements this
* interface as well.
*
* Field definitions may fully define a concrete data object (e.g.,
* $node_1->body), or may provide a best-guess definition for a data object that
@ -51,7 +51,7 @@ use Drupal\Core\Entity\EntityInterface;
* based on that abstract definition, even though that abstract definition can
* differ from the concrete definition of any particular node's body field.
*/
interface FieldDefinitionInterface {
interface FieldDefinitionInterface extends ListDefinitionInterface {
/**
* Value indicating a field accepts an unlimited number of values.
@ -130,9 +130,18 @@ interface FieldDefinitionInterface {
* Determines whether the field is configurable via field.module.
*
* @return bool
* TRUE if the field is configurable.
*/
public function isFieldConfigurable();
/**
* Determines whether the field is queryable via QueryInterface.
*
* @return bool
* TRUE if the field is queryable.
*/
public function isFieldQueryable();
/**
* Returns the human-readable label for the field.
*
@ -148,8 +157,8 @@ interface FieldDefinitionInterface {
* descriptive information is helpful. For example, as help text below the
* form element in entity edit forms.
*
* @return string
* The field description.
* @return string|null
* The field description, or NULL if no description is available.
*/
public function getFieldDescription();

View File

@ -25,7 +25,7 @@ abstract class FieldItemBase extends Map implements FieldItemInterface {
/**
* Overrides \Drupal\Core\TypedData\TypedData::__construct().
*/
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
// Initialize computed properties by default, such that they get cloned
// with the whole item.

View File

@ -9,20 +9,16 @@ namespace Drupal\Core\Field;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\ItemList;
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
use Drupal\Core\Language\Language;
/**
* Represents an entity field; that is, a list of field item objects.
*
* An entity field is a list of field items, which contain only primitive
* properties or entity references. Note that even single-valued entity
* fields are represented as list of items, however for easy access to the
* 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.
* An entity field is a list of field items, each containing a set of
* properties. Note that even single-valued entity fields are represented as
* list of field items, however for easy access to the contained item the entity
* field delegates __get() and __set() calls directly to the first item.
*/
class FieldItemList extends ItemList implements FieldItemListInterface {
@ -42,11 +38,10 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
protected $langcode = Language::LANGCODE_DEFAULT;
/**
* Overrides TypedData::__construct().
* {@inheritdoc}
*/
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
$this->definition['field_name'] = $name;
// Always initialize one empty item as most times a value for at least one
// item will be present. That way prototypes created by
// \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will
@ -79,7 +74,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
* {@inheritdoc}
*/
public function getFieldDefinition() {
return new FieldDefinition($this->definition);
return $this->definition;
}
/**
@ -108,7 +103,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
}
/**
* Overrides \Drupal\Core\TypedData\ItemList::setValue().
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
if (!isset($values) || $values === array()) {
@ -213,10 +208,8 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// @todo Remove getDefaultValue() and directly call
// FieldDefinition::getFieldDefaultValue() here, once
// https://drupal.org/node/2047229 is fixed.
$value = $this->getDefaultValue();
// NULL or array() mean "no default value", but 0, '0' and the empty string
// are valid default values.
if (!isset($value) || (is_array($value) && empty($value))) {
@ -236,24 +229,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
* The default value for the field.
*/
protected function getDefaultValue() {
if (isset($this->definition['settings']['default_value'])) {
return $this->definition['settings']['default_value'];
}
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
// Constraints usually apply to the field item, but required does make
// sense on the field only. So we special-case it to apply to the field for
// now.
// @todo: Separate list and list item definitions to separate constraints.
$constraints = array();
if (!empty($this->definition['required'])) {
$constraints[] = \Drupal::typedData()->getValidationConstraintManager()->create('NotNull', array());
}
return $constraints;
return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
}
/**

View File

@ -2,10 +2,10 @@
/**
* @file
* Contains \Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver.
* Contains \Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver.
*/
namespace Drupal\Core\Entity\Plugin\DataType\Deriver;
namespace Drupal\Core\Field\Plugin\DataType\Deriver;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;

View File

@ -2,13 +2,11 @@
/**
* @file
* Contains \Drupal\Core\Entity\Plugin\DataType\FieldItem.
* Contains \Drupal\Core\Field\Plugin\DataType\FieldItem.
*/
namespace Drupal\Core\Entity\Plugin\DataType;
namespace Drupal\Core\Field\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
/**
* Defines the base plugin for deriving data types for field types.
@ -20,7 +18,7 @@ use Drupal\Core\Annotation\Translation;
* id = "field_item",
* label = @Translation("Field item"),
* list_class = "\Drupal\Core\Field\FieldItemList",
* derivative = "Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver"
* derivative = "Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver"
* )
*/
abstract class FieldItem {

View File

@ -8,6 +8,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'integer' entity field type.
@ -31,15 +32,13 @@ class IntegerItem extends FieldItemBase {
static $propertyDefinitions;
/**
* Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
* {@inheritdoc}
*/
public function getPropertyDefinitions() {
if (!isset(static::$propertyDefinitions)) {
static::$propertyDefinitions['value'] = array(
'type' => 'integer',
'label' => t('Integer value'),
);
static::$propertyDefinitions['value'] = DataDefinition::create('integer')
->setLabel(t('Integer value'));
}
return static::$propertyDefinitions;
}

View File

@ -102,8 +102,8 @@ class LegacyConfigFieldItemList extends ConfigFieldItemList {
* The name of the hook, e.g. 'presave', 'validate'.
*/
protected function legacyCallback($hook, $args = array()) {
$definition = $this->getPluginDefinition();
$module = $definition['provider'];
$type_definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($this->getFieldDefinition()->getFieldType());
$module = $type_definition['provider'];
$callback = "{$module}_field_{$hook}";
if (function_exists($callback)) {
// We need to remove the empty "prototype" item here.

View File

@ -8,6 +8,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'string' entity field type.
@ -31,15 +32,13 @@ class StringItem extends FieldItemBase {
static $propertyDefinitions;
/**
* Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
* {@inheritdoc}
*/
public function getPropertyDefinitions() {
if (!isset(static::$propertyDefinitions)) {
static::$propertyDefinitions['value'] = array(
'type' => 'string',
'label' => t('Text value'),
);
static::$propertyDefinitions['value'] = DataDefinition::create('string')
->setLabel(t('Text value'));
}
return static::$propertyDefinitions;
}

View File

@ -25,7 +25,7 @@ use Drupal\Component\Annotation\Plugin;
*
* Furthermore, lists of data items are represented by objects implementing the
* \Drupal\Core\TypedData\ListInterface. A list contains items of the same data
* type, is ordered and may contain duplicates. The classed used for a list of
* type, is ordered and may contain duplicates. The class used for a list of
* items of a certain type may be specified using the 'list class' key.
*
* @see \Drupal::typedData()
@ -67,7 +67,7 @@ class DataType extends Plugin {
*
* @var string
*/
public $list_class = '\Drupal\Core\TypedData\ItemList';
public $list_class = '\Drupal\Core\TypedData\Plugin\DataType\ItemList';
/**
* The pre-defined primitive type that this data type maps to.

View File

@ -105,10 +105,6 @@ interface ComplexDataInterface extends \Traversable, TypedDataInterface {
/**
* Gets an array of property definitions of contained properties.
*
* @param array $definition
* The definition of the container's property, e.g. the definition of an
* entity reference property.
*
* @return array
* An array of property definitions of contained properties, keyed by
* property name.

View File

@ -0,0 +1,371 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\DataDefinition.
*/
namespace Drupal\Core\TypedData;
/**
* A class for defining data based on defined data types.
*/
class DataDefinition implements DataDefinitionInterface, \ArrayAccess {
/**
* The array holding values for all definition keys.
*
* @var array
*/
protected $definition = array();
/**
* Creates a new data definition.
*
* @param string $type
* The data type of the data; e.g., 'string', 'integer' or 'any'.
*
* @return \Drupal\Core\TypedData\DataDefinition
* A new DataDefinition object.
*/
public static function create($type) {
$definition['type'] = $type;
return new static($definition);
}
/**
* Constructs a new data definition object.
*
* @param array $definition
* (optional) If given, a data definition represented as array.
*/
public function __construct(array $definition = array()) {
$this->definition = $definition;
}
/**
* {@inheritdoc}
*/
public function getDataType() {
return !empty($this->definition['type']) ? $this->definition['type'] : 'any';
}
/**
* Sets the data type.
*
* @param string $type
* The data type to set.
*
* @return self
* The object itself for chaining.
*/
public function setDataType($type) {
$this->definition['type'] = $type;
return $this;
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return isset($this->definition['label']) ? $this->definition['label'] : NULL;
}
/**
* Sets the human-readable label.
*
* @param string $label
* The label to set.
*
* @return self
* The object itself for chaining.
*/
public function setLabel($label) {
$this->definition['label'] = $label;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return isset($this->definition['description']) ? $this->definition['description'] : NULL;
}
/**
* Sets the human-readable description.
*
* @param string $description
* The description to set.
*
* @return self
* The object itself for chaining.
*/
public function setDescription($description) {
$this->definition['description'] = $description;
return $this;
}
/**
* {@inheritdoc}
*/
public function isList() {
return ($this instanceof ListDefinitionInterface);
}
/**
* {@inheritdoc}
*/
public function isReadOnly() {
if (!isset($this->definition['read-only'])) {
// Default to read-only if the data value is computed.
return $this->isComputed();
}
return !empty($this->definition['read-only']);
}
/**
* Sets whether the data is read-only.
*
* @param bool $read_only
* Whether the data is read-only.
*
* @return self
* The object itself for chaining.
*/
public function setReadOnly($read_only) {
$this->definition['read-only'] = $read_only;
return $this;
}
/**
* {@inheritdoc}
*/
public function isComputed() {
return !empty($this->definition['computed']);
}
/**
* Sets whether the data is computed.
*
* @param bool $computed
* Whether the data is computed.
*
* @return self
* The object itself for chaining.
*/
public function setComputed($computed) {
$this->definition['computed'] = $computed;
return $this;
}
/**
* {@inheritdoc}
*/
public function isRequired() {
return !empty($this->definition['required']);
}
/**
* Sets whether the data is required.
*
* @param bool $required
* Whether the data is required.
*
* @return self
* The object itself for chaining.
*/
public function setRequired($required) {
$this->definition['required'] = $required;
return $this;
}
/**
* {@inheritdoc}
*/
public function getClass() {
return isset($this->definition['class']) ? $this->definition['class'] : NULL;
}
/**
* Sets the class used for creating the typed data object.
*
* @param string|null $class
* The class to use.
*
* @return self
* The object itself for chaining.
*/
public function setClass($class) {
$this->definition['class'] = $class;
return $this;
}
/**
* Returns the array of settings, as required by the used class.
*
* See the documentation of the class for supported or required settings.
*
* @return array
* The array of settings.
*/
public function getSettings() {
return isset($this->definition['settings']) ? $this->definition['settings'] : array();
}
/**
* Sets the array of settings, as required by the used class.
*
* @param array $settings
* The array of settings.
*
* @return self
* The object itself for chaining.
*/
public function setSettings(array $settings) {
$this->definition['settings'] = $settings;
return $this;
}
/**
* Returns an array of validation constraints.
*
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
*
* @return array
* Array of constraints, each being an instance of
* \Symfony\Component\Validator\Constraint.
*/
public function getConstraints() {
return isset($this->definition['constraints']) ? $this->definition['constraints'] : array();
}
/**
* Sets the array of validation constraints.
*
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
*
* @param array $constraints
* The array of constraints.
*
* @return self
* The object itself for chaining.
*/
public function setConstraints(array $constraints) {
$this->definition['constraints'] = $constraints;
return $this;
}
/**
* Adds a validation constraint.
*
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
*
* @param string $constraint_name
* The name of the constraint to add, i.e. its plugin id.
* @param array|null $options
* The constraint options as required by the constraint plugin, or NULL.
*
* @return self
* The object itself for chaining.
*/
public function addConstraint($constraint_name, $options = NULL) {
$this->definition['constraints'][$constraint_name] = $options;
return $this;
}
/**
* {@inheritdoc}
*
* This is for BC support only.
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public function offsetExists($offset) {
// PHP's array access does not work correctly with isset(), so we have to
// bake isset() in here. See https://bugs.php.net/bug.php?id=41727.
return array_key_exists($offset, $this->definition) && isset($this->definition[$offset]);
}
/**
* {@inheritdoc}
*
* This is for BC support only.
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public function &offsetGet($offset) {
if (!isset($this->definition[$offset])) {
$this->definition[$offset] = NULL;
}
return $this->definition[$offset];
}
/**
* {@inheritdoc}
*
* This is for BC support only.
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public function offsetSet($offset, $value) {
$this->definition[$offset] = $value;
}
/**
* {@inheritdoc}
*
* This is for BC support only.
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public function offsetUnset($offset) {
unset($this->definition[$offset]);
}
/**
* Returns all definition values as array.
*
* @return array
*/
public function toArray() {
return $this->definition;
}
/**
* Allows creating data definition objects from old style definition arrays.
*
* @todo: Remove once https://drupal.org/node/2112239 is in.
*/
public static function createFromOldStyleDefinition(array $definition) {
if (empty($definition['list'])) {
return new DataDefinition($definition);
}
// If the definition describes a list, separate the list item definition
// from the list definition.
unset($definition['list']);
$list_definition = $definition;
unset($list_definition['type']);
// Constraints, class and settings apply to the list item.
unset($list_definition['constraints']);
unset($list_definition['class']);
unset($list_definition['settings']);
$list_definition = new ListDefinition($list_definition);
if (isset($definition['list_class'])) {
$list_definition->setClass($definition['list_class']);
}
else {
$type_definition = \Drupal::typedData()->getDefinition($definition['type']);
if (isset($type_definition['list_class'])) {
$list_definition->setClass($type_definition['list_class']);
}
}
// Take care of the item definition now.
// Required applies to the list definition only.
unset($definition['required']);
$item_definition = new DataDefinition($definition);
$list_definition->setItemDefinition($item_definition);
return $list_definition;
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\DataDefinitionInterface.
*/
namespace Drupal\Core\TypedData;
/**
* Interface for data definitions.
*
* Data definitions are used to describe data based upon available data types.
* For example, a plugin could describe its parameters using data definitions
* in order to specify what kind of data is required for it.
*
* @see \Drupal\Core\TypedData\DataDefinition
*/
interface DataDefinitionInterface {
/**
* Returns the data type of the data.
*
* @return string
* The data type.
*/
public function getDataType();
/**
* Returns a human readable label.
*
* @return string
* The label.
*/
public function getLabel();
/**
* Returns a human readable description.
*
* @return string|null
* The description, or NULL if no description is available.
*/
public function getDescription();
/**
* Returns whether the data is multi-valued, i.e. a list of data items.
*
* This is equivalent to checking whether the data definition implements the
* \Drupal\Core\TypedData\ListDefinitionInterface interface.
*
* @return bool
* Whether the data is multi-valued.
*/
public function isList();
/**
* Determines whether the data is read-only.
*
* @return bool
* Whether the data is read-only.
*/
public function isReadOnly();
/**
* Determines whether the data value is computed.
*
* For example, data could be computed depending on some other values.
*
* @return bool
* Whether the data value is computed.
*/
public function isComputed();
/**
* Determines whether a data value is required.
*
* For required data a non-NULL value is mandatory.
*
* @return bool
* Whether a data value is required.
*/
public function isRequired();
/**
* Returns the class used for creating the typed data object.
*
* If not specified, the default class of the data type will be used.
*
* @return string|null
* The class used for creating the typed data object.
*/
public function getClass();
/**
* Returns the array of settings, as required by the used class.
*
* See the documentation of the class for supported or required settings.
*
* @return array
* The array of settings.
*/
public function getSettings();
/**
* Returns an array of validation constraints.
*
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
*
* @return array
* Array of constraints, each being an instance of
* \Symfony\Component\Validator\Constraint.
*/
public function getConstraints();
}

View File

@ -0,0 +1,98 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\ListDefinition.
*/
namespace Drupal\Core\TypedData;
/**
* A class for defining data based on defined data types.
*/
class ListDefinition extends DataDefinition implements ListDefinitionInterface {
/**
* The data definition of a list item.
*
* @var \Drupal\Core\TypedData\DataDefinitionInterface
*/
protected $itemDefinition;
/**
* Creates a new list definition.
*
* @param string $item_type
* The data type of the list items; e.g., 'string', 'integer' or 'any'.
*
* @return \Drupal\Core\TypedData\ListDefinition
* A new List Data Definition object.
*/
public static function create($item_type) {
return new static(array(), DataDefinition::create($item_type));
}
/**
* {@inheritdoc}
*
* @param
*/
public function __construct(array $definition = array(), DataDefinitionInterface $item_definition = NULL) {
parent::__construct($definition);
$this->itemDefinition = isset($item_definition) ? $item_definition : DataDefinition::create('any');
}
/**
* {@inheritdoc}
*/
public function getDataType() {
return 'list';
}
/**
* {@inheritdoc}
*/
public function setDataType($type) {
if ($type != 'list') {
throw new \LogicException('Lists must always be of data type "list".');
}
}
/**
* {@inheritdoc}
*/
public function getClass() {
$class = isset($this->definition['class']) ? $this->definition['class'] : NULL;
if (!empty($class)) {
return $class;
}
else {
// If a list definition is used but no class has been specified, derive
// the default list class from the item type.
$item_type_definition = \Drupal::typedData()
->getDefinition($this->getItemDefinition()->getDataType());
return $item_type_definition['list_class'];
}
}
/**
* {@inheritdoc}
*/
public function getItemDefinition() {
return $this->itemDefinition;
}
/**
* Sets the item definition.
*
* @param \Drupal\Core\TypedData\DataDefinition $definition
* A list item's data definition.
*
* @return self
* The object itself for chaining.
*/
public function setItemDefinition(DataDefinitionInterface $definition) {
$this->itemDefinition = $definition;
return $this;
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\ListDefinitionInterface.
*/
namespace Drupal\Core\TypedData;
/**
* Interface for data definitions of lists.
*
* This interface is present on a data definition if it describes a list. The
* actual lists implement the \Drupal\Core\TypedData\ListInterface.
*/
interface ListDefinitionInterface extends DataDefinitionInterface {
/**
* Gets the data definition of an item of the list.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface
* A data definition describing the list items.
*/
public function getItemDefinition();
}

View File

@ -11,7 +11,7 @@ namespace Drupal\Core\TypedData;
* Interface for a list of typed data.
*
* A list of typed data contains only items of the same type, is ordered and may
* contain duplicates.
* contain duplicates. Note that the data type of a list is always 'list'.
*
* When implementing this interface which extends Traversable, make sure to list
* IteratorAggregate or Iterator before this interface in the implements clause.
@ -29,7 +29,7 @@ interface ListInterface extends TypedDataInterface, \ArrayAccess, \Countable, \T
/**
* Gets the definition of a contained item.
*
* @return array
* @return \Drupal\Core\TypedData\DataDefinitionInterface
* The data definition of contained items.
*/
public function getItemDefinition();

View File

@ -2,16 +2,28 @@
/**
* @file
* Contains \Drupal\Core\TypedData\List.
* Contains \Drupal\Core\TypedData\Plugin\DataType\ItemList.
*/
namespace Drupal\Core\TypedData;
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\TypedDataInterface;
/**
* A generic list class.
*
* This class can serve as list for any type of items.
* This class can serve as list for any type of items and is used by default.
* Data types may specify the default list class in their definition, see
* Drupal\Core\TypedData\Annotation\DataType.
* Note: The class cannot be called "List" as list is a reserved PHP keyword.
*
* @DataType(
* id = "list",
* label = @Translation("List of items")
* )
*/
class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
@ -88,14 +100,6 @@ class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
}
}
/**
* Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
*/
public function getConstraints() {
// Apply the constraints to the list items only.
return array();
}
/**
* Implements \ArrayAccess::offsetExists().
*/
@ -140,7 +144,7 @@ class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
* Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
*/
public function getItemDefinition() {
return array('list' => FALSE) + $this->definition;
return $this->definition->getItemDefinition();
}
/**

View File

@ -20,7 +20,7 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
/**
* The data definition.
*
* @var array
* @var \Drupal\Core\TypedData\DataDefinitionInterface
*/
protected $definition;
@ -41,7 +41,7 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
/**
* Constructs a TypedData object given its definition and context.
*
* @param array $definition
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
* The data definition.
* @param string $name
* (optional) The name of the created property, or NULL if it is the root
@ -51,8 +51,12 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
* root of a typed data tree. Defaults to NULL.
*
* @see \Drupal\Core\TypedData\TypedDataManager::create()
*
* @todo When \Drupal\Core\Config\TypedConfigManager has been fixed to use
* class-based definitions, type-hint $definition to
* DataDefinitionInterface.
*/
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
$this->definition = $definition;
$this->parent = $parent;
$this->name = $name;

View File

@ -17,8 +17,8 @@ interface TypedDataInterface {
/**
* Gets the data definition.
*
* @return array
* The data definition array.
* @return \Drupal\Core\TypedData\DataDefinitionInterface
* The data definition object.
*/
public function getDefinition();

View File

@ -52,77 +52,49 @@ class TypedDataManager extends DefaultPluginManager {
}
/**
* Implements \Drupal\Component\Plugin\PluginManagerInterface::createInstance().
* Instantiates a typed data object.
*
* @param string $plugin_id
* The id of a plugin, i.e. the data type.
* @param string $data_type
* The data type, for which a typed object should be instantiated.
* @param array $configuration
* The plugin configuration, i.e. the data definition.
* @param string $name
* (optional) If a property or list item is to be created, the name of the
* property or the delta of the list item.
* @param mixed $parent
* (optional) If a property or list item is to be created, the parent typed
* data object implementing either the ListInterface or the
* ComplexDataInterface.
* The plugin configuration array, i.e. an array with the following keys:
* - data definition: The data definition object, i.e. an instance of
* \Drupal\Core\TypedData\DataDefinitionInterface.
* - name: (optional) If a property or list item is to be created, the name
* of the property or the delta of the list item.
* - parent: (optional) If a property or list item is to be created, the
* parent typed data object implementing either the ListInterface or the
* ComplexDataInterface.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
* The instantiated typed data object.
*/
public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) {
$type_definition = $this->getDefinition($plugin_id);
public function createInstance($data_type, array $configuration) {
$data_definition = $configuration['data_definition'];
$type_definition = $this->getDefinition($data_type);
if (!isset($type_definition)) {
throw new \InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $plugin_id)));
throw new \InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $data_type)));
}
// Allow per-data definition overrides of the used classes, i.e. take over
// classes specified in the data definition.
$key = empty($configuration['list']) ? 'class' : 'list_class';
if (isset($configuration[$key])) {
$class = $configuration[$key];
}
elseif (isset($type_definition[$key])) {
$class = $type_definition[$key];
}
// classes specified in the type definition.
$class = $data_definition->getClass();
$class = isset($class) ? $class : $type_definition['class'];
if (!isset($class)) {
throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id));
throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $data_type));
}
return new $class($configuration, $name, $parent);
return new $class($data_definition, $configuration['name'], $configuration['parent']);
}
/**
* Creates a new typed data object instance.
*
* @param array $definition
* The data definition array with the following array keys and values:
* - type: The data type of the data to wrap. Required.
* - label: A human readable label.
* - description: A human readable description.
* - list: Whether the data is multi-valued, i.e. a list of data items.
* Defaults to FALSE.
* - computed: A boolean specifying whether the data value is computed by
* the object, e.g. depending on some other values.
* - read-only: A boolean specifying whether the data is read-only. Defaults
* to TRUE for computed properties, to FALSE otherwise.
* - class: If set and 'list' is FALSE, the class to use for creating the
* typed data object; otherwise the default class of the data type will be
* used.
* - list_class: If set and 'list' is TRUE, the class to use for creating
* the typed data object; otherwise the default list class of the data
* type will be used.
* - settings: An array of settings, as required by the used 'class'. See
* the documentation of the class for supported or required settings.
* - list_settings: An array of settings as required by the used
* 'list_class'. See the documentation of the list class for support or
* required settings.
* - constraints: An array of validation constraints. See
* \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
* - required: A boolean specifying whether a non-NULL value is mandatory.
* Further keys may be supported in certain usages, e.g. for further keys
* supported for entity field definitions see
* \Drupal\Core\Entity\StorageControllerInterface::getPropertyDefinitions().
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
* The data definition of the typed data object. For backwards-compatibility
* an array representation of the data definition may be passed also.
* @todo: Need to remove array support in https://drupal.org/node/2112239.
* @param mixed $value
* (optional) The data value. If set, it has to match one of the supported
* data type format as documented for the data type classes.
@ -148,12 +120,20 @@ class TypedDataManager extends DefaultPluginManager {
* @see \Drupal\Core\TypedData\Plugin\DataType\Uri
* @see \Drupal\Core\TypedData\Plugin\DataType\Binary
*/
public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) {
$wrapper = $this->createInstance($definition['type'], $definition, $name, $parent);
if (isset($value)) {
$wrapper->setValue($value, FALSE);
public function create($definition, $value = NULL, $name = NULL, $parent = NULL) {
// @todo: Remove array support once https://drupal.org/node/2112239 is in.
if (is_array($definition)) {
$definition = DataDefinition::createFromOldStyleDefinition($definition);
}
return $wrapper;
$typed_data = $this->createInstance($definition->getDataType(), array(
'data_definition' => $definition,
'name' => $name,
'parent' => $parent,
));
if (isset($value)) {
$typed_data->setValue($value, FALSE);
}
return $typed_data;
}
/**
@ -335,18 +315,20 @@ class TypedDataManager extends DefaultPluginManager {
*
* @see \Drupal\Core\Validation\ConstraintManager
*
* @param array $definition
* A data definition array.
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
* A data definition.
*
* @return array
* Array of constraints, each being an instance of
* \Symfony\Component\Validator\Constraint.
*
* @todo: Having this as well as $definition->getConstraints() is confusing.
*/
public function getConstraints($definition) {
public function getConstraints(DataDefinitionInterface $definition) {
$constraints = array();
$validation_manager = $this->getValidationConstraintManager();
$type_definition = $this->getDefinition($definition['type']);
$type_definition = $this->getDefinition($definition->getDataType());
// Auto-generate a constraint for data types implementing a primitive
// interface.
if (is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\PrimitiveInterface')) {
@ -363,19 +345,21 @@ class TypedDataManager extends DefaultPluginManager {
}
}
// Add any constraints specified as part of the data definition.
if (isset($definition['constraints'])) {
foreach ($definition['constraints'] as $name => $options) {
$constraints[] = $validation_manager->create($name, $options);
}
$defined_constraints = $definition->getConstraints();
foreach ($defined_constraints as $name => $options) {
$constraints[] = $validation_manager->create($name, $options);
}
// Add the NotNull constraint for required data.
if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
if ($definition->isRequired() && !isset($defined_constraints['NotNull'])) {
$constraints[] = $validation_manager->create('NotNull', array());
}
// If the definition does not provide a class use the class from the type
// definition for performing interface checks.
$class = isset($definition['class']) ? $definition['class'] : $type_definition['class'];
$class = $definition->getClass();
if (!$class) {
$class = $type_definition['class'];
}
// Check if the class provides allowed values.
if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) {
$constraints[] = $validation_manager->create('AllowedValues', array());

View File

@ -19,6 +19,9 @@ class ValidReferenceConstraintValidator extends ConstraintValidator {
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
if (!isset($value)) {
return;
}
$id = $value->get('target_id')->getValue();
// '0' or NULL are considered valid empty references.
if (empty($id)) {

View File

@ -101,7 +101,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
// translation.
// @todo Remove this special casing as soon as configurable and
// base field definitions are "unified".
if (!empty($definition['configurable']) && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
if ($definition->isFieldConfigurable() && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
$instance = FieldService::fieldInfo()->getInstance($entity_type, $bundle, $field_name);
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
'#label' => $instance->getFieldLabel(),
@ -122,7 +122,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
// fields support translation. Whether they are actually enabled is
// determined through our settings. As a consequence only fields
// that support translation can be enabled or disabled.
elseif (isset($field_settings[$field_name]) || !empty($definition['translatable'])) {
elseif (isset($field_settings[$field_name]) || $definition->isFieldTranslatable()) {
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
'#label' => $definition['label'],
'#type' => 'checkbox',

View File

@ -134,7 +134,7 @@ function content_translation_entity_field_info_alter(&$info, $entity_type) {
foreach ($fields as $name => $translatable) {
foreach ($keys as $key) {
if (isset($info[$key][$name])) {
$info[$key][$name]['translatable'] = (bool) $translatable;
$info[$key][$name]->setTranslatable((bool) $translatable);
break;
}
}
@ -655,7 +655,7 @@ function content_translation_form_alter(array &$form, array &$form_state) {
if (!$entity->isNew() && (!isset($translations[$form_langcode]) || count($translations) > 1)) {
foreach ($entity->getPropertyDefinitions() as $property_name => $definition) {
if (isset($form[$property_name])) {
$form[$property_name]['#multilingual'] = !empty($definition['translatable']);
$form[$property_name]['#multilingual'] = $definition->isFieldTranslatable();
}
}
}

View File

@ -29,7 +29,7 @@ class DateTimeComputed extends TypedData {
/**
* {@inheritdoc}
*/
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
if (!isset($definition['settings']['date source'])) {
throw new \InvalidArgumentException("The definition's 'date source' key has to specify the name of the date property to be computed.");

View File

@ -189,7 +189,7 @@ function edit_preprocess_field(&$variables) {
// Fields that are not part of the entity (i.e. dynamically injected "pseudo
// fields") and computed fields are not editable.
$definition = $entity->getPropertyDefinition($element['#field_name']);
if ($definition && empty($definition['computed'])) {
if ($definition && !$definition->isComputed()) {
$variables['attributes']['data-edit-field-id'] = $entity->entityType() . '/' . $entity->id() . '/' . $element['#field_name'] . '/' . $element['#language'] . '/' . $element['#view_mode'];
}
}

View File

@ -557,7 +557,7 @@ function _editor_get_processed_text_fields(ContentEntityInterface $entity) {
// Find all configurable fields, because only they could have a
// text_processing setting.
$configurable_fields = array_keys(array_filter($properties, function ($definition) {
return isset($definition['configurable']) && $definition['configurable'] === TRUE;
return $definition->isFieldConfigurable();
}));
if (empty($configurable_fields)) {
return array();

View File

@ -27,35 +27,6 @@ function entity_reference_field_info_alter(&$info) {
$info['entity_reference']['list_class'] = '\Drupal\entity_reference\Plugin\Field\FieldType\ConfigurableEntityReferenceFieldItemList';
}
/**
* Implements hook_entity_field_info_alter().
*
* Set the "target_type" and "list_class" property definition for entity
* reference fields.
*
* @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::getPropertyDefinitions()
*
* @param array $info
* The property info array as returned by hook_entity_field_info().
* @param string $entity_type
* The entity type for which entity properties are defined.
*/
function entity_reference_entity_field_info_alter(&$info, $entity_type) {
foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
foreach ($instances as $field_name => $instance) {
if ($instance->getFieldType() != 'entity_reference') {
continue;
}
if (isset($info['definitions'][$field_name])) {
$info['definitions'][$field_name]['settings']['target_type'] = $instance->getFieldSetting('target_type');
}
elseif (isset($info['optional'][$field_name])) {
$info['optional'][$field_name]['settings']['target_type'] = $instance->getFieldSetting('target_type');
}
}
}
}
/**
* Implements hook_field_widget_info_alter().
*/

View File

@ -616,7 +616,7 @@ function field_attach_form_validate(ContentEntityInterface $entity, $form, &$for
$has_violations = FALSE;
foreach ($entity as $field_name => $field) {
$definition = $field->getDefinition();
if (!empty($definition['configurable']) && (empty($options['field_name']) || $options['field_name'] == $field_name)) {
if ($definition->isFieldConfigurable() && (empty($options['field_name']) || $options['field_name'] == $field_name)) {
$field_violations = $field->validate();
if (count($field_violations)) {
$has_violations = TRUE;
@ -926,13 +926,13 @@ function field_language(EntityInterface $entity, $field_name = NULL, $langcode =
if (!isset($field_name)) {
$display_langcodes = array();
foreach ($definitions as $name => $definition) {
if (!empty($definition['configurable'])) {
if ($definition->isFieldConfigurable()) {
$display_langcodes[$name] = $translatable ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
}
}
return $display_langcodes;
}
elseif (!empty($definitions[$field_name]['configurable'])) {
elseif ($definitions[$field_name]->isFieldConfigurable()) {
return $translatable ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
}
}

View File

@ -194,16 +194,17 @@ function field_entity_field_info($entity_type) {
foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
$optional = $bundle_name != $entity_type;
// @todo: Improve hook_entity_field_info() to allow per-bundle field
// definitions, such that we can pass on field instances as field
// definitions here. See https://drupal.org/node/2114707.
foreach ($instances as $field_name => $instance) {
$definition = _field_generate_entity_field_definition($instance->getField());
if ($optional) {
$property_info['optional'][$field_name] = $definition;
$property_info['optional'][$field_name] = $instance->getField();
$property_info['bundle map'][$bundle_name][] = $field_name;
}
else {
$property_info['definitions'][$field_name] = $definition;
$property_info['definitions'][$field_name] = $instance->getField();
}
}
}
@ -211,33 +212,6 @@ function field_entity_field_info($entity_type) {
return $property_info;
}
/**
* Generates an entity field definition for a configurable field.
*
* @param \Drupal\field\FieldInterface $field
* The field definition.
* @param \Drupal\field\FieldInstanceInterface $instance
* (Optional) The field instance definition.
*
* @return array
* The entity field definition.
*/
function _field_generate_entity_field_definition(FieldInterface $field, FieldInstanceInterface $instance = NULL) {
// @todo: Allow for adding field type settings.
$definition = array(
'label' => t('Field !name', array('!name' => $field->name)),
'type' => 'field_item:' . $field->type,
'list' => TRUE,
'configurable' => TRUE,
'translatable' => !empty($field->translatable),
);
if ($instance) {
$definition['instance'] = $instance;
}
return $definition;
}
/**
* Implements hook_entity_bundle_create().
*/

View File

@ -11,6 +11,7 @@ use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\field\FieldException;
use Drupal\field\FieldInterface;
@ -197,6 +198,13 @@ class Field extends ConfigEntityBase implements FieldInterface {
*/
public $original = NULL;
/**
* The data definition of a field item.
*
* @var \Drupal\Core\TypedData\DataDefinition
*/
protected $itemDefinition;
/**
* Constructs a Field object.
*
@ -566,6 +574,20 @@ class Field extends ConfigEntityBase implements FieldInterface {
return $this->translatable;
}
/**
* Sets whether the field is translatable.
*
* @param bool $translatable
* Whether the field is translatable.
*
* @return \Drupal\field\Entity\Field
* The object itself for chaining.
*/
public function setTranslatable($translatable) {
$this->translatable = $translatable;
return $this;
}
/**
* {@inheritdoc}
*/
@ -577,7 +599,7 @@ class Field extends ConfigEntityBase implements FieldInterface {
* {@inheritdoc}
*/
public function getFieldDescription() {
return '';
return NULL;
}
/**
@ -607,6 +629,20 @@ class Field extends ConfigEntityBase implements FieldInterface {
*/
public function getFieldDefaultValue(EntityInterface $entity) { }
/**
* {@inheritdoc}
*/
public function isFieldConfigurable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function isFieldQueryable() {
return TRUE;
}
/**
* A list of columns that can not be used as field type columns.
*
@ -675,8 +711,90 @@ class Field extends ConfigEntityBase implements FieldInterface {
/**
* {@inheritdoc}
*/
public function isFieldConfigurable() {
public function getDataType() {
return 'list';
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label();
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->getFieldDescription();
}
/**
* {@inheritdoc}
*/
public function isList() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function isReadOnly() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function isComputed() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function isRequired() {
return $this->isFieldRequired();
}
/**
* {@inheritdoc}
*/
public function getClass() {
// Derive list class from the field type.
$type_definition = \Drupal::service('plugin.manager.field.field_type')
->getDefinition($this->getFieldType());
return $type_definition['list_class'];
}
/**
* {@inheritdoc}
*/
public function getSettings() {
// This should actually return the settings for field item list, which are
// not the field settings. However, there is no harm in returning field
// settings here, so we do that to avoid confusion for now.
// @todo: Unify with getFieldSettings() or remove once typed data moved
// to the adapter approach.
return $this->getFieldSettings();
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
return array();
}
/**
* {@inheritdoc}
*/
public function getItemDefinition() {
if (!isset($this->itemDefinition)) {
$this->itemDefinition = DataDefinition::create('field_item:' . $this->type)
->setSettings($this->getFieldSettings());
}
return $this->itemDefinition;
}
}

View File

@ -11,6 +11,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\field\FieldException;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\field\FieldInstanceInterface;
/**
@ -206,6 +207,13 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
*/
public $original = NULL;
/**
* The data definition of a field item.
*
* @var \Drupal\Core\TypedData\DataDefinition
*/
protected $itemDefinition;
/**
* Constructs a FieldInstance object.
*
@ -566,17 +574,24 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
/**
* {@inheritdoc}
*/
public function allowBundleRename() {
$this->bundle_rename_allowed = TRUE;
public function isFieldConfigurable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function isFieldConfigurable() {
public function isFieldQueryable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function allowBundleRename() {
$this->bundle_rename_allowed = TRUE;
}
/*
* Implements the magic __sleep() method.
*
@ -598,4 +613,82 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
$this->__construct($values);
}
public function getDataType() {
return 'list';
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label();
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->getFieldDescription();
}
/**
* {@inheritdoc}
*/
public function isList() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function isReadOnly() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function isComputed() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function isRequired() {
// Only field instances can be required.
return $this->isFieldRequired();
}
/**
* {@inheritdoc}
*/
public function getClass() {
return $this->field->getClass();
}
/**
* {@inheritdoc}
*/
public function getSettings() {
return $this->getFieldSettings();
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
return array();
}
/**
* {@inheritdoc}
*/
public function getItemDefinition() {
if (!isset($this->itemDefinition)) {
$this->itemDefinition = DataDefinition::create('field_item:' . $this->field->type)
->setSettings($this->getFieldSettings());
}
return $this->itemDefinition;
}
}

View File

@ -64,8 +64,8 @@ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidR
*/
protected function constructValue($data, $context) {
$field_item = $context['target_instance'];
$field_definition = $field_item->getDefinition();
$target_type = $field_definition['settings']['target_type'];
$field_definition = $field_item->getFieldDefinition();
$target_type = $field_definition->getFieldSetting('target_type');
if ($id = $this->entityResolver->resolve($this, $data, $target_type)) {
return array('target_id' => $id);
}

View File

@ -57,8 +57,8 @@ class FieldItemNormalizer extends NormalizerBase {
if (isset($data['lang'])) {
$langcode = $data['lang'];
unset($data['lang']);
$field_definition = $field_item->getDefinition();
if ($field_definition['translatable']) {
$field_definition = $field_item->getFieldDefinition();
if ($field_definition->isFieldTranslatable()) {
$field_item = $this->createTranslatedInstance($field_item, $langcode);
}
}

View File

@ -33,11 +33,11 @@ class FieldNormalizer extends NormalizerBase {
// Get the field definition.
$entity = $field->getEntity();
$field_name = $field->getName();
$field_definition = $entity->getPropertyDefinition($field_name);
$field_definition = $field->getFieldDefinition();
// If this field is not translatable, it can simply be normalized without
// separating it into different translations.
if (empty($field_definition['translatable'])) {
if (!$field_definition->isFieldTranslatable()) {
$normalized_field_items = $this->normalizeFieldItems($field, $format, $context);
}
// Otherwise, the languages have to be extracted from the entity and passed

View File

@ -41,7 +41,7 @@ class LocaleTypedConfig extends Element {
/**
* Constructs a configuration wrapper object.
*
* @param array $definition
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
* The data definition.
* @param string $name
* The configuration object name.
@ -50,7 +50,7 @@ class LocaleTypedConfig extends Element {
* @param \Drupal\locale\LocaleConfigManager $localeConfig;
* The locale configuration manager object.
*/
public function __construct(array $definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) {
public function __construct($definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) {
parent::__construct($definition, $name);
$this->langcode = $langcode;
$this->localeConfig = $localeConfig;

View File

@ -9,8 +9,7 @@ namespace Drupal\serialization\Tests\Normalizer;
use Drupal\Tests\UnitTestCase;
use Drupal\serialization\Normalizer\ListNormalizer;
use Drupal\Core\TypedData\ItemList;
use Drupal\Core\TypedData\Plugin\DataType\Integer;
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
/**
* Tests the ListNormalizer class.

View File

@ -6,6 +6,7 @@
*/
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldDefinition;
/**
* @addtogroup hooks
@ -626,10 +627,8 @@ function hook_entity_form_display_alter(\Drupal\entity\Entity\EntityFormDisplay
*
* @return array
* An array of entity field information having the following optional entries:
* - definitions: An array of field definitions to add all entities of this
* type, keyed by field name. See
* \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions() for a list of
* supported keys in field definitions.
* - definitions: An array of field definitions to add to all entities of this
* type, keyed by field name.
* - optional: An array of field definitions for optional entity fields, keyed
* by field name. Optional fields are fields that only exist for certain
* bundles of the entity type.
@ -637,29 +636,26 @@ function hook_entity_form_display_alter(\Drupal\entity\Entity\EntityFormDisplay
* optional fields that entities of this bundle have.
*
* @see hook_entity_field_info_alter()
* @see \Drupal\Core\Field\FieldDefinitionInterface
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
* @see \Drupal\Core\TypedData\TypedDataManager::create()
*/
function hook_entity_field_info($entity_type) {
if (mymodule_uses_entity_type($entity_type)) {
$info = array();
$info['definitions']['mymodule_text'] = array(
'type' => 'string_item',
'list' => TRUE,
'label' => t('The text'),
'description' => t('A text property added by mymodule.'),
'computed' => TRUE,
'class' => '\Drupal\mymodule\EntityComputedText',
);
$info['definitions']['mymodule_text'] = FieldDefinition::create('string')
->setLabel(t('The text'))
->setDescription(t('A text property added by mymodule.'))
->setComputed(TRUE)
->setClass('\Drupal\mymodule\EntityComputedText');
if ($entity_type == 'node') {
// Add a property only to entities of the 'article' bundle.
$info['optional']['mymodule_text_more'] = array(
'type' => 'string_item',
'list' => TRUE,
'label' => t('More text'),
'computed' => TRUE,
'class' => '\Drupal\mymodule\EntityComputedMoreText',
);
$info['optional']['mymodule_text_more'] = FieldDefinition::create('string')
->setLabel(t('More text'))
->setComputed(TRUE)
->setClass('\Drupal\mymodule\EntityComputedMoreText');
$info['bundle map']['article'][0] = 'mymodule_text_more';
}
return $info;
@ -678,8 +674,8 @@ function hook_entity_field_info($entity_type) {
*/
function hook_entity_field_info_alter(&$info, $entity_type) {
if (!empty($info['definitions']['mymodule_text'])) {
// Alter the mymodule_text property to use a custom class.
$info['definitions']['mymodule_text']['class'] = '\Drupal\anothermodule\EntityComputedText';
// Alter the mymodule_text field to use a custom class.
$info['definitions']['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
}
}

View File

@ -353,21 +353,19 @@ class EntityFieldTest extends EntityUnitTestBase {
*/
protected function checkIntrospection($entity_type) {
// Test getting metadata upfront.
// @todo: Make this work without having to create entity objects.
$entity = entity_create($entity_type, array());
$definitions = $entity->getPropertyDefinitions();
$this->assertEqual($definitions['name']['type'], 'field_item:string', $entity_type .': Name field found.');
$this->assertEqual($definitions['user_id']['type'], 'field_item:entity_reference', $entity_type .': User field found.');
$this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.');
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type);
$this->assertEqual($definitions['name']->getFieldType(), 'string', $entity_type .': Name field found.');
$this->assertEqual($definitions['user_id']->getFieldType(), 'entity_reference', $entity_type .': User field found.');
$this->assertEqual($definitions['field_test_text']->getFieldType(), 'text', $entity_type .': Test-text-field field found.');
// Test introspecting an entity object.
// @todo: Add bundles and test bundles as well.
$entity = entity_create($entity_type, array());
$definitions = $entity->getPropertyDefinitions();
$this->assertEqual($definitions['name']['type'], 'field_item:string', $entity_type .': Name field found.');
$this->assertEqual($definitions['user_id']['type'], 'field_item:entity_reference', $entity_type .': User field found.');
$this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.');
$this->assertEqual($definitions['name']->getFieldType(), 'string', $entity_type .': Name field found.');
$this->assertEqual($definitions['user_id']->getFieldType(), 'entity_reference', $entity_type .': User field found.');
$this->assertEqual($definitions['field_test_text']->getFieldType(), 'text', $entity_type .': Test-text-field field found.');
$name_properties = $entity->name->getPropertyDefinitions();
$this->assertEqual($name_properties['value']['type'], 'string', $entity_type .': String value property of the name found.');

View File

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\TypedData;
use Drupal\Component\Utility\String;
use Drupal\Core\TypedData\ListDefinition;
use Drupal\simpletest\DrupalUnitTestBase;
use Drupal\Core\Datetime\DrupalDateTime;
@ -313,10 +313,7 @@ class TypedDataTest extends DrupalUnitTestBase {
public function testTypedDataLists() {
// Test working with an existing list of strings.
$value = array('one', 'two', 'three');
$typed_data = $this->createTypedData(array(
'type' => 'string',
'list' => TRUE,
), $value);
$typed_data = $this->createTypedData(ListDefinition::create('string'), $value);
$this->assertEqual($typed_data->getValue(), $value, 'List value has been set.');
// Test iterating.
$count = 0;

View File

@ -75,7 +75,7 @@ function entity_test_entity_info_alter(&$info) {
function entity_test_entity_field_info_alter(&$info, $entity_type) {
if ($entity_type == 'entity_test_mulrev' && ($names = \Drupal::state()->get('entity_test.field_definitions.translatable'))) {
foreach ($names as $name => $value) {
$info['definitions'][$name]['translatable'] = $value;
$info['definitions'][$name]->setTranslatable($value);
}
}
}

View File

@ -11,7 +11,9 @@ use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\Annotation\EntityType;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\taxonomy\TermInterface;
/**
@ -200,67 +202,55 @@ class Term extends ContentEntityBase implements TermInterface {
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
$properties['tid'] = array(
'label' => t('Term ID'),
'description' => t('The term ID.'),
'type' => 'integer_field',
'read-only' => TRUE,
);
$properties['uuid'] = array(
'label' => t('UUID'),
'description' => t('The term UUID.'),
'type' => 'uuid_field',
'read-only' => TRUE,
);
$properties['vid'] = array(
'label' => t('Vocabulary ID'),
'description' => t('The ID of the vocabulary to which the term is assigned.'),
'type' => 'string_field',
);
$properties['langcode'] = array(
'label' => t('Language code'),
'description' => t('The term language code.'),
'type' => 'language_field',
);
$properties['name'] = array(
'label' => t('Name'),
'description' => t('The term name.'),
'type' => 'string_field',
);
$properties['description'] = array(
'label' => t('Description'),
'description' => t('A description of the term'),
'type' => 'string_field',
);
$fields['tid'] = FieldDefinition::create('integer')
->setLabel(t('Term ID'))
->setDescription(t('The term ID.'))
->setReadOnly(TRUE);
$fields['uuid'] = FieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The term UUID.'))
->setReadOnly(TRUE);
$fields['vid'] = FieldDefinition::create('string')
->setLabel(t('Vocabulary ID'))
->setDescription(t('The ID of the vocabulary to which the term is assigned.'));
$fields['langcode'] = FieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The term language code.'));
$fields['name'] = FieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The term name.'));
$fields['description'] = FieldDefinition::create('string')
->setLabel(t('Description'))
->setDescription(t('A description of the term.'));
// @todo Combine with description.
$properties['format'] = array(
'label' => t('Description format'),
'description' => t('The filter format ID of the description.'),
'type' => 'string_field',
);
$properties['weight'] = array(
'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',
$fields['format'] = FieldDefinition::create('string')
->setLabel(t('Description format'))
->setDescription(t('The filter format ID of the description.'));
$fields['weight'] = FieldDefinition::create('integer')
->setLabel(t('Weight'))
->setDescription(t('The weight of this term in relation to other terms.'))
->setFieldSetting('default_value', 0);
$fields['parent'] = FieldDefinition::create('integer')
->setLabel(t('Term Parents'))
->setDescription(t('The parents of this term.'))
// Save new terms with no parents by default.
'settings' => array('default_value' => 0),
'computed' => TRUE,
);
$properties['changed'] = array(
'label' => t('Changed'),
'description' => t('The time that the term was last edited.'),
'type' => 'integer_field',
'property_constraints' => array(
'value' => array('EntityChanged' => array()),
),
);
return $properties;
->setFieldSetting('default_value', 0)
->setComputed(TRUE);
$fields['changed'] = FieldDefinition::create('integer')
->setLabel(t('Changed'))
->setDescription(t('The time that the term was last edited.'))
->setPropertyConstraints('value', array('EntityChanged' => array()));
return $fields;
}
/**

View File

@ -29,7 +29,7 @@ class TextProcessed extends TypedData {
/**
* Overrides TypedData::__construct().
*/
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
if (!isset($definition['settings']['text source'])) {

View File

@ -25,6 +25,34 @@ class FieldDefinitionTest extends UnitTestCase {
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Prepare a container with a mock typed data object, that returns no
// type definitions.
// @todo: Overhaul how field definitions deal with dependencies and improve
// unit tests. See https://drupal.org/node/2143555.
$typed_data = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
->disableOriginalConstructor()
->getMock();
$typed_data
->expects($this->any())
->method('getDefinition')
->will($this->returnValue(NULL));
$container = $this->getMock('Drupal\Core\DependencyInjection\Container');
$container
->expects($this->any())
->method('get')
->will($this->returnValue($typed_data));
\Drupal::setContainer($container);
}
/**
* Tests field name methods.
*/
@ -59,10 +87,9 @@ class FieldDefinitionTest extends UnitTestCase {
* Tests field type methods.
*/
public function testFieldType() {
$definition = new FieldDefinition();
$field_name = $this->randomName();
$definition->setFieldType($field_name);
$this->assertEquals($field_name, $definition->getFieldType());
$field_type = $this->randomName();
$definition = FieldDefinition::create($field_type);
$this->assertEquals($field_type, $definition->getFieldType());
}
/**