diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 389bf8b..3a92337 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -392,7 +392,7 @@ protected function getTranslatedField($property_name, $langcode) { } // 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()) { $this->fields[$property_name][$langcode] = $this->getTranslatedField($property_name, Language::LANGCODE_DEFAULT); } else { @@ -421,7 +421,7 @@ public function set($property_name, $value, $notify = TRUE) { 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); } } @@ -693,7 +693,7 @@ public function addTranslation($langcode, array $values = array()) { $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; } } @@ -707,7 +707,7 @@ public function addTranslation($langcode, array $values = array()) { 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]); } @@ -762,7 +762,7 @@ public function updateOriginalValues() { 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(); diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php index 3fe24c2..bf2d77a 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php @@ -305,8 +305,7 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) { // Set only translatable properties, unless we are dealing with a // revisable entity, in which case we did not load the untranslatable // data before. - $translatable = !empty($definition['translatable']); - if (isset($data_fields[$name]) && ($this->revisionTable || $translatable)) { + if (isset($data_fields[$name]) && ($this->revisionTable || $definition->isFieldTranslatable())) { $entities[$id][$name][$langcode] = $values[$name]; } } diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index e7a9992..23c601e 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -496,15 +496,19 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) { if (is_array($definition)) { $definition = FieldDefinition::createFromOldStyleDefinition($definition); } - // Make sure the field name is properly set. - $definition->setFieldName($field_name); + // For manually defined fields, make sure the field name is set. + if ($definition instanceof FieldDefinition) { + $definition->setFieldName($field_name); + } } foreach ($this->entityFieldInfo[$entity_type]['optional'] as $field_name => &$definition) { if (is_array($definition)) { $definition = FieldDefinition::createFromOldStyleDefinition($definition); } - // Make sure the field name is properly set. - $definition->setFieldName($field_name); + // For manually defined fields, make sure the field name is set. + if ($definition instanceof FieldDefinition) { + $definition->setFieldName($field_name); + } } $this->cache->set($cid, $this->entityFieldInfo[$entity_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE, 'entity_field_info' => TRUE)); } diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php index c2bc4ce..c339576 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Entity\Field; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\TypedData\ListDefinitionInterface; /** * Defines an interface for entity field definitions. @@ -50,10 +51,8 @@ * abstractly, and present Views configuration options to the administrator * based on that abstract definition, even though that abstract definition can * differ from the concrete definition of any particular node's body field. - * - * @todo Extends this from DataDefinitionInterface. */ -interface FieldDefinitionInterface { +interface FieldDefinitionInterface extends ListDefinitionInterface { /** * Returns the machine name of the field. @@ -152,8 +151,8 @@ public function getFieldLabel(); * 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(); diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php index 3457acb..7a18f3f 100644 --- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php +++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php @@ -263,9 +263,7 @@ public function onBundleDelete($bundle) { } */ 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); diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php index 4f2fb06..abfd1d1 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php @@ -51,7 +51,6 @@ class FieldItemList extends ItemList implements FieldItemListInterface { */ 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 diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php index 90763fc..fb3b0a5 100644 --- a/core/lib/Drupal/Core/TypedData/DataDefinition.php +++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php @@ -117,7 +117,7 @@ public function isReadOnly() { // Default to read-only if the data value is computed. return $this->isComputed(); } - return $this->definition['read-only']; + return !empty($this->definition['read-only']); } /** diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php index d866d15..d891a56 100644 --- a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php +++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php @@ -37,8 +37,8 @@ public function getLabel(); /** * Returns a human readable description. * - * @return string - * The description. + * @return string|null + * The description, or NULL if no description is available. */ public function getDescription(); diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module index 7b379e4..1231b43 100644 --- a/core/modules/entity_reference/entity_reference.module +++ b/core/modules/entity_reference/entity_reference.module @@ -23,34 +23,6 @@ function entity_reference_field_info_alter(&$info) { } /** - * Implements hook_entity_field_info_alter(). - * - * Set the "target_type" property definition for entity reference fields. - * - * @see \Drupal\Core\Entity\Plugin\DataType\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(). */ function entity_reference_field_widget_info_alter(&$info) { diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 84fd041..41c3f5f 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -195,16 +195,16 @@ 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() so that it can take field + // instance also. 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(); } } } @@ -213,33 +213,6 @@ function field_entity_field_info($entity_type) { } /** - * 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_field_widget_info_alter(). */ function field_field_widget_info_alter(&$info) { diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php index b09c9bc..11cef9b 100644 --- a/core/modules/field/lib/Drupal/field/Entity/Field.php +++ b/core/modules/field/lib/Drupal/field/Entity/Field.php @@ -12,6 +12,7 @@ use Drupal\Core\Annotation\Translation; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\TypedData\DataDefinition; use Drupal\field\FieldException; use Drupal\field\FieldInterface; @@ -199,6 +200,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. * * @param array $values @@ -582,7 +590,7 @@ public function getFieldLabel() { * {@inheritdoc} */ public function getFieldDescription() { - return ''; + return NULL; } /** @@ -683,4 +691,100 @@ public function __wakeup() { $this->__construct($values); } + /** + * {@inheritdoc} + */ + public function getDataType() { + // Derive list type from the item type. + $item_type_definition = \Drupal::typedData() + ->getDefinition($this->getItemDefinition()->getDataType()); + return isset($item_type_definition['list_type']) ? $item_type_definition['list_type'] : 'field_item_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() { + // For BC check for a defined list class. + // @todo: Remove once all list_class usages have been converted. + $item_type_definition = \Drupal::typedData() + ->getDefinition($this->getItemDefinition()->getDataType()); + + if (isset($item_type_definition['list_class'])) { + return $item_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() + ->setDataType('field_item:' . $this->type) + ->setSettings($this->getFieldSettings()); + } + return $this->itemDefinition; + } } diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php index def3c42..93821ef 100644 --- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php @@ -210,6 +210,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. * * @param array $values @@ -642,4 +649,97 @@ public function __wakeup() { $this->__construct($values); } + /** + * {@inheritdoc} + */ + public function getDataType() { + // Derive list type from the item type. + $item_type_definition = \Drupal::typedData() + ->getDefinition($this->getItemDefinition()->getDataType()); + return isset($item_type_definition['list_type']) ? $item_type_definition['list_type'] : 'field_item_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() { + // For BC check for a defined list class. + // @todo: Remove once all list_class usages have been converted. + $item_type_definition = \Drupal::typedData() + ->getDefinition($this->getItemDefinition()->getDataType()); + + if (isset($item_type_definition['list_class'])) { + return $item_type_definition['list_class']; + } + } + + /** + * {@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() + ->setDataType('field_item:' . $this->field->type) + ->setSettings($this->getFieldSettings()); + } + return $this->itemDefinition; + } + } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php index 1a2a6d0..3000e56 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php @@ -10,6 +10,7 @@ use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Core\Entity\Plugin\DataType\FieldItemList; use Drupal\field\Field as FieldAPI; +use Drupal\field\FieldInstanceInterface; /** * Represents a configurable entity field item list. @@ -28,8 +29,9 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn */ 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; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index 7f6c87e..2149237 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -352,21 +352,18 @@ public function testIntrospection() { */ 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'], 'string_field', $entity_type .': Name field found.'); - $this->assertEqual($definitions['user_id']['type'], 'entity_reference_field', $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'], 'string_field', $entity_type .': Name field found.'); - $this->assertEqual($definitions['user_id']['type'], 'entity_reference_field', $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.');