Commit acbd5fa8 authored by alexpott's avatar alexpott

Issue #2226197 by fago, jessebeach: Introduce FieldStorageDefinitionInterface...

Issue #2226197 by fago, jessebeach: Introduce FieldStorageDefinitionInterface in the Entity Field API.
parent 07f63fe6
......@@ -9,6 +9,7 @@
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\Language;
use Drupal\field\FieldInfo;
use Drupal\field\FieldConfigUpdateForbiddenException;
......@@ -821,7 +822,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() == FieldStorageDefinitionInterface::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.
......@@ -907,7 +908,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() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $field->getCardinality()) {
break;
}
}
......
......@@ -98,6 +98,16 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
*/
protected $fieldDefinitions;
/**
* Static cache of field storage definitions per entity type.
*
* Elements of the array:
* - $entity_type_id: \Drupal\Core\Field\FieldDefinition[]
*
* @var array
*/
protected $fieldStorageDefinitions;
/**
* The root paths.
*
......@@ -332,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.
......@@ -348,8 +358,8 @@ protected function buildBaseFieldDefinitions($entity_type_id) {
$base_field_definitions = $class::baseFieldDefinitions($entity_type);
$provider = $entity_type->getProvider();
foreach ($base_field_definitions as $definition) {
// @todo Remove this check one FieldDefinitionInterface exposes a proper
// provider setter. See https://drupal.org/node/2225961.
// @todo Remove this check once FieldDefinitionInterface exposes a proper
// provider setter. See https://drupal.org/node/2225961.
if ($definition instanceof FieldDefinition) {
$definition->setProvider($provider);
}
......@@ -362,8 +372,8 @@ protected function buildBaseFieldDefinitions($entity_type_id) {
// Ensure the provider key actually matches the name of the provider
// defining the field.
foreach ($module_definitions as $field_name => $definition) {
// @todo Remove this check one FieldDefinitionInterface exposes a
// proper provider setter. See https://drupal.org/node/2225961.
// @todo Remove this check once FieldDefinitionInterface exposes a
// proper provider setter. See https://drupal.org/node/2225961.
if ($definition instanceof FieldDefinition) {
$definition->setProvider($module);
}
......@@ -445,8 +455,8 @@ protected function buildBundleFieldDefinitions($entity_type_id, $bundle, array $
$bundle_field_definitions = $class::bundleFieldDefinitions($entity_type, $bundle, $base_field_definitions);
$provider = $entity_type->getProvider();
foreach ($bundle_field_definitions as $definition) {
// @todo Remove this check one FieldDefinitionInterface exposes a proper
// provider setter. See https://drupal.org/node/2225961.
// @todo Remove this check once FieldDefinitionInterface exposes a proper
// provider setter. See https://drupal.org/node/2225961.
if ($definition instanceof FieldDefinition) {
$definition->setProvider($provider);
}
......@@ -459,8 +469,8 @@ protected function buildBundleFieldDefinitions($entity_type_id, $bundle, array $
// Ensure the provider key actually matches the name of the provider
// defining the field.
foreach ($module_definitions as $field_name => $definition) {
// @todo Remove this check one FieldDefinitionInterface exposes a
// proper provider setter. See https://drupal.org/node/2225961.
// @todo Remove this check once FieldDefinitionInterface exposes a
// proper provider setter. See https://drupal.org/node/2225961.
if ($definition instanceof FieldDefinition) {
$definition->setProvider($module);
}
......@@ -483,12 +493,77 @@ protected function buildBundleFieldDefinitions($entity_type_id, $bundle, array $
return $bundle_field_definitions;
}
/**
* {@inheritdoc}
*/
public function getFieldStorageDefinitions($entity_type_id) {
if (!isset($this->fieldStorageDefinitions[$entity_type_id])) {
$this->fieldStorageDefinitions[$entity_type_id] = array();
// Add all non-computed base fields.
foreach ($this->getBaseFieldDefinitions($entity_type_id) as $field_name => $definition) {
if (!$definition->isComputed()) {
$this->fieldStorageDefinitions[$entity_type_id][$field_name] = $definition;
}
}
// Not prepared, try to load from cache.
$cid = 'entity_field_storage_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->id;
if ($cache = $this->cache->get($cid)) {
$field_storage_definitions = $cache->data;
}
else {
// Rebuild the definitions and put it into the cache.
$field_storage_definitions = $this->buildFieldStorageDefinitions($entity_type_id);
$this->cache->set($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
}
$this->fieldStorageDefinitions[$entity_type_id] += $field_storage_definitions;
}
return $this->fieldStorageDefinitions[$entity_type_id];
}
/**
* Builds field storage definitions for an entity type.
*
* @param string $entity_type_id
* The entity type ID. Only entity types that implement
* \Drupal\Core\Entity\ContentEntityInterface are supported
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
* An array of field storage definitions, keyed by field name.
*/
protected function buildFieldStorageDefinitions($entity_type_id) {
$entity_type = $this->getDefinition($entity_type_id);
$field_definitions = array();
// Retrieve base field definitions from modules.
foreach ($this->moduleHandler->getImplementations('entity_field_storage_info') as $module) {
$module_definitions = $this->moduleHandler->invoke($module, 'entity_field_storage_info', array($entity_type));
if (!empty($module_definitions)) {
// Ensure the provider key actually matches the name of the provider
// defining the field.
foreach ($module_definitions as $field_name => $definition) {
// @todo Remove this check once FieldDefinitionInterface exposes a
// proper provider setter. See https://drupal.org/node/2225961.
if ($definition instanceof FieldDefinition) {
$definition->setProvider($module);
}
$field_definitions[$field_name] = $definition;
}
}
}
// Invoke alter hook.
$this->moduleHandler->alter('entity_field_storage_info', $field_definitions, $entity_type);
return $field_definitions;
}
/**
* {@inheritdoc}
*/
public function clearCachedFieldDefinitions() {
$this->baseFieldDefinitions = array();
$this->fieldDefinitions = array();
$this->fieldStorageDefinitions = array();
Cache::deleteTags(array('entity_field_info' => TRUE));
}
......
......@@ -56,6 +56,26 @@ public function getBaseFieldDefinitions($entity_type_id);
*/
public function getFieldDefinitions($entity_type_id, $bundle);
/**
* Gets the field storage definitions for a content entity type.
*
* This returns all field storage definitions for base fields and bundle
* fields of an entity type. Note that field storage definitions of a base
* field equal the full base field definition (i.e. they implement
* FieldDefinitionInterface), while the storage definitions for bundle fields
* may implement FieldStorageDefinitionInterface only.
*
* @param string $entity_type_id
* The entity type ID. Only content entities are supported.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
* The array of field storage definitions for the entity type, keyed by
* field name.
*
* @see \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
public function getFieldStorageDefinitions($entity_type_id);
/**
* Creates a new access controller instance.
*
......
......@@ -67,6 +67,37 @@ public static function create($type) {
return $field_definition;
}
/**
* Creates a new field definition based upon a field storage definition.
*
* In cases where one needs a field storage definitions to act like full
* field definitions, this creates a new field definition based upon the
* (limited) information available. That way it is possible to use the field
* definition in places where a full field definition is required; e.g., with
* widgets or formatters.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
* The field storage definition to base the new field definition upon.
*
* @return $this
*/
public static function createFromFieldStorageDefinition(FieldStorageDefinitionInterface $definition) {
return static::create($definition->getType())
->setCardinality($definition->getCardinality())
->setConstraints($definition->getConstraints())
->setCustomStorage($definition->hasCustomStorage())
->setDescription($definition->getDescription())
->setLabel($definition->getLabel())
->setName($definition->getName())
->setProvider($definition->getProvider())
->setQueryable($definition->isQueryable())
->setRequired($definition->isRequired())
->setRevisionable($definition->isRevisionable())
->setSettings($definition->getSettings())
->setTargetEntityTypeId($definition->getTargetEntityTypeId())
->setTranslatable($definition->isTranslatable());
}
/**
* {@inheritdoc}
*/
......
......@@ -52,80 +52,7 @@
* 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 extends ListDataDefinitionInterface {
/**
* 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,
* if the field name is "foo", then $entity->foo returns its data.
*
* @return string
* The field name.
*/
public function getName();
/**
* Returns the field type.
*
* @return string
* The field type, i.e. the id of a field type plugin. For example 'text'.
*
* @see \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
public function getType();
/**
* Returns the field settings.
*
* Each field type defines the settings that are meaningful for that type.
* For example, a text field can define a 'max_length' setting, and an image
* field can define a 'alt_field_required' setting.
*
* @return array
* An array of key/value pairs.
*/
public function getSettings();
/**
* Returns the value of a given field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
public function getSetting($setting_name);
/**
* Returns the name of the provider of this field.
*
* @return string
* The provider name; e.g., the module name.
*/
public function getProvider();
/**
* Returns whether the field is translatable.
*
* @return bool
* TRUE if the field is translatable.
*/
public function isTranslatable();
/**
* Returns whether the field is revisionable.
*
* @return bool
* TRUE if the field is revisionable.
*/
public function isRevisionable();
interface FieldDefinitionInterface extends FieldStorageDefinitionInterface, ListDataDefinitionInterface {
/**
* Returns whether the display for the field can be configured.
......@@ -173,45 +100,6 @@ public function isDisplayConfigurable($display_context);
*/
public function getDisplayOptions($display_context);
/**
* Determines whether the field is queryable via QueryInterface.
*
* @return bool
* TRUE if the field is queryable.
*/
public function isQueryable();
/**
* Returns the human-readable label for the field.
*
* @return string
* The field label.
*/
public function getLabel();
/**
* Returns the human-readable description for the field.
*
* This is displayed in addition to the label in places where additional
* descriptive information is helpful. For example, as help text below the
* form element in entity edit forms.
*
* @return string|null
* The field description, or NULL if no description is available.
*/
public function getDescription();
/**
* Returns the maximum number of items allowed for the field.
*
* Possible values are positive integers or
* FieldDefinitionInterface::CARDINALITY_UNLIMITED.
*
* @return integer
* The field cardinality.
*/
public function getCardinality();
/**
* Returns whether at least one non-empty item is required for this field.
*
......@@ -223,14 +111,6 @@ public function getCardinality();
*/
public function isRequired();
/**
* Returns whether the field can contain multiple items.
*
* @return bool
* TRUE if the field can contain multiple items, FALSE otherwise.
*/
public function isMultiple();
/**
* Returns the default value for the field in a newly created entity.
*
......@@ -248,112 +128,4 @@ public function isMultiple();
*/
public function getDefaultValue(EntityInterface $entity);
/**
* Gets the definition of a contained property.
*
* @param string $name
* The name of property.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface|null
* The definition of the property or NULL if the property does not exist.
*/
public function getPropertyDefinition($name);
/**
* Gets an array of property definitions of contained properties.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface[]
* An array of property definitions of contained properties, keyed by
* property name.
*/
public function getPropertyDefinitions();
/**
* Returns the names of the field's subproperties.
*
* A field is a list of items, and each item can contain one or more
* properties. All items for a given field contain the same property names,
* but the values can be different for each item.
*
* For example, an email field might just contain a single 'value' property,
* while a link field might contain 'title' and 'url' properties, and a text
* field might contain 'value', 'summary', and 'format' properties.
*
* @return array
* The property names.
*/
public function getPropertyNames();
/**
* Returns the name of the main property, if any.
*
* Some field items consist mainly of one main property, e.g. the value of a
* text field or the @code target_id @endcode of an entity reference. If the
* field item has no main property, the method returns NULL.
*
* @return string|null
* The name of the value property, or NULL if there is none.
*/
public function getMainPropertyName();
/**
* Returns the ID of the type of the entity this field is attached to.
*
* This method should not be confused with EntityInterface::entityType()
* (configurable fields are config entities, and thus implement both
* interfaces):
* - FieldDefinitionInterface::getTargetEntityTypeId() answers "as a field,
* which entity type are you attached to?".
* - EntityInterface::getEntityTypeId() answers "as a (config) entity, what
* is your own entity type".
*
* @return string
* The name of the entity type.
*/
public function getTargetEntityTypeId();
/**
* Returns the field schema.
*
* Note that this method returns an empty array for computed fields which have
* no schema.
*
* @return array
* The field schema, as an array of key/value pairs in the format returned
* by hook_field_schema():
* - columns: An array of Schema API column specifications, keyed by column
* name. This specifies what comprises a single value for a given field.
* No assumptions should be made on how storage backends internally use
* the original column name to structure their storage.
* - indexes: An array of Schema API index definitions. Some storage
* backends might not support indexes.
* - foreign keys: An array of Schema API foreign key definitions. Note,
* however, that depending on the storage backend specified for the field,
* the field data is not necessarily stored in SQL.
*/
public function getSchema();
/**
* Returns the field columns, as defined in the field schema.
*
* @return array
* The array of field columns, keyed by column name, in the same format
* returned by getSchema().
*
* @see \Drupal\Core\Field\FieldDefinitionInterface::getSchema()
*/
public function getColumns();
/**
* Returns the storage behavior for this field.
*
* Indicates whether the entity type's storage should take care of storing the
* field values or whether it is handled separately; e.g. by the
* module providing the field.
*
* @return bool
* FALSE if the storage takes care of storing the field, TRUE otherwise.
*/
public function hasCustomStorage();
}
......@@ -32,7 +32,7 @@ interface FieldItemInterface extends ComplexDataInterface {
*
* @see \Drupal\Core\Field\FieldDefinition
*/
public static function propertyDefinitions(FieldDefinitionInterface $field_definition);
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition);
/**
* Returns the name of the main property, if any.
......@@ -57,7 +57,7 @@ public static function mainPropertyName();
*
* Computed fields having no schema should return an empty array.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_definition
* The field definition.
*
* @return array
......@@ -81,7 +81,7 @@ public static function mainPropertyName();
* specify another field as related, only existing SQL tables,
* such as {taxonomy_term_data}.
*/
public static function schema(FieldDefinitionInterface $field_definition);
public static function schema(FieldStorageDefinitionInterface $field_definition);
/**
* Gets the entity that field belongs to.
......
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionInterface.
*/
namespace Drupal\Core\Field;
/**
* Defines an interface for entity field storage definitions.
*
* Field storage definitions represent the part of full field definitions (see
* FieldDefinitionInterface) that is responsible for defining how the field is
* stored. While field definitions may differ by entity bundle, all of those
* bundle fields have to share the same common field storage definition. Thus,
* the storage definitions can be defined by entity type only.
* The bundle fields corresponding to a field storage definition may provide
* additional information; e.g., they may provide bundle-specific settings or
* constraints that are not present in the storage definition. However bundle
* fields may not override or alter any information provided by the storage
* definition except for the label and the description; e.g., any constraints
* and settings on the storage definition must be present on the bundle field as
* well.
*
* @see hook_entity_field_storage_info()
*/
interface FieldStorageDefinitionInterface {
/**
* 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,
* if the field name is "foo", then $entity->foo returns its data.
*
* @return string
* The field name.
*/
public function getName();
/**
* Returns the field type.
*
* @return string
* The field type, i.e. the id of a field type plugin. For example 'text'.
*
* @see \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
public function getType();
/**
* Returns the field settings.
*
* Each field type defines the settings that are meaningful for that type.
* For example, a text field can define a 'max_length' setting, and an image
* field can define a 'alt_field_required' setting.
*
* @return mixed[]
* An array of key/value pairs.
*/
public function getSettings();
/**
* Returns the value of a given field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
public function getSetting($setting_name);
/**
* Returns whether the field is translatable.
*
* @return bool
* TRUE if the field is translatable.
*/
public function isTranslatable();
/**
* Returns whether the field is revisionable.
*
* @return bool
* TRUE if the field is revisionable.
*/
public function isRevisionable();
/**
* Determines whether the field is queryable via QueryInterface.
*
* @return bool
* TRUE if the field is queryable.
*/
public function isQueryable();
/**
* Returns the human-readable label for the field.
*
* @return string
* The field label.
*/
public function getLabel();
/**
* Returns the human-readable description for the field.
*
* This is displayed in addition to the label in places where additional
* descriptive information is helpful. For example, as help text below the
* form element in entity edit forms.
*
* @return string|null
* The field description, or NULL if no description is available.
*/
public function getDescription();
/**
* Returns whether the field can contain multiple items.
*
* @return bool
* TRUE if the field can contain multiple items, FALSE otherwise.
*/
public function isMultiple();
/**
* Returns the maximum number of items allowed for the field.
*
* Possible values are positive integers or
* FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED.
*
* @return int
* The field cardinality.
*/
public function getCardinality();
/**
* Gets the definition of a contained property.
*
* @param string $name
* The name of property.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface|null
* The definition of the property or NULL if the property does not exist.
*/
public function getPropertyDefinition($name);
/**
* Gets an array of property definitions of contained properties.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface[]
* An array of property definitions of contained properties, keyed by
* property name.
*/
public function getPropertyDefinitions();
/**
* Returns the names of the field's subproperties.
*
* A field is a list of items, and each item can contain one or more
* properties. All items for a given field contain the same property names,
* but the values can be different for each item.
*
* For example, an email field might just contain a single 'value' property,
* while a link field might contain 'title' and 'url' properties, and a text
* field might contain 'value', 'summary', and 'format' properties.
*
* @return string[]
* The property names.
*/
public function getPropertyNames();
/**
* Returns the name of the main property, if any.
*
* Some field items consist mainly of one main property, e.g. the value of a
* text field or the @code target_id @endcode of an entity reference. If the
* field item has no main property, the method returns NULL.
*
* @return string|null
* The name of the value property, or NULL if there is none.
*/