diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index 6b7de03..927b934 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -9,6 +9,7 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Entity\Query\QueryInterface; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Language\Language; use Drupal\field\FieldInfo; use Drupal\field\FieldConfigUpdateForbiddenException; @@ -822,7 +823,7 @@ protected function doLoadFieldItems($entities, $age) { $delta_count[$row->entity_id][$row->langcode] = 0; } - if ($field->getCardinality() == FieldConfigInterface::CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field->getCardinality()) { + if ($field->getCardinality() == FieldDefinitionInterface::CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field->getCardinality()) { $item = array(); // For each column declared by the field, populate the item from the // prefixed database column. @@ -908,7 +909,7 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { $query->values($record); $revision_query->values($record); - if ($field->getCardinality() != FieldConfigInterface::CARDINALITY_UNLIMITED && ++$delta_count == $field->getCardinality()) { + if ($field->getCardinality() != FieldDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $field->getCardinality()) { break; } } diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 9a30d41..93f49ae 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -342,7 +342,7 @@ public function getBaseFieldDefinitions($entity_type_id) { * * @param string $entity_type_id * The entity type ID. Only entity types that implement - * \Drupal\Core\Entity\ContentEntityInterface are supported + * \Drupal\Core\Entity\ContentEntityInterface are supported. * * @return \Drupal\Core\Field\FieldDefinitionInterface[] * An array of field definitions, keyed by field name. @@ -498,7 +498,13 @@ protected function buildBundleFieldDefinitions($entity_type_id, $bundle, array $ */ public function getStorageFieldDefinitions($entity_type_id) { if (!isset($this->storageFieldDefinitions[$entity_type_id])) { - $base_field_definitions = $this->getBaseFieldDefinitions($entity_type_id); + $this->storageFieldDefinitions[$entity_type_id] = array(); + // Add all non-computed base fields. + foreach ($this->getBaseFieldDefinitions($entity_type_id) as $field_name => $definition) { + if (!$definition->isComputed()) { + $this->storageFieldDefinitions[$entity_type_id][$field_name] = $definition; + } + } // Not prepared, try to load from cache. $cid = 'entity_storage_field_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->id; if ($cache = $this->cache->get($cid)) { @@ -509,7 +515,7 @@ public function getStorageFieldDefinitions($entity_type_id) { $storage_field_definitions = $this->buildStorageFieldDefinitions($entity_type_id); $this->cache->set($cid, $storage_field_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE)); } - $this->storageFieldDefinitions[$entity_type_id] = $base_field_definitions + $storage_field_definitions; + $this->storageFieldDefinitions[$entity_type_id] += $storage_field_definitions; } return $this->storageFieldDefinitions[$entity_type_id]; } diff --git a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php index 977fce5..c3b54b2 100644 --- a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php +++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php @@ -55,6 +55,22 @@ interface FieldDefinitionInterface extends StorageFieldDefinitionInterface, ListDataDefinitionInterface { /** + * Value indicating a field accepts an unlimited number of values. + */ + const CARDINALITY_UNLIMITED = -1; + + /** + * Returns the maximum number of items allowed for the field. + * + * Possible values are positive integers or + * FieldDefinitionInterface::CARDINALITY_UNLIMITED. + * + * @return int + * The field cardinality. + */ + public function getCardinality(); + + /** * Returns whether the display for the field can be configured. * * @param string $display_context @@ -101,6 +117,17 @@ public function isDisplayConfigurable($display_context); public function getDisplayOptions($display_context); /** + * Returns whether at least one non-empty item is required for this field. + * + * Currently, required-ness is only enforced at the Form API level in entity + * edit forms, not during direct API saves. + * + * @return bool + * TRUE if the field is required. + */ + public function isRequired(); + + /** * Returns the default value for the field in a newly created entity. * * @param \Drupal\Core\Entity\EntityInterface $entity diff --git a/core/lib/Drupal/Core/Field/StorageFieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/StorageFieldDefinitionInterface.php index e1751b8..5c62dc3 100644 --- a/core/lib/Drupal/Core/Field/StorageFieldDefinitionInterface.php +++ b/core/lib/Drupal/Core/Field/StorageFieldDefinitionInterface.php @@ -15,17 +15,18 @@ * stored. While field definitions may differ by entity bundle, all of those * bundle fields have to share the same common field storage definition. Thus, * field storage definitions can be defined by entity type only. + * The bundle fields corresponding to a storage field may provide additional + * information; e.g., they may provide bundle-specific settings or constraints + * that are not present in the storage field. However bundle fields may not + * override or alter any information provided by the storage field except for + * the label and the description; e.g., any constraints and settings on the + * storage field must be present on the bundle field as well. * * @see hook_entity_storage_field_info() */ interface StorageFieldDefinitionInterface { /** - * Value indicating a field accepts an unlimited number of values. - */ - const CARDINALITY_UNLIMITED = -1; - - /** * Returns the machine name of the field. * * This defines how the field data is accessed from the entity. For example, @@ -114,28 +115,6 @@ public function getLabel(); public function getDescription(); /** - * Returns the maximum number of items allowed for the field. - * - * Possible values are positive integers or - * FieldDefinitionInterface::CARDINALITY_UNLIMITED. - * - * @return int - * The field cardinality. - */ - public function getCardinality(); - - /** - * Returns whether at least one non-empty item is required for this field. - * - * Currently, required-ness is only enforced at the Form API level in entity - * edit forms, not during direct API saves. - * - * @return bool - * TRUE if the field is required. - */ - public function isRequired(); - - /** * Returns whether the field can contain multiple items. * * @return bool diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php index 7281d7e..69fb841 100644 --- a/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\field\FieldException; use Drupal\field\FieldConfigInterface; @@ -616,7 +617,7 @@ public function isRequired() { */ public function isMultiple() { $cardinality = $this->getCardinality(); - return ($cardinality == static::CARDINALITY_UNLIMITED) || ($cardinality > 1); + return ($cardinality == FieldDefinitionInterface::CARDINALITY_UNLIMITED) || ($cardinality > 1); } /** diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php index a2dca38..b50c661 100644 --- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php +++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php @@ -26,7 +26,6 @@ class HiddenTestItem extends TestItem { /** * {@inheritdoc} - */ public static function propertyDefinitions(StorageFieldDefinitionInterface $field_definition) { $properties['value'] = DataDefinition::create('integer') diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php index 174f071..acb35cb 100644 --- a/core/modules/system/entity.api.php +++ b/core/modules/system/entity.api.php @@ -707,7 +707,8 @@ function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityT * Provides field definitions for a specific bundle within an entity type. * * Bundle fields either have to override an existing base field, or need to - * provide a storage field via hook_entity_storage_field_info(). + * provide a storage field via hook_entity_storage_field_info() unless they are + * computed. * * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type * The entity type definition. diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php index a0a72de..4fe8000 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php @@ -487,12 +487,19 @@ public function testGetStorageFieldDefinitions() { ->method('getName') ->will($this->returnValue('storage_field')); - $this->moduleHandler->expects($this->any()) + $this->moduleHandler->expects($this->at(0)) ->method('getImplementations') - ->will($this->returnValue(array('phpunit'))); + ->with('entity_base_field_info') + ->will($this->returnValue(array())); + + $this->moduleHandler->expects($this->at(2)) + ->method('getImplementations') + ->with('entity_storage_field_info') + ->will($this->returnValue(array('example_module'))); $this->moduleHandler->expects($this->any()) ->method('invoke') + ->with('example_module', 'entity_storage_field_info') ->will($this->returnValue(array('storage_field' => $storage_field_definition))); $expected = array( @@ -577,11 +584,11 @@ public function testGetStorageFieldDefinitionsWithCaching() { $this->moduleHandler->expects($this->any()) ->method('getImplementations') ->with('entity_storage_field_info') - ->will($this->returnValue(array('phpunit'))); + ->will($this->returnValue(array('example_module'))); $this->moduleHandler->expects($this->once()) ->method('invoke') - ->with('phpunit') + ->with('example_module') ->will($this->returnValue(array('storage_field' => $storage_field_definition))); $expected = array(