diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 8071a0d..d49f5cd 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -102,6 +102,12 @@ function entity_get_bundles($entity_type = NULL) {
*/
function entity_invoke_bundle_hook($hook, $entity_type, $bundle, $bundle_new = NULL) {
entity_info_cache_clear();
+
+ // Notify the entity storage controller.
+ $method = 'onBundle' . ucfirst($hook);
+ Drupal::entityManager()->getStorageController($entity_type)->$method($bundle, $bundle_new);
+
+ // Invoke hook_entity_bundle_*() hooks.
Drupal::moduleHandler()->invokeAll('entity_bundle_' . $hook, array($entity_type, $bundle, $bundle_new));
}
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
index 14d3f18..0d9bd71 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
@@ -75,4 +75,14 @@ public function setStatus($status);
*/
public function status();
+ /**
+ * Retrieves the exportable properties of the entity.
+ *
+ * These are the values that get saved into config.
+ *
+ * @return array
+ * An array of exportable properties and their values.
+ */
+ public function getExportProperties();
+
}
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index bbf1ae0..2713085 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -7,13 +7,15 @@
namespace Drupal\Core\Entity;
-use Drupal\Core\Language\Language;
-use PDO;
-use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\Query\QueryInterface;
-use Drupal\Component\Uuid\Uuid;
use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Database\Connection;
+use Drupal\Component\Uuid\Uuid;
+use Drupal\field\FieldInfo;
+use Drupal\field\FieldUpdateForbiddenException;
+use Drupal\field\FieldInterface;
+use Drupal\field\FieldInstanceInterface;
+use Drupal\field\Entity\Field;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -24,7 +26,7 @@
* This class can be used as-is by most simple entity types. Entity types
* requiring special handling can extend the class.
*/
-class DatabaseStorageController extends EntityStorageControllerBase {
+class DatabaseStorageController extends FieldableEntityStorageControllerBase {
/**
* Name of entity's revision database table field, if it supports revisions.
@@ -59,13 +61,21 @@ class DatabaseStorageController extends EntityStorageControllerBase {
protected $database;
/**
+ * The field info object.
+ *
+ * @var \Drupal\field\FieldInfo
+ */
+ protected $fieldInfo;
+
+ /**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
return new static(
$entity_type,
$entity_info,
- $container->get('database')
+ $container->get('database'),
+ $container->get('field.info')
);
}
@@ -78,11 +88,14 @@ public static function createInstance(ContainerInterface $container, $entity_typ
* An array of entity info for the entity type.
* @param \Drupal\Core\Database\Connection $database
* The database connection to be used.
+ * @param \Drupal\field\FieldInfo $field_info
+ * The field info service.
*/
- public function __construct($entity_type, array $entity_info, Connection $database) {
+ public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
parent::__construct($entity_type, $entity_info);
$this->database = $database;
+ $this->fieldInfo = $field_info;
// Check if the entity type supports IDs.
if (isset($this->entityInfo['entity_keys']['id'])) {
@@ -143,7 +156,7 @@ public function loadMultiple(array $ids = NULL) {
// We provide the necessary arguments for PDO to create objects of the
// specified entity class.
// @see Drupal\Core\Entity\EntityInterface::__construct()
- $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
+ $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
}
$queried_entities = $query_result->fetchAllAssoc($this->idKey);
}
@@ -196,7 +209,7 @@ public function loadRevision($revision_id) {
// We provide the necessary arguments for PDO to create objects of the
// specified entity class.
// @see Drupal\Core\Entity\EntityInterface::__construct()
- $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
+ $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
}
$queried_entities = $query_result->fetchAllAssoc($this->idKey);
@@ -223,6 +236,7 @@ public function deleteRevision($revision_id) {
->condition($this->revisionKey, $revision->getRevisionId())
->execute();
$this->invokeFieldMethod('deleteRevision', $revision);
+ $this->deleteFieldItemsRevision($revision);
$this->invokeHook('revision_delete', $revision);
}
}
@@ -335,14 +349,9 @@ protected function buildQuery($ids, $revision_id = FALSE) {
* (optional) TRUE if the revision should be loaded, defaults to FALSE.
*/
protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
- // Attach fields.
+ // Attach field values.
if ($this->entityInfo['fieldable']) {
- if ($load_revision) {
- field_attach_load_revision($this->entityType, $queried_entities);
- }
- else {
- field_attach_load($this->entityType, $queried_entities);
- }
+ $this->loadFieldItems($queried_entities, $load_revision ? FIELD_LOAD_REVISION : FIELD_LOAD_CURRENT);
}
// Call hook_entity_load().
@@ -416,6 +425,7 @@ public function delete(array $entities) {
$entity_class::postDelete($this, $entities);
foreach ($entities as $entity) {
$this->invokeFieldMethod('delete', $entity);
+ $this->deleteFieldItems($entity);
$this->invokeHook('delete', $entity);
}
// Ignore slave server temporarily.
@@ -458,6 +468,7 @@ public function save(EntityInterface $entity) {
$this->resetCache(array($entity->id()));
$entity->postSave($this, TRUE);
$this->invokeFieldMethod('update', $entity);
+ $this->saveFieldItems($entity, TRUE);
$this->invokeHook('update', $entity);
}
else {
@@ -471,6 +482,7 @@ public function save(EntityInterface $entity) {
$entity->enforceIsNew(FALSE);
$entity->postSave($this, FALSE);
$this->invokeFieldMethod('insert', $entity);
+ $this->saveFieldItems($entity, FALSE);
$this->invokeHook('insert', $entity);
}
@@ -490,7 +502,7 @@ public function save(EntityInterface $entity) {
/**
* Saves an entity revision.
*
- * @param Drupal\Core\Entity\EntityInterface $entity
+ * @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*/
protected function saveRevision(EntityInterface $entity) {
@@ -528,31 +540,6 @@ protected function saveRevision(EntityInterface $entity) {
}
/**
- * Invokes a hook on behalf of the entity.
- *
- * @param $hook
- * One of 'presave', 'insert', 'update', 'predelete', 'delete', or
- * 'revision_delete'.
- * @param $entity
- * The entity object.
- */
- protected function invokeHook($hook, EntityInterface $entity) {
- $function = 'field_attach_' . $hook;
- // @todo: field_attach_delete_revision() is named the wrong way round,
- // consider renaming it.
- if ($function == 'field_attach_revision_delete') {
- $function = 'field_attach_delete_revision';
- }
- if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
- $function($entity);
- }
- // Invoke the hook.
- module_invoke_all($this->entityType . '_' . $hook, $entity);
- // Invoke the respective entity-level hook.
- module_invoke_all('entity_' . $hook, $entity, $this->entityType);
- }
-
- /**
* {@inheritdoc}
*/
public function baseFieldDefinitions() {
@@ -566,4 +553,644 @@ public function baseFieldDefinitions() {
public function getQueryServiceName() {
return 'entity.query.sql';
}
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doLoadFieldItems($entities, $age) {
+ $load_current = $age == FIELD_LOAD_CURRENT;
+
+ // Collect entities ids and bundles.
+ $bundles = array();
+ $ids = array();
+ foreach ($entities as $key => $entity) {
+ $bundles[$entity->bundle()] = TRUE;
+ $ids[] = $load_current ? $key : $entity->getRevisionId();
+ }
+
+ // Collect impacted fields.
+ $fields = array();
+ foreach ($bundles as $bundle => $v) {
+ foreach ($this->fieldInfo->getBundleInstances($this->entityType, $bundle) as $field_name => $instance) {
+ $fields[$field_name] = $instance->getField();
+ }
+ }
+
+ // Load field data.
+ foreach ($fields as $field_name => $field) {
+ $table = $load_current ? static::_fieldTableName($field) : static::_fieldRevisionTableName($field);
+
+ $results = $this->database->select($table, 't')
+ ->fields('t')
+ ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+ ->condition('langcode', field_available_languages($this->entityType, $field), 'IN')
+ ->orderBy('delta')
+ ->condition('deleted', 0)
+ ->execute();
+
+ $delta_count = array();
+ foreach ($results as $row) {
+ if (!isset($delta_count[$row->entity_id][$row->langcode])) {
+ $delta_count[$row->entity_id][$row->langcode] = 0;
+ }
+
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
+ $item = array();
+ // For each column declared by the field, populate the item from the
+ // prefixed database column.
+ foreach ($field['columns'] as $column => $attributes) {
+ $column_name = static::_fieldColumnName($field, $column);
+ // Unserialize the value if specified in the column schema.
+ $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
+ }
+
+ // Add the item to the field values for the entity.
+ $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
+ $delta_count[$row->entity_id][$row->langcode]++;
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSaveFieldItems(EntityInterface $entity, $update) {
+ $vid = $entity->getRevisionId();
+ $id = $entity->id();
+ $bundle = $entity->bundle();
+ $entity_type = $entity->entityType();
+ if (!isset($vid)) {
+ $vid = $id;
+ }
+
+ foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
+ $field = $instance->getField();
+ $table_name = static::_fieldTableName($field);
+ $revision_name = static::_fieldRevisionTableName($field);
+
+ $all_langcodes = field_available_languages($entity_type, $field);
+ $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
+
+ // Delete and insert, rather than update, in case a value was added.
+ if ($update) {
+ // Delete language codes present in the incoming $entity->$field_name.
+ // Delete all language codes if $entity->$field_name is empty.
+ $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
+ if ($langcodes) {
+ // Only overwrite the field's base table if saving the default revision
+ // of an entity.
+ if ($entity->isDefaultRevision()) {
+ $this->database->delete($table_name)
+ ->condition('entity_id', $id)
+ ->condition('langcode', $langcodes, 'IN')
+ ->execute();
+ }
+ $this->database->delete($revision_name)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->condition('langcode', $langcodes, 'IN')
+ ->execute();
+ }
+ }
+
+ // Prepare the multi-insert query.
+ $do_insert = FALSE;
+ $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
+ foreach ($field['columns'] as $column => $attributes) {
+ $columns[] = static::_fieldColumnName($field, $column);
+ }
+ $query = $this->database->insert($table_name)->fields($columns);
+ $revision_query = $this->database->insert($revision_name)->fields($columns);
+
+ foreach ($field_langcodes as $langcode) {
+ $items = (array) $entity->{$field_name}[$langcode];
+ $delta_count = 0;
+ foreach ($items as $delta => $item) {
+ // We now know we have someting to insert.
+ $do_insert = TRUE;
+ $record = array(
+ 'entity_id' => $id,
+ 'revision_id' => $vid,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'langcode' => $langcode,
+ );
+ foreach ($field['columns'] as $column => $attributes) {
+ $column_name = static::_fieldColumnName($field, $column);
+ $value = isset($item[$column]) ? $item[$column] : NULL;
+ // Serialize the value if specified in the column schema.
+ $record[$column_name] = (!empty($attributes['serialize'])) ? serialize($value) : $value;
+ }
+ $query->values($record);
+ if (isset($vid)) {
+ $revision_query->values($record);
+ }
+
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+ break;
+ }
+ }
+ }
+
+ // Execute the query if we have values to insert.
+ if ($do_insert) {
+ // Only overwrite the field's base table if saving the default revision
+ // of an entity.
+ if ($entity->isDefaultRevision()) {
+ $query->execute();
+ }
+ $revision_query->execute();
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteFieldItems(EntityInterface $entity) {
+ foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
+ $field = $instance->getField();
+ $table_name = static::_fieldTableName($field);
+ $revision_name = static::_fieldRevisionTableName($field);
+ $this->database->delete($table_name)
+ ->condition('entity_id', $entity->id())
+ ->execute();
+ $this->database->delete($revision_name)
+ ->condition('entity_id', $entity->id())
+ ->execute();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteFieldItemsRevision(EntityInterface $entity) {
+ $vid = $entity->getRevisionId();
+ if (isset($vid)) {
+ foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
+ $revision_name = static::_fieldRevisionTableName($instance->getField());
+ $this->database->delete($revision_name)
+ ->condition('entity_id', $entity->id())
+ ->condition('revision_id', $vid)
+ ->execute();
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldCreate(FieldInterface $field) {
+ $schema = $this->_fieldSqlSchema($field);
+ foreach ($schema as $name => $table) {
+ $this->database->schema()->createTable($name, $table);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldUpdate(FieldInterface $field) {
+ $original = $field->original;
+
+ if (!$field->hasData()) {
+ // There is no data. Re-create the tables completely.
+
+ if ($this->database->supportsTransactionalDDL()) {
+ // If the database supports transactional DDL, we can go ahead and rely
+ // on it. If not, we will have to rollback manually if something fails.
+ $transaction = $this->database->startTransaction();
+ }
+
+ try {
+ $original_schema = $this->_fieldSqlSchema($original);
+ foreach ($original_schema as $name => $table) {
+ $this->database->schema()->dropTable($name, $table);
+ }
+ $schema = $this->_fieldSqlSchema($field);
+ foreach ($schema as $name => $table) {
+ $this->database->schema()->createTable($name, $table);
+ }
+ }
+ catch (\Exception $e) {
+ if ($this->database->supportsTransactionalDDL()) {
+ $transaction->rollback();
+ }
+ else {
+ // Recreate tables.
+ $original_schema = $this->_fieldSqlSchema($original);
+ foreach ($original_schema as $name => $table) {
+ if (!$this->database->schema()->tableExists($name)) {
+ $this->database->schema()->createTable($name, $table);
+ }
+ }
+ }
+ throw $e;
+ }
+ }
+ else {
+ if ($field['columns'] != $original['columns']) {
+ throw new FieldUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
+ }
+ // There is data, so there are no column changes. Drop all the prior
+ // indexes and create all the new ones, except for all the priors that
+ // exist unchanged.
+ $table = static::_fieldTableName($original);
+ $revision_table = static::_fieldRevisionTableName($original);
+
+ $schema = $field->getSchema();
+ $original_schema = $original->getSchema();
+
+ foreach ($original_schema['indexes'] as $name => $columns) {
+ if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
+ $real_name = static::_fieldIndexName($field, $name);
+ $this->database->schema()->dropIndex($table, $real_name);
+ $this->database->schema()->dropIndex($revision_table, $real_name);
+ }
+ }
+ $table = static::_fieldTableName($field);
+ $revision_table = static::_fieldRevisionTableName($field);
+ foreach ($schema['indexes'] as $name => $columns) {
+ if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
+ $real_name = static::_fieldIndexName($field, $name);
+ $real_columns = array();
+ foreach ($columns as $column_name) {
+ // Indexes can be specified as either a column name or an array with
+ // column name and length. Allow for either case.
+ if (is_array($column_name)) {
+ $real_columns[] = array(
+ static::_fieldColumnName($field, $column_name[0]),
+ $column_name[1],
+ );
+ }
+ else {
+ $real_columns[] = static::_fieldColumnName($field, $column_name);
+ }
+ }
+ $this->database->schema()->addIndex($table, $real_name, $real_columns);
+ $this->database->schema()->addIndex($revision_table, $real_name, $real_columns);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldDelete(FieldInterface $field) {
+ // Mark all data associated with the field for deletion.
+ $field['deleted'] = FALSE;
+ $table = static::_fieldTableName($field);
+ $revision_table = static::_fieldRevisionTableName($field);
+ $this->database->update($table)
+ ->fields(array('deleted' => 1))
+ ->execute();
+
+ // Move the table to a unique name while the table contents are being
+ // deleted.
+ $field['deleted'] = TRUE;
+ $new_table = static::_fieldTableName($field);
+ $revision_new_table = static::_fieldRevisionTableName($field);
+ $this->database->schema()->renameTable($table, $new_table);
+ $this->database->schema()->renameTable($revision_table, $revision_new_table);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onInstanceDelete(FieldInstanceInterface $instance) {
+ $field = $instance->getField();
+ $table_name = static::_fieldTableName($field);
+ $revision_name = static::_fieldRevisionTableName($field);
+ $this->database->update($table_name)
+ ->fields(array('deleted' => 1))
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+ $this->database->update($revision_name)
+ ->fields(array('deleted' => 1))
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onBundleRename($bundle, $bundle_new) {
+ // We need to account for deleted or inactive fields and instances.
+ $instances = field_read_instances(array('entity_type' => $this->entityType, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+ foreach ($instances as $instance) {
+ $field = $instance->getField();
+ if ($field['storage']['type'] == 'field_sql_storage') {
+ $table_name = static::_fieldTableName($field);
+ $revision_name = static::_fieldRevisionTableName($field);
+ $this->database->update($table_name)
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('bundle', $bundle)
+ ->execute();
+ $this->database->update($revision_name)
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('bundle', $bundle)
+ ->execute();
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
+ $field = $instance->getField();
+ $table_name = static::_fieldTableName($field);
+ $query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC))
+ ->condition('entity_id', $entity->id())
+ ->orderBy('delta');
+ foreach ($field->getColumns() as $column_name => $data) {
+ $query->addField('t', static::_fieldColumnName($field, $column_name), $column_name);
+ }
+ return $query->execute()->fetchAll();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance) {
+ $field = $instance->getField();
+ $table_name = static::_fieldTableName($field);
+ $revision_name = static::_fieldRevisionTableName($field);
+ $this->database->delete($table_name)
+ ->condition('entity_id', $entity->id())
+ ->execute();
+ $this->database->delete($revision_name)
+ ->condition('entity_id', $entity->id())
+ ->execute();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldPurge(FieldInterface $field) {
+ $table_name = static::_fieldTableName($field);
+ $revision_name = static::_fieldRevisionTableName($field);
+ $this->database->schema()->dropTable($table_name);
+ $this->database->schema()->dropTable($revision_name);
+ }
+
+ /**
+ * Gets the SQL table schema.
+ *
+ * @private Calling this function circumvents the entity system and is
+ * strongly discouraged. This function is not considered part of the public
+ * API and modules relying on it might break even in minor releases.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field object
+ * @param array $schema
+ * The field schema array. Mandatory for upgrades, omit otherwise.
+ *
+ * @return array
+ * The same as a hook_schema() implementation for the data and the
+ * revision tables.
+ *
+ * @see hook_schema()
+ */
+ public static function _fieldSqlSchema(FieldInterface $field, array $schema = NULL) {
+ if ($field['deleted']) {
+ $description_current = "Data storage for deleted field {$field['id']} ({$field['entity_type']}, {$field['field_name']}).";
+ $description_revision = "Revision archive storage for deleted field {$field['id']} ({$field['entity_type']}, {$field['field_name']}).";
+ }
+ else {
+ $description_current = "Data storage for {$field['entity_type']} field {$field['field_name']}.";
+ $description_revision = "Revision archive storage for {$field['entity_type']} field {$field['field_name']}.";
+ }
+
+ $current = array(
+ 'description' => $description_current,
+ 'fields' => array(
+ 'bundle' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
+ ),
+ 'deleted' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'A boolean indicating whether this data item has been deleted'
+ ),
+ 'entity_id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The entity id this data is attached to',
+ ),
+ 'revision_id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => FALSE,
+ 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
+ ),
+ 'langcode' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The language code for this data item.',
+ ),
+ 'delta' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The sequence number for this data item, used for multi-value fields',
+ ),
+ ),
+ 'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'),
+ 'indexes' => array(
+ 'bundle' => array('bundle'),
+ 'deleted' => array('deleted'),
+ 'entity_id' => array('entity_id'),
+ 'revision_id' => array('revision_id'),
+ 'langcode' => array('langcode'),
+ ),
+ );
+
+ if (!$schema) {
+ $schema = $field->getSchema();
+ }
+
+ // Add field columns.
+ foreach ($schema['columns'] as $column_name => $attributes) {
+ $real_name = static::_fieldColumnName($field, $column_name);
+ $current['fields'][$real_name] = $attributes;
+ }
+
+ // Add indexes.
+ foreach ($schema['indexes'] as $index_name => $columns) {
+ $real_name = static::_fieldIndexName($field, $index_name);
+ foreach ($columns as $column_name) {
+ // Indexes can be specified as either a column name or an array with
+ // column name and length. Allow for either case.
+ if (is_array($column_name)) {
+ $current['indexes'][$real_name][] = array(
+ static::_fieldColumnName($field, $column_name[0]),
+ $column_name[1],
+ );
+ }
+ else {
+ $current['indexes'][$real_name][] = static::_fieldColumnName($field, $column_name);
+ }
+ }
+ }
+
+ // Add foreign keys.
+ foreach ($schema['foreign keys'] as $specifier => $specification) {
+ $real_name = static::_fieldIndexName($field, $specifier);
+ $current['foreign keys'][$real_name]['table'] = $specification['table'];
+ foreach ($specification['columns'] as $column_name => $referenced) {
+ $sql_storage_column = static::_fieldColumnName($field, $column_name);
+ $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
+ }
+ }
+
+ // Construct the revision table.
+ $revision = $current;
+ $revision['description'] = $description_revision;
+ $revision['primary key'] = array('entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
+ $revision['fields']['revision_id']['not null'] = TRUE;
+ $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
+
+ return array(
+ static::_fieldTableName($field) => $current,
+ static::_fieldRevisionTableName($field) => $revision,
+ );
+ }
+
+ /**
+ * Generates a table name for a field data table.
+ *
+ * @private Calling this function circumvents the entity system and is
+ * strongly discouraged. This function is not considered part of the public
+ * API and modules relying on it might break even in minor releases. Only
+ * call this function to write a query that \Drupal::entityQuery() does not
+ * support. Always call entity_load() before using the data found in the
+ * table.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field object.
+ *
+ * @return string
+ * A string containing the generated name for the database table.
+ *
+ */
+ static public function _fieldTableName(FieldInterface $field) {
+ if ($field['deleted']) {
+ // When a field is a deleted, the table is renamed to
+ // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
+ // table names longer than 64 characters, we hash the uuid and return the
+ // first 10 characters so we end up with a short unique ID.
+ return "field_deleted_data_" . substr(hash('sha256', $field['uuid']), 0, 10);
+ }
+ else {
+ return static::_truncateFieldTableName("{$field->entity_type}__{$field->name}", $field->uuid);
+ }
+ }
+
+ /**
+ * Generates a table name for a field revision archive table.
+ *
+ * @private Calling this function circumvents the entity system and is
+ * strongly discouraged. This function is not considered part of the public
+ * API and modules relying on it might break even in minor releases. Only
+ * call this function to write a query that Drupal::entityQuery() does not
+ * support. Always call entity_load() before using the data found in the
+ * table.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field object.
+ *
+ * @return string
+ * A string containing the generated name for the database table.
+ */
+ static public function _fieldRevisionTableName(FieldInterface $field) {
+ if ($field['deleted']) {
+ // When a field is a deleted, the table is renamed to
+ // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
+ // table names longer than 64 characters, we hash the uuid and return the
+ // first 10 characters so we end up with a short unique ID.
+ return "field_deleted_revision_" . substr(hash('sha256', $field['uuid']), 0, 10);
+ }
+ else {
+ return static::_truncateFieldTableName("{$field->entity_type}_revision__{$field->name}", $field->uuid);
+ }
+ }
+
+ /**
+ * Generates a safe and unanbiguous field table name for a condidate name.
+ *
+ * The method accounts for a maximum table name length of 64 characters, and
+ * adds a hash of the field UUID for disambiguation.
+ *
+ * @param string $table_name
+ * The candidate table name.
+ * @param string $field_uuid
+ * The UUID of the field.
+ *
+ * @return string
+ * The final table name.
+ */
+ static protected function _truncateFieldTableName($table_name, $field_uuid) {
+ // Limit the string to 48 characters, keeping a 16 characters margin for db
+ // prefixes.
+ if (strlen($table_name) > 48) {
+ $table_name = substr($table_name, 0, 38) . substr(hash('sha256', $field_uuid), 0, 10);
+ }
+ return $table_name;
+ }
+
+ /**
+ * Generates an index name for a field data table.
+ *
+ * @private Calling this function circumvents the entity system and is
+ * strongly discouraged. This function is not considered part of the public
+ * API and modules relying on it might break even in minor releases.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field structure
+ * @param string $index
+ * The name of the index.
+ *
+ * @return string
+ * A string containing a generated index name for a field data table that is
+ * unique among all other fields.
+ */
+ static public function _fieldIndexName(FieldInterface $field, $index) {
+ return $field->getFieldName() . '_' . $index;
+ }
+
+ /**
+ * Generates a column name for a field data table.
+ *
+ * @private Calling this function circumvents the entity system and is
+ * strongly discouraged. This function is not considered part of the public
+ * API and modules relying on it might break even in minor releases. Only
+ * call this function to write a query that \Drupal::entityQuery() does not
+ * support. Always call entity_load() before using the data found in the
+ * table.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field object.
+ * @param string $column
+ * The name of the column.
+ *
+ * @return string
+ * A string containing a generated column name for a field data table that is
+ * unique among all other fields.
+ */
+ static public function _fieldColumnName(FieldInterface $field, $column) {
+ return in_array($column, Field::getReservedColumns()) ? $column : $field->getFieldName() . '_' . $column;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index ad361d7..42ea348 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -8,6 +8,7 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Language\Language;
+use Drupal\field\FieldInfo;
use PDO;
use Drupal\Core\Entity\Query\QueryInterface;
@@ -53,8 +54,8 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
/**
* Overrides DatabaseStorageController::__construct().
*/
- public function __construct($entity_type, array $entity_info, Connection $database) {
- parent::__construct($entity_type,$entity_info, $database);
+ public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
+ parent::__construct($entity_type,$entity_info, $database, $field_info);
$this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
$this->entityClass = $this->entityInfo['class'];
@@ -367,6 +368,7 @@ public function save(EntityInterface $entity) {
$this->resetCache(array($entity->id()));
$entity->postSave($this, TRUE);
$this->invokeFieldMethod('update', $entity);
+ $this->saveFieldItems($entity, TRUE);
$this->invokeHook('update', $entity);
if ($this->dataTable) {
$this->invokeTranslationHooks($entity);
@@ -389,6 +391,7 @@ public function save(EntityInterface $entity) {
$entity->enforceIsNew(FALSE);
$entity->postSave($this, FALSE);
$this->invokeFieldMethod('insert', $entity);
+ $this->saveFieldItems($entity, FALSE);
$this->invokeHook('insert', $entity);
}
@@ -603,6 +606,7 @@ public function delete(array $entities) {
$entity_class::postDelete($this, $entities);
foreach ($entities as $entity) {
$this->invokeFieldMethod('delete', $entity);
+ $this->deleteFieldItems($entity);
$this->invokeHook('delete', $entity);
}
// Ignore slave server temporarily.
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 3fc660d..372cbbd 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -333,8 +333,7 @@ public function getTranslationLanguages($include_default = TRUE) {
// Go through translatable properties and determine all languages for
// which translated values are available.
foreach (field_info_instances($this->entityType, $this->bundle()) as $field_name => $instance) {
- $field = field_info_field($field_name);
- if (field_is_translatable($this->entityType, $field) && isset($this->$field_name)) {
+ if (field_is_translatable($this->entityType, $instance->getField()) && isset($this->$field_name)) {
foreach (array_filter($this->$field_name) as $langcode => $value) {
$languages[$langcode] = TRUE;
}
diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php
index bc84d16..07dd8f3 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormController.php
@@ -269,6 +269,7 @@ protected function actions(array $form, array &$form_state) {
*/
public function validate(array $form, array &$form_state) {
$entity = $this->buildEntity($form, $form_state);
+ $entity_type = $entity->entityType();
$entity_langcode = $entity->language()->id;
$violations = array();
@@ -285,9 +286,9 @@ public function validate(array $form, array &$form_state) {
else {
// For BC entities, iterate through each field instance and
// instantiate NG items objects manually.
- $definitions = \Drupal::entityManager()->getFieldDefinitions($entity->entityType(), $entity->bundle());
- foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $field_name => $instance) {
- $langcode = field_is_translatable($entity->entityType(), $instance->getField()) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
+ $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity->bundle());
+ foreach (field_info_instances($entity_type, $entity->bundle()) as $field_name => $instance) {
+ $langcode = field_is_translatable($entity_type, $instance->getField()) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
// Create the field object.
$items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
@@ -304,7 +305,7 @@ public function validate(array $form, array &$form_state) {
// Map errors back to form elements.
if ($violations) {
foreach ($violations as $field_name => $field_violations) {
- $langcode = field_is_translatable($entity->entityType(), field_info_field($field_name)) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
+ $langcode = field_is_translatable($entity_type , field_info_field($entity_type, $field_name)) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
$field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
$field_state['constraint_violations'] = $field_violations;
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
@@ -433,9 +434,8 @@ protected function submitEntityLanguage(array $form, array &$form_state) {
$current_langcode = $this->isDefaultFormLangcode($form_state) ? $form_state['values']['langcode'] : $this->getFormLangcode($form_state);
foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
- $field_name = $instance['field_name'];
- $field = field_info_field($field_name);
-
+ $field = $instance->getField();
+ $field_name = $field->name;
if (isset($form[$field_name]['#language'])) {
$previous_langcode = $form[$field_name]['#language'];
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index eeb1cac..e2c3087 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -89,6 +89,15 @@ class EntityManager extends PluginManagerBase {
protected $fieldDefinitions;
/**
+ * The root paths.
+ *
+ * @see \Drupal\Core\Entity\EntityManager::__construct().
+ *
+ * @var \Traversable
+ */
+ protected $namespaces;
+
+ /**
* The string translationManager.
*
* @var \Drupal\Core\StringTranslation\TranslationInterface
@@ -114,22 +123,46 @@ class EntityManager extends PluginManagerBase {
*/
public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager, TranslationInterface $translation_manager) {
// Allow the plugin definition to be altered by hook_entity_info_alter().
- $annotation_namespaces = array(
- 'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
- );
$this->moduleHandler = $module_handler;
$this->cache = $cache;
$this->languageManager = $language_manager;
+ $this->namespaces = $namespaces;
$this->translationManager = $translation_manager;
+ $this->doDiscovery($namespaces);
+ $this->factory = new DefaultFactory($this->discovery);
+ $this->container = $container;
+ }
+
+ protected function doDiscovery($namespaces) {
+ $annotation_namespaces = array(
+ 'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
+ );
$this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
$this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
$this->discovery = new AlterDecorator($this->discovery, 'entity_info');
$this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+ }
- $this->factory = new DefaultFactory($this->discovery);
- $this->container = $container;
+ /**
+ * Add more namespaces to the entity manager.
+ *
+ * This is usually only necessary for uninstall purposes.
+ *
+ * @todo Remove this method, along with doDiscovery(), when
+ * https://drupal.org/node/1199946 is fixed.
+ *
+ * @param \Traversable $namespaces
+ *
+ * @see comment_uninstall()
+ */
+ public function addNamespaces(\Traversable $namespaces) {
+ reset($this->namespaces);
+ $iterator = new \AppendIterator;
+ $iterator->append(new \IteratorIterator($this->namespaces));
+ $iterator->append($namespaces);
+ $this->doDiscovery($iterator);
}
/**
@@ -164,6 +197,9 @@ public function hasController($entity_type, $controller_type) {
*/
public function getControllerClass($entity_type, $controller_type, $nested = NULL) {
$definition = $this->getDefinition($entity_type);
+ if (!$definition) {
+ throw new \InvalidArgumentException(sprintf('The %s entity type does not exist.', $entity_type));
+ }
$definition = $definition['controllers'];
if (!$definition) {
throw new \InvalidArgumentException(sprintf('The entity type (%s) does not exist.', $entity_type));
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index 988afa7..22eb472 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -234,6 +234,22 @@ public function invokeFieldItemPrepareCache(EntityInterface $entity) {
}
/**
+ * Invokes a hook on behalf of the entity.
+ *
+ * @param string $hook
+ * One of 'presave', 'insert', 'update', 'predelete', 'delete', or
+ * 'revision_delete'.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity object.
+ */
+ protected function invokeHook($hook, EntityInterface $entity) {
+ // Invoke the hook.
+ module_invoke_all($this->entityType . '_' . $hook, $entity);
+ // Invoke the respective entity-level hook.
+ module_invoke_all('entity_' . $hook, $entity, $this->entityType);
+ }
+
+ /**
* Checks translation statuses and invoke the related hooks if needed.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
new file mode 100644
index 0000000..970eeac
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -0,0 +1,295 @@
+entityType);
+ $use_cache = $load_current && $info['field_cache'];
+
+ // Ensure we are working with a BC mode entity.
+ foreach ($entities as $id => $entity) {
+ $entities[$id] = $entity->getBCEntity();
+ }
+
+ // Assume all entities will need to be queried. Entities found in the cache
+ // will be removed from the list.
+ $queried_entities = $entities;
+
+ // Fetch available entities from cache, if applicable.
+ if ($use_cache) {
+ // Build the list of cache entries to retrieve.
+ $cids = array();
+ foreach ($entities as $id => $entity) {
+ $cids[] = "field:{$this->entityType}:$id";
+ }
+ $cache = cache('field')->getMultiple($cids);
+ // Put the cached field values back into the entities and remove them from
+ // the list of entities to query.
+ foreach ($entities as $id => $entity) {
+ $cid = "field:{$this->entityType}:$id";
+ if (isset($cache[$cid])) {
+ unset($queried_entities[$id]);
+ foreach ($cache[$cid]->data as $field_name => $values) {
+ $entity->$field_name = $values;
+ }
+ }
+ }
+ }
+
+ // Fetch other entities from their storage location.
+ if ($queried_entities) {
+ // Let the storage controller actually load the values.
+ $this->doLoadFieldItems($queried_entities, $age);
+
+ // Invoke the field type's prepareCache() method.
+ foreach ($queried_entities as $entity) {
+ $this->invokeFieldItemPrepareCache($entity);
+ }
+
+ // Build cache data.
+ if ($use_cache) {
+ foreach ($queried_entities as $id => $entity) {
+ $data = array();
+ $instances = field_info_instances($this->entityType, $entity->bundle());
+ foreach ($instances as $instance) {
+ $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
+ }
+ $cid = "field:{$this->entityType}:$id";
+ cache('field')->set($cid, $data);
+ }
+ }
+ }
+ }
+
+ /**
+ * Saves values of configurable fields for an entity.
+ *
+ * This method is a wrapper that handles the field data cache. Subclasses
+ * need to implement the doSaveFieldItems() method with the actual storage
+ * logic.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity.
+ * @param bool $update
+ * TRUE if the entity is being updated, FALSE if it is being inserted.
+ */
+ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
+ // Ensure we are working with a BC mode entity.
+ $entity = $entity->getBCEntity();
+
+ $this->doSaveFieldItems($entity, $update);
+
+ if ($update) {
+ $entity_info = $entity->entityInfo();
+ if ($entity_info['field_cache']) {
+ cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
+ }
+ }
+ }
+
+ /**
+ * Deletes values of configurable fields for all revisions of an entity.
+ *
+ * This method is a wrapper that handles the field data cache. Subclasses
+ * need to implement the doDeleteFieldItems() method with the actual storage
+ * logic.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity.
+ */
+ protected function deleteFieldItems(EntityInterface $entity) {
+ // Ensure we are working with a BC mode entity.
+ $entity = $entity->getBCEntity();
+
+ $this->doDeleteFieldItems($entity);
+
+ $entity_info = $entity->entityInfo();
+ if ($entity_info['field_cache']) {
+ cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
+ }
+ }
+
+ /**
+ * Deletes values of configurable fields for a single revision of an entity.
+ *
+ * This method is a wrapper that handles the field data cache. Subclasses
+ * need to implement the doDeleteFieldItemsRevision() method with the actual
+ * storage logic.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity. It must have a revision ID attribute.
+ */
+ protected function deleteFieldItemsRevision(EntityInterface $entity) {
+ $this->doDeleteFieldItemsRevision($entity->getBCEntity());
+ }
+
+ /**
+ * Loads values of configurable fields for a group of entities.
+ *
+ * This is the method that holds the actual storage logic.
+ *
+ * @param array $entities
+ * An array of entities keyed by entity ID.
+ * @param int $age
+ * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
+ * FIELD_LOAD_REVISION to load the version indicated by each entity.
+ */
+ abstract protected function doLoadFieldItems($entities, $age);
+
+ /**
+ * Saves values of configurable fields for an entity.
+ *
+ * This is the method that holds the actual storage logic.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity.
+ * @param bool $update
+ * TRUE if the entity is being updated, FALSE if it is being inserted.
+ */
+ abstract protected function doSaveFieldItems(EntityInterface $entity, $update);
+
+ /**
+ * Deletes values of configurable fields for all revisions of an entity.
+ *
+ * This is the method that holds the actual storage logic.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity.
+ */
+ abstract protected function doDeleteFieldItems(EntityInterface $entity);
+
+ /**
+ * Deletes values of configurable fields for a single revision of an entity.
+ *
+ * This is the method that holds the actual storage logic.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity.
+ */
+ abstract protected function doDeleteFieldItemsRevision(EntityInterface $entity);
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldCreate(FieldInterface $field) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldUpdate(FieldInterface $field) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldDelete(FieldInterface $field) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onInstanceCreate(FieldInstanceInterface $instance) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onInstanceUpdate(FieldInstanceInterface $instance) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onInstanceDelete(FieldInstanceInterface $instance) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onBundleCreate($bundle) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onBundleRename($bundle, $bundle_new) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onBundleDelete($bundle) { }
+
+ /**
+ * {@inheritdoc}
+ */
+ 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->delete();
+ }
+ $this->purgeFieldItems($entity, $instance);
+ }
+
+ /**
+ * Reads values to be purged for a single field of a single entity.
+ *
+ * This method is called during field data purge, on fields for which
+ * onFieldDelete() or onFieldInstanceDelete() has previously run.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity.
+ * @param \Drupal\field\FieldInstanceInterface $instance
+ * The field instance.
+ *
+ * @return array
+ * The field values, in their canonical array format (numerically indexed
+ * array of items, each item being a property/value array).
+ */
+ abstract protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance);
+
+ /**
+ * Removes field data from storage during purge.
+ *
+ * @param EntityInterface $entity
+ * The entity whose values are being purged.
+ * @param FieldInstanceInterface $instance
+ * The field whose values are bing purged.
+ */
+ abstract protected function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance);
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onFieldPurge(FieldInterface $field) { }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php
new file mode 100644
index 0000000..a3dc37a
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php
@@ -0,0 +1,120 @@
+getFieldById((substr($specifier, 3)));
}
- elseif (isset($field_map[$specifier])) {
- $field = $field_info->getField($specifier);
+ elseif (isset($field_map[$entity_type][$specifier])) {
+ $field = $field_info->getField($entity_type, $specifier);
}
else {
$field = FALSE;
@@ -136,7 +137,7 @@ public function addField($field, $type, $langcode) {
$field_name = $field->getFieldName();
// If there are bundles, pick one.
if (!empty($entity_info['entity_keys']['bundle'])) {
- $values[$entity_info['entity_keys']['bundle']] = reset($field_map[$field_name]['bundles'][$entity_type]);
+ $values[$entity_info['entity_keys']['bundle']] = reset($field_map[$entity_type][$field_name]['bundles']);
}
$entity = $entity_manager
->getStorageController($entity_type)
@@ -161,7 +162,7 @@ public function addField($field, $type, $langcode) {
$column = 'value';
}
$table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
- $sql_column = _field_sql_storage_columnname($field['field_name'], $column);
+ $sql_column = DatabaseStorageController::_fieldColumnName($field, $column);
}
// This is an entity property (non-configurable field).
else {
@@ -250,12 +251,12 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode,
protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) {
$field_name = $field['field_name'];
if (!isset($this->fieldTables[$index_prefix . $field_name])) {
- $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
+ $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? DatabaseStorageController::_fieldTableName($field) : DatabaseStorageController::_fieldRevisionTableName($field);
if ($field['cardinality'] != 1) {
$this->sqlQuery->addMetaData('simple_query', FALSE);
}
$entity_type = $this->sqlQuery->getMetaData('entity_type');
- $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field AND %alias.entity_type = '$entity_type'", $langcode);
+ $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field", $langcode);
}
return $this->fieldTables[$index_prefix . $field_name];
}
diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index 3c1755f..4363ef8 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -35,6 +35,10 @@ function block_update_dependencies() {
$dependencies['block'][8005] = array(
'user' => 8016,
);
+ // Migrate custom blocks after field storage has been reorganized.
+ $dependencies['block'][8008] = array(
+ 'field' => 8006,
+ );
return $dependencies;
}
@@ -247,9 +251,9 @@ function block_update_8008() {
// First, create the body field.
$body_field = array(
- 'id' => 'block_body',
+ 'name' => 'block_body',
+ 'entity_type' => 'custom_block',
'type' => 'text_with_summary',
- 'entity_types' => array('custom_block'),
'module' => 'text',
'cardinality' => 1,
'schema' => array(
@@ -283,8 +287,8 @@ function block_update_8008() {
);
_update_8003_field_create_field($body_field);
+
$instance = array(
- 'id' => 'custom_block.basic.block_body',
'entity_type' => 'custom_block',
'bundle' => 'basic',
'label' => 'Block body',
@@ -338,7 +342,7 @@ function block_update_8008() {
);
// This is a core update and no contrib modules are enabled yet, so
// we can assume default field storage for a faster update.
- _update_8000_field_sql_storage_write('custom_block', 'basic', $block->bid, $block->bid, 'block_body', $data);
+ _update_8006_field_write_data_sql('custom_block', 'basic', $block->bid, $block->bid, 'block_body', $data);
$sandbox['last'] = $block->bid;
$sandbox['count'] += 1;
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index b8da16f..d6262b8 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -207,13 +207,13 @@ function custom_block_entity_bundle_info() {
*/
function custom_block_add_body_field($block_type_id, $label = 'Block body') {
// Add or remove the body field, as needed.
- $field = field_info_field('block_body');
+ $field = field_info_field('custom_block', 'block_body');
$instance = field_info_instance('custom_block', 'block_body', $block_type_id);
if (empty($field)) {
$field = entity_create('field_entity', array(
- 'field_name' => 'block_body',
+ 'name' => 'block_body',
+ 'entity_type' => 'custom_block',
'type' => 'text_with_summary',
- 'entity_types' => array('custom_block'),
));
$field->save();
}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
index 0698234..5fb5330 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
@@ -16,7 +16,7 @@
* This extends the Drupal\Core\Entity\DatabaseStorageControllerNG class,
* adding required special handling for custom block entities.
*/
-class CustomBlockStorageController extends DatabaseStorageControllerNG implements EntityStorageControllerInterface {
+class CustomBlockStorageController extends DatabaseStorageControllerNG {
/**
* Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
index c59b435..53f8f5a 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
@@ -65,13 +65,14 @@ public function testBlockFields() {
// Create a field with settings to validate.
$this->field = entity_create('field_entity', array(
- 'field_name' => drupal_strtolower($this->randomName()),
+ 'name' => drupal_strtolower($this->randomName()),
+ 'entity_type' => 'custom_block',
'type' => 'link',
'cardinality' => 2,
));
$this->field->save();
$this->instance = entity_create('field_instance', array(
- 'field_name' => $this->field->id(),
+ 'field_name' => $this->field->name,
'entity_type' => 'custom_block',
'bundle' => 'link',
'settings' => array(
diff --git a/core/modules/comment/comment.api.php b/core/modules/comment/comment.api.php
index a613825..fb38e40 100644
--- a/core/modules/comment/comment.api.php
+++ b/core/modules/comment/comment.api.php
@@ -160,9 +160,8 @@ function hook_comment_unpublish(Drupal\comment\Comment $comment) {
/**
* Act before comment deletion.
*
- * This hook is invoked from entity_delete_multiple() before
- * field_attach_delete() is called and before the comment is actually removed
- * from the database.
+ * This hook is invoked from entity_delete_multiple() before field values are
+ * deleted and before the comment is actually removed from the database.
*
* @param Drupal\comment\Comment $comment
* The comment object for the comment that is about to be deleted.
@@ -180,9 +179,8 @@ function hook_comment_predelete(Drupal\comment\Comment $comment) {
/**
* Respond to comment deletion.
*
- * This hook is invoked from entity_delete_multiple() after
- * field_attach_delete() has called and after the comment has been removed from
- * the database.
+ * This hook is invoked from entity_delete_multiple() after feild values are
+ * deleted and after the comment has been removed from the database.
*
* @param Drupal\comment\Comment $comment
* The comment object for the comment that has been deleted.
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index efaf9f3..04e1291 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -12,6 +12,10 @@ function comment_uninstall() {
// Remove variables.
variable_del('comment_block_count');
$node_types = array_keys(node_type_get_types());
+ Drupal::entityManager()->addNamespaces(new ArrayIterator(array(
+ 'Drupal\comment' => DRUPAL_ROOT . '/core/modules/comment/lib',
+ )));
+ drupal_classloader_register('comment', 'core/modules/comment');
foreach ($node_types as $node_type) {
entity_invoke_bundle_hook('delete', 'comment', 'comment_node_' . $node_type);
variable_del('comment_' . $node_type);
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index ef12cc1..d7f5db4 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -322,11 +322,11 @@ function comment_node_type_delete($info) {
*/
function _comment_body_field_create($info) {
// Create the field if needed.
- if (!field_read_field('comment_body', array('include_inactive' => TRUE))) {
+ if (!field_read_field('comment', 'comment_body', array('include_inactive' => TRUE))) {
$field = entity_create('field_entity', array(
- 'field_name' => 'comment_body',
+ 'name' => 'comment_body',
'type' => 'text_long',
- 'entity_types' => array('comment'),
+ 'entity_type' => 'comment',
));
$field->save();
}
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
index 8928f4f..f9d25f1 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
@@ -45,7 +45,7 @@ function testCommentDefaultFields() {
}
// Check that the 'comment_body' field is deleted.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertTrue(empty($field), 'The comment_body field was deleted');
// Create a new content type.
@@ -54,7 +54,7 @@ function testCommentDefaultFields() {
// Check that the 'comment_body' field exists and has an instance on the
// new comment bundle.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertTrue($field, 'The comment_body field exists');
$instances = field_info_instances('comment');
$this->assertTrue(isset($instances['comment_node_' . $type_name]['comment_body']), format_string('The comment_body field is present for comments on type @type', array('@type' => $type_name)));
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index 84a080e..4064b22 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -71,7 +71,7 @@ function setUp() {
$this->drupalPost("user/" . $admin_user->id() . "/edit", $edit, t('Save'));
// Make comment body translatable.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$field['translatable'] = TRUE;
$field->save();
$this->assertTrue(field_is_translatable('comment', $field), 'Comment body is translatable.');
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
index 5c25660..96e2d20 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
@@ -63,7 +63,7 @@ protected function getTranslatorPermissions() {
*/
function setupTestFields() {
parent::setupTestFields();
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$field['translatable'] = TRUE;
$field->save();
}
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php
index 48e749f..127a611 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php
@@ -42,7 +42,7 @@ protected function setUp() {
*/
function testCommentUninstallWithField() {
// Ensure that the field exists before uninstallation.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertNotNull($field, 'The comment_body field exists.');
// Uninstall the comment module which should trigger field deletion.
@@ -50,7 +50,7 @@ function testCommentUninstallWithField() {
$this->container->get('module_handler')->uninstall(array('comment'));
// Check that the field is now deleted.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertNull($field, 'The comment_body field has been deleted.');
}
@@ -60,12 +60,12 @@ function testCommentUninstallWithField() {
*/
function testCommentUninstallWithoutField() {
// Manually delete the comment_body field before module uninstallation.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertNotNull($field, 'The comment_body field exists.');
$field->delete();
// Check that the field is now deleted.
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertNull($field, 'The comment_body field has been deleted.');
// Ensure that uninstallation succeeds even if the field has already been
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
index 224340c..989c074 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
@@ -21,7 +21,7 @@ class ConfigLocaleOverride extends DrupalUnitTestBase {
*
* @var array
*/
- public static $modules = array('locale', 'config_test', 'user', 'language', 'system');
+ public static $modules = array('locale', 'config_test', 'user', 'language', 'system', 'field');
public static function getInfo() {
return array(
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php b/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
index 49c73d7..7a1120d 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
@@ -21,7 +21,7 @@ class MessageEntityTest extends DrupalUnitTestBase {
*
* @var array
*/
- public static $modules = array('system', 'contact');
+ public static $modules = array('system', 'contact', 'field');
public static function getInfo() {
return array(
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
index 1e835e2..650a216 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
@@ -7,6 +7,7 @@
namespace Drupal\contact\Tests\Views;
+use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\views\Tests\ViewTestBase;
/**
@@ -40,13 +41,14 @@ protected function setUp() {
parent::setUp();
$this->field = entity_create('field_entity', array(
- 'field_name' => strtolower($this->randomName()),
+ 'name' => strtolower($this->randomName()),
+ 'entity_type' => 'contact_message',
'type' => 'text'
));
$this->field->save();
entity_create('field_instance', array(
- 'field_name' => $this->field->id(),
+ 'field_name' => $this->field->name,
'entity_type' => 'contact_message',
'bundle' => 'contact_message',
))->save();
@@ -58,16 +60,11 @@ protected function setUp() {
* Tests the views data generation.
*/
public function testViewsData() {
- $table_name = _field_sql_storage_tablename($this->field);
+ // Test that the field is not exposed to views, since contact_message
+ // entities have no storage.
+ $table_name = DatabaseStorageController::_fieldTableName($this->field);
$data = $this->container->get('views.views_data')->get($table_name);
-
- // Test that the expected data array is returned.
- $expected = array('', '_value', '_format');
- $this->assertEqual(count($data), count($expected), 'The expected amount of array keys were found.');
- foreach ($expected as $suffix) {
- $this->assertTrue(isset($data[$this->field->id() . $suffix]));
- }
- $this->assertTrue(empty($data['table']['join']), 'The field is not joined to the non existent contact message base table.');
+ $this->assertFalse($data, 'The field is not exposed to Views.');
}
}
diff --git a/core/modules/content_translation/content_translation.admin.inc b/core/modules/content_translation/content_translation.admin.inc
index 97cbfa9..819a63b 100644
--- a/core/modules/content_translation/content_translation.admin.inc
+++ b/core/modules/content_translation/content_translation.admin.inc
@@ -95,7 +95,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
// @todo Exploit field definitions once all core entities and field
// types are migrated to the Entity Field API.
foreach ($fields as $field_name => $instance) {
- $field = field_info_field($field_name);
+ $field = $instance->getField();
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
'#label' => $instance['label'],
'#type' => 'checkbox',
@@ -326,7 +326,7 @@ function _content_translation_update_field_translatability($settings) {
foreach ($bundle_settings['fields'] as $field_name => $translatable) {
// If a field is enabled for translation for at least one instance we
// need to mark it as translatable.
- $fields[$field_name] = $translatable || !empty($fields[$field_name]);
+ $fields[$entity_type][$field_name] = $translatable || !empty($fields[$entity_type][$field_name]);
}
}
// @todo Store non-configurable field settings to be able to alter their
@@ -335,22 +335,24 @@ function _content_translation_update_field_translatability($settings) {
}
$operations = array();
- foreach ($fields as $field_name => $translatable) {
- $field = field_info_field($field_name);
- if ($field['translatable'] != $translatable) {
- // If a field is untranslatable, it can have no data except under
- // Language::LANGCODE_NOT_SPECIFIED. Thus we need a field to be translatable before
- // we convert data to the entity language. Conversely we need to switch
- // data back to Language::LANGCODE_NOT_SPECIFIED before making a field
- // untranslatable lest we lose information.
- $field_operations = array(
- array('content_translation_translatable_switch', array($translatable, $field_name)),
- );
- if ($field->hasData()) {
- $field_operations[] = array('content_translation_translatable_batch', array($translatable, $field_name));
- $field_operations = $translatable ? $field_operations : array_reverse($field_operations);
+ foreach ($fields as $entity_type => $entity_type_fields) {
+ foreach ($entity_type_fields as $field_name => $translatable) {
+ $field = field_info_field($entity_type, $field_name);
+ if ($field['translatable'] != $translatable) {
+ // If a field is untranslatable, it can have no data except under
+ // Language::LANGCODE_NOT_SPECIFIED. Thus we need a field to be translatable before
+ // we convert data to the entity language. Conversely we need to switch
+ // data back to Language::LANGCODE_NOT_SPECIFIED before making a field
+ // untranslatable lest we lose information.
+ $field_operations = array(
+ array('content_translation_translatable_switch', array($translatable, $entity_type, $field_name)),
+ );
+ if ($field->hasData()) {
+ $field_operations[] = array('content_translation_translatable_batch', array($translatable, $field_name));
+ $field_operations = $translatable ? $field_operations : array_reverse($field_operations);
+ }
+ $operations = array_merge($operations, $field_operations);
}
- $operations = array_merge($operations, $field_operations);
}
}
@@ -374,11 +376,13 @@ function _content_translation_update_field_translatability($settings) {
* @param bool $translatable
* Indicator of whether the field should be made translatable (TRUE) or
* untranslatble (FALSE).
+ * @param string $entity_type
+ * Field entity type.
* @param string $field_name
* Field machine name.
*/
-function content_translation_translatable_switch($translatable, $field_name) {
- $field = field_info_field($field_name);
+function content_translation_translatable_switch($translatable, $entity_type, $field_name) {
+ $field = field_info_field($entity_type, $field_name);
if ($field['translatable'] !== $translatable) {
$field['translatable'] = $translatable;
$field->save();
@@ -395,10 +399,6 @@ function content_translation_translatable_switch($translatable, $field_name) {
* Field machine name.
*/
function content_translation_translatable_batch($translatable, $field_name, &$context) {
- $field = field_info_field($field_name);
- $column = isset($field['columns']['value']) ? 'value' : key($field['columns']);
- $query_field = "$field_name.$column";
-
// Determine the entity types to act on.
$entity_types = array();
foreach (field_info_instances() as $entity_type => $info) {
@@ -417,6 +417,10 @@ function content_translation_translatable_batch($translatable, $field_name, &$co
$context['sandbox']['max'] = 0;
foreach ($entity_types as $entity_type) {
+ $field = field_info_field($entity_type, $field_name);
+ $column = isset($field['columns']['value']) ? 'value' : key($field['columns']);
+ $query_field = "$field_name.$column";
+
// How many entities will need processing?
$query = Drupal::entityQuery($entity_type);
$count = $query
@@ -444,6 +448,9 @@ function content_translation_translatable_batch($translatable, $field_name, &$co
$info = entity_get_info($entity_type);
$offset = $context['sandbox']['progress_entity_type'][$entity_type];
$query = Drupal::entityQuery($entity_type);
+ $field = field_info_field($entity_type, $field_name);
+ $column = isset($field['columns']['value']) ? 'value' : key($field['columns']);
+ $query_field = "$field_name.$column";
$result = $query
->exists($query_field)
->sort($info['entity_keys']['id'])
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 34e918c..3edb6aa 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -200,7 +200,7 @@ function content_translation_menu() {
}
}
- $items['admin/config/regional/content_translation/translatable/%'] = array(
+ $items['admin/config/regional/content_translation/translatable/%/%'] = array(
'title' => 'Confirm change in translatability.',
'description' => 'Confirm page for changing field translatability.',
'route_name' => 'content_translation_translatable',
@@ -621,7 +621,7 @@ function content_translation_form_alter(array &$form, array &$form_state) {
else {
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
$field_name = $instance['field_name'];
- $field = field_info_field($field_name);
+ $field = $instance->getField();
$form[$field_name]['#multilingual'] = !empty($field['translatable']);
}
}
@@ -808,6 +808,7 @@ function content_translation_field_extra_fields() {
*/
function content_translation_form_field_ui_field_edit_form_alter(array &$form, array &$form_state, $form_id) {
$field = $form['#field'];
+ $entity_type = $field['entity_type'];
$field_name = $field['field_name'];
$translatable = $field['translatable'];
$label = t('Field translation');
@@ -821,7 +822,7 @@ function content_translation_form_field_ui_field_edit_form_alter(array &$form, a
'#type' => 'link',
'#prefix' => t('This field has data in existing content.') . ' ',
'#title' => !$translatable ? t('Enable translation') : t('Disable translation'),
- '#href' => 'admin/config/regional/content_translation/translatable/' . $field_name,
+ '#href' => "admin/config/regional/content_translation/translatable/$entity_type/$field_name",
'#options' => array('query' => drupal_get_destination()),
'#access' => user_access('administer content translation'),
),
@@ -1022,7 +1023,7 @@ function content_translation_save_settings($settings) {
// Store whether fields have translation enabled or not.
if (!empty($bundle_settings['columns'])) {
foreach ($bundle_settings['columns'] as $field_name => $column_settings) {
- $field = field_info_field($field_name);
+ $field = field_info_field($entity_type, $field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
if ($field['translatable']) {
$instance['settings']['translation_sync'] = $column_settings;
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index e277791..966d259 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -40,8 +40,7 @@ function content_translation_overview(EntityInterface $entity) {
// Determine whether the current entity is translatable.
$translatable = FALSE;
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
- $field_name = $instance['field_name'];
- $field = field_info_field($field_name);
+ $field = $instance->getField();
if ($field['translatable']) {
$translatable = TRUE;
break;
@@ -246,7 +245,7 @@ function content_translation_prepare_translation(EntityInterface $entity, Langua
else {
$instances = field_info_instances($entity->entityType(), $entity->bundle());
foreach ($instances as $field_name => $instance) {
- $field = field_info_field($field_name);
+ $field = $instance->getField();
if (!empty($field['translatable'])) {
$value = $entity->get($field_name);
$value[$target->id] = isset($value[$source->id]) ? $value[$source->id] : array();
diff --git a/core/modules/content_translation/content_translation.routing.yml b/core/modules/content_translation/content_translation.routing.yml
index d7d55a1..64f210c 100644
--- a/core/modules/content_translation/content_translation.routing.yml
+++ b/core/modules/content_translation/content_translation.routing.yml
@@ -1,5 +1,5 @@
content_translation_translatable:
- pattern: 'admin/config/regional/content_translation/translatable/{field_name}'
+ pattern: 'admin/config/regional/content_translation/translatable/{entity_type}/{field_name}'
defaults:
_form: 'Drupal\content_translation\Form\TranslatableForm'
requirements:
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
index 3374ddf..503bd3b 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
@@ -51,7 +51,7 @@ public function removeTranslation(EntityInterface $entity, $langcode) {
// Remove field translations.
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
$field_name = $instance['field_name'];
- $field = field_info_field($field_name);
+ $field = $instance->getField();
if ($field['translatable']) {
$entity->{$field_name}[$langcode] = array();
}
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php b/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php
index bdeb983..7d844eb 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php
@@ -8,7 +8,8 @@
namespace Drupal\content_translation\Form;
use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\field\Field;
+use Drupal\field\Entity\Field;
+use Drupal\field\Field as FieldInfo;
/**
* Provides a confirm form for changing translatable status on translation
@@ -19,9 +20,9 @@ class TranslatableForm extends ConfirmFormBase {
/**
* The field info we are changing translatable status on.
*
- * @var array.
+ * @var \Drupal\field\Entity\Field
*/
- protected $fieldInfo;
+ protected $field;
/**
* The field name we are changing translatable
@@ -42,7 +43,7 @@ public function getFormID() {
* {@inheritdoc}
*/
public function getQuestion() {
- if ($field['translatable']) {
+ if ($this->field['translatable']) {
$question = t('Are you sure you want to disable translation for the %name field?', array('%name' => $this->fieldName));
}
else {
@@ -58,7 +59,7 @@ public function getDescription() {
$description = t('By submitting this form these changes will apply to the %name field everywhere it is used.',
array('%name' => $this->fieldName)
);
- $description .= $this->fieldInfo['translatable'] ? "
" . t("All the existing translations of this field will be deleted.
This action cannot be undone.") : '';
+ $description .= $this->field['translatable'] ? "
" . t("All the existing translations of this field will be deleted.
This action cannot be undone.") : '';
return $description;
}
@@ -71,12 +72,14 @@ public function getCancelPath() {
/**
* {@inheritdoc}
+ * @param string $entity_type
+ * The entity type.
* @param string $field_name
* The field name.
*/
- public function buildForm(array $form, array &$form_state, $field_name = NULL) {
+ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $field_name = NULL) {
$this->fieldName = $field_name;
- $this->fieldInfo = Field::fieldInfo($field_name);
+ $this->fieldInfo = FieldInfo::fieldInfo()->getField($entity_type, $field_name);
return parent::buildForm($form, $form_state);
}
@@ -127,6 +130,7 @@ public function submitForm(array &$form, array &$form_state) {
array(
'content_translation_translatable_switch', array(
!$translatable,
+ $this->field['entity_type'],
$this->fieldName,
),
),
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php
index c17bd06..f4e2f13 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php
@@ -89,7 +89,7 @@ function testSettingsUI() {
);
$this->assertSettings('comment', 'comment_node_article', TRUE, $edit);
field_info_cache_clear();
- $field = field_info_field('comment_body');
+ $field = field_info_field('comment', 'comment_body');
$this->assertTrue($field['translatable'], 'Comment body is translatable.');
// Test that language settings are correctly stored.
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php
index 54d234e..c813790 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php
@@ -57,7 +57,8 @@ protected function setupTestFields() {
$this->cardinality = 3;
entity_create('field_entity', array(
- 'field_name' => $this->fieldName,
+ 'name' => $this->fieldName,
+ 'entity_type' => $this->entityType,
'type' => 'image',
'cardinality' => $this->cardinality,
'translatable' => TRUE,
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
index b635c45..0ea5ae6 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
@@ -162,8 +162,9 @@ protected function setupTestFields() {
$this->fieldName = 'field_test_et_ui_test';
entity_create('field_entity', array(
- 'field_name' => $this->fieldName,
+ 'name' => $this->fieldName,
'type' => 'text',
+ 'entity_type' => $this->entityType,
'cardinality' => 1,
'translatable' => TRUE,
))->save();
diff --git a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php
index 16731a2..f755730 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php
@@ -36,8 +36,9 @@ public function setUp() {
// Create a field with settings to validate.
$this->field = entity_create('field_entity', array(
- 'field_name' => 'field_datetime',
+ 'name' => 'field_datetime',
'type' => 'datetime',
+ 'entity_type' => 'entity_test',
'settings' => array('datetime_type' => 'date'),
));
$this->field->save();
diff --git a/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php b/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php
index 867e67d..64ba5cd 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php
@@ -57,13 +57,14 @@ function setUp() {
// Create a field with settings to validate.
$this->field = entity_create('field_entity', array(
- 'field_name' => drupal_strtolower($this->randomName()),
+ 'name' => drupal_strtolower($this->randomName()),
+ 'entity_type' => 'entity_test',
'type' => 'datetime',
'settings' => array('datetime_type' => 'date'),
));
$this->field->save();
$this->instance = entity_create('field_instance', array(
- 'field_name' => $this->field->id(),
+ 'field_name' => $this->field->name,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'settings' => array(
@@ -73,7 +74,7 @@ function setUp() {
$this->instance->save();
entity_get_form_display($this->instance->entity_type, $this->instance->bundle, 'default')
- ->setComponent($this->field->id(), array(
+ ->setComponent($this->field->name, array(
'type' => 'datetime_default',
))
->save();
@@ -84,7 +85,7 @@ function setUp() {
'settings' => array('format_type' => 'medium'),
);
entity_get_display($this->instance->entity_type, $this->instance->bundle, 'full')
- ->setComponent($this->field->id(), $this->display_options)
+ ->setComponent($this->field->name, $this->display_options)
->save();
}
@@ -92,7 +93,7 @@ function setUp() {
* Tests date field functionality.
*/
function testDateField() {
- $field_name = $this->field->id();
+ $field_name = $this->field->name;
// Display creation form.
$this->drupalGet('entity_test/add');
@@ -160,7 +161,7 @@ function testDateField() {
* Tests date and time field.
*/
function testDatetimeField() {
- $field_name = $this->field->id();
+ $field_name = $this->field->name;
// Change the field to a datetime field.
$this->field['settings']['datetime_type'] = 'datetime';
$this->field->save();
@@ -229,7 +230,7 @@ function testDatetimeField() {
* Tests Date List Widget functionality.
*/
function testDatelistWidget() {
- $field_name = $this->field->id();
+ $field_name = $this->field->name;
// Change the field to a datetime field.
$this->field->settings['datetime_type'] = 'datetime';
$this->field->save();
@@ -299,7 +300,7 @@ function testDefaultValue() {
// Change the field to a datetime field.
$this->field->settings['datetime_type'] = 'datetime';
$this->field->save();
- $field_name = $this->field->id();
+ $field_name = $this->field->name;
// Set the default value to 'now'.
$this->instance->settings['default_value'] = 'now';
@@ -341,7 +342,7 @@ function testInvalidField() {
// Change the field to a datetime field.
$this->field->settings['datetime_type'] = 'datetime';
$this->field->save();
- $field_name = $this->field->id();
+ $field_name = $this->field->name;
// Display creation form.
$this->drupalGet('entity_test/add');
diff --git a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
index 9559be7..01dcf0a 100644
--- a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
+++ b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
@@ -42,7 +42,8 @@ public function access(Route $route, Request $request) {
* {@inheritdoc}
*/
public function accessEditEntityField(EntityInterface $entity, $field_name) {
- return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
+ $entity_type = $entity->entityType();
+ return $entity->access('update') && ($field = field_info_field($entity_type, $field_name)) && field_access('edit', $field, $entity_type, $entity);
}
/**
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
index c288284..c8db56e 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
@@ -19,7 +19,8 @@ class EditTestBase extends DrupalUnitTestBase {
*
* @var array
*/
- public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'filter', 'user', 'text', 'edit');
+ public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_test', 'number', 'filter', 'user', 'text', 'edit');
+
/**
* Sets the default field storage backend for fields created during tests.
*/
@@ -55,7 +56,8 @@ function setUp() {
function createFieldWithInstance($field_name, $type, $cardinality, $label, $instance_settings, $widget_type, $widget_settings, $formatter_type, $formatter_settings) {
$field = $field_name . '_field';
$this->$field = entity_create('field_entity', array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => $type,
'cardinality' => $cardinality,
));
diff --git a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
index e6a2bc0..d051ba2 100644
--- a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
+++ b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
@@ -62,7 +62,8 @@ function testEmailField() {
// Create a field with settings to validate.
$field_name = drupal_strtolower($this->randomName());
$this->field = entity_create('field_entity', array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => 'email',
));
$this->field->save();
diff --git a/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php b/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php
index 7e72163..b4a1bfe 100644
--- a/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php
+++ b/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php
@@ -36,7 +36,8 @@ public function setUp() {
// Create an email field and instance for validation.
entity_create('field_entity', array(
- 'field_name' => 'field_email',
+ 'name' => 'field_email',
+ 'entity_type' => 'entity_test',
'type' => 'email',
))->save();
entity_create('field_instance', array(
diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php
index 6a1c9de..128bc7c 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php
+++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php
@@ -209,7 +209,7 @@ public function setComponent($name, array $options = array()) {
}
if ($instance = field_info_instance($this->targetEntityType, $name, $this->bundle)) {
- $field = field_info_field($instance['field_name']);
+ $field = $instance->getField();
$options = $this->pluginManager->prepareConfiguration($field['type'], $options);
// Clear the persisted plugin, if any.
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
index fdeaa1b..637489b 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
@@ -131,7 +131,7 @@ public function testExtraFieldComponent() {
* Tests the behavior of a field component within an EntityDisplay object.
*/
public function testFieldComponent() {
- $this->enableModules(array('field_sql_storage', 'field_test'));
+ $this->enableModules(array('field_test'));
$display = entity_create('entity_display', array(
'targetEntityType' => 'entity_test',
@@ -142,7 +142,8 @@ public function testFieldComponent() {
$field_name = 'test_field';
// Create a field and an instance.
$field = entity_create('field_entity', array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field'
));
$field->save();
@@ -202,7 +203,7 @@ public function testFieldComponent() {
* Tests renaming and deleting a bundle.
*/
public function testRenameDeleteBundle() {
- $this->enableModules(array('field_sql_storage', 'field_test', 'node', 'system', 'text'));
+ $this->enableModules(array('field_test', 'node', 'system', 'text'));
$this->installSchema('system', array('variable'));
$this->installSchema('node', array('node'));
@@ -239,12 +240,13 @@ public function testRenameDeleteBundle() {
* Tests deleting field instance.
*/
public function testDeleteFieldInstance() {
- $this->enableModules(array('field_sql_storage', 'field_test'));
+ $this->enableModules(array('field_test'));
$field_name = 'test_field';
// Create a field and an instance.
$field = entity_create('field_entity', array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field'
));
$field->save();
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
index 8d7df46..595ac74 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
@@ -53,7 +53,7 @@ public function testEntityGetFromDisplay() {
* Tests the behavior of a field component within an EntityFormDisplay object.
*/
public function testFieldComponent() {
- $this->enableModules(array('field_sql_storage', 'field_test'));
+ $this->enableModules(array('field_test'));
$form_display = entity_create('entity_form_display', array(
'targetEntityType' => 'entity_test',
@@ -64,7 +64,8 @@ public function testFieldComponent() {
// Create a field and an instance.
$field_name = 'test_field';
$field = entity_create('field_entity', array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field'
));
$field->save();
diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module
index 4d1554d..10364d8 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -37,7 +37,7 @@ function entity_reference_field_info_alter(&$info) {
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) {
- $field = field_info_field($field_name);
+ $field = $instance->getField();
if ($field['type'] != 'entity_reference') {
continue;
}
@@ -84,7 +84,7 @@ function entity_reference_field_entity_update(FieldInterface $field) {
return;
}
- $field_name = $field->id();
+ $field_name = $field->getFieldName();
foreach ($field->bundles() as $entity_type => $bundles) {
foreach ($bundles as $bundle) {
@@ -225,14 +225,14 @@ function entity_reference_create_instance($entity_type, $bundle, $field_name, $f
}
// Look for or add the specified field to the requested entity bundle.
- $field = field_info_field($field_name);
+ $field = field_info_field($entity_type, $field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
if (empty($field)) {
$field = array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
'type' => 'entity_reference',
- 'entity_types' => array($entity_type),
+ 'entity_type' => $entity_type,
'settings' => array(
'target_type' => $target_entity_type,
),
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php b/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php
index b6099f1..644304a 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php
@@ -69,7 +69,7 @@ public static function create(ContainerInterface $container) {
* The matched labels as json.
*/
public function handleAutocomplete(Request $request, $type, $field_name, $entity_type, $bundle_name, $entity_id) {
- if (!$field = field_info_field($field_name)) {
+ if (!$field = field_info_field($entity_type, $field_name)) {
throw new AccessDeniedHttpException();
}
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
index 5ee7fa9..74a1a8a 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
@@ -107,7 +107,7 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
$fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table']));
foreach (field_info_instances($target_type) as $bundle_instances) {
foreach ($bundle_instances as $instance_name => $instance_info) {
- $field_info = field_info_field($instance_name);
+ $field_info = $instance_info->getField();
foreach ($field_info['columns'] as $column_name => $column_info) {
$fields[$instance_name . '.' . $column_name] = t('@label (@column)', array('@label' => $instance_info['label'], '@column' => $column_name));
}
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
index a19e1a9..d81dc5e 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
@@ -36,12 +36,13 @@ function setUp() {
$this->referenced_type = $referenced->type;
entity_create('field_entity', array(
+ 'name' => 'test_field',
+ 'entity_type' => 'node',
'translatable' => FALSE,
'entity_types' => array(),
'settings' => array(
'target_type' => 'node',
),
- 'field_name' => 'test_field',
'type' => 'entity_reference',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
))->save();
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
index e100d4e..4119240 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
@@ -89,7 +89,7 @@ public function setUp() {
array('target_bundles' => array($this->bundle))
);
- $this->field = Field::fieldInfo()->getField($this->fieldName);
+ $this->field = Field::fieldInfo()->getField($this->entityType, $this->fieldName);
$instances = Field::fieldInfo()->getBundleInstances($this->entityType, $this->bundle);
$this->instance = $instances[$this->fieldName];
}
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
index 8c17fd6..2a50949 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
@@ -7,6 +7,7 @@
namespace Drupal\entity_reference\Tests;
+use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\Core\Entity\Field\FieldInterface;
use Drupal\Core\Entity\Field\FieldItemInterface;
use Drupal\Core\Language\Language;
@@ -110,16 +111,16 @@ public function testEntityReferenceItem() {
* Tests foreign key support.
*/
public function testEntityReferenceFieldSchema() {
- $field = field_info_field('field_test_taxonomy');
+ $field = field_info_field('entity_test', 'field_test_taxonomy');
$foreign_key_column_name = 'target_id';
// Grab the SQL schema and verify that the 'foreign keys' are present.
- $schemas = _field_sql_storage_schema($field);
- $schema = $schemas[_field_sql_storage_tablename($field)];
+ $schemas = DatabaseStorageController::_fieldSqlSchema($field);
+ $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
$this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema.');
$foreign_key = reset($schema['foreign keys']);
- $foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_column_name);
+ $foreign_key_column = DatabaseStorageController::_fieldColumnName($field, $foreign_key_column_name);
$this->assertEqual($foreign_key['table'], 'taxonomy_term_data', 'Foreign key table name preserved in the schema.');
$this->assertEqual($foreign_key['columns'][$foreign_key_column], 'tid', 'Foreign key column name preserved in the schema.');
@@ -127,10 +128,10 @@ public function testEntityReferenceFieldSchema() {
// foreign key is present.
$field_name = 'field_test_vocabulary';
entity_reference_create_instance('entity_test', 'entity_test', $field_name, 'Test vocabulary reference', 'taxonomy_vocabulary');
- $field = field_info_field($field_name);
+ $field = field_info_field('entity_test', $field_name);
- $schemas = _field_sql_storage_schema($field);
- $schema = $schemas[_field_sql_storage_tablename($field)];
+ $schemas = DatabaseStorageController::_fieldSqlSchema($field);
+ $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
$this->assertFalse(isset($schema['foreign keys']), 'There is no foreign key in the schema.');
}
}
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
index c3107ec..b97dce4 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
@@ -24,7 +24,7 @@ public static function getInfo() {
);
}
- public static $modules = array('node', 'comment', 'entity_reference');
+ public static $modules = array('node', 'comment', 'entity_reference', 'entity_test');
function setUp() {
parent::setUp();
@@ -61,19 +61,20 @@ protected function assertReferenceable(FieldDefinitionInterface $field_definitio
public function testNodeHandler() {
// Create a field and instance.
$field = entity_create('field_entity', array(
+ 'name' => 'test_field',
+ 'entity_type' => 'entity_test',
'translatable' => FALSE,
'entity_types' => array(),
'settings' => array(
'target_type' => 'node',
),
- 'field_name' => 'test_field',
'type' => 'entity_reference',
'cardinality' => '1',
));
$field->save();
$instance = entity_create('field_instance', array(
'field_name' => 'test_field',
- 'entity_type' => 'test_entity',
+ 'entity_type' => 'entity_test',
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
@@ -205,19 +206,19 @@ public function testNodeHandler() {
public function testUserHandler() {
// Create a field and instance.
$field = entity_create('field_entity', array(
+ 'name' => 'test_field',
+ 'entity_type' => 'entity_test',
'translatable' => FALSE,
- 'entity_types' => array(),
'settings' => array(
'target_type' => 'user',
),
- 'field_name' => 'test_field',
'type' => 'entity_reference',
'cardinality' => '1',
));
$field->save();
$instance = entity_create('field_instance', array(
'field_name' => 'test_field',
- 'entity_type' => 'test_entity',
+ 'entity_type' => 'entity_test',
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
@@ -351,19 +352,20 @@ public function testUserHandler() {
public function testCommentHandler() {
// Create a field and instance.
$field = entity_create('field_entity', array(
+ 'name' => 'test_field',
+ 'entity_type' => 'entity_test',
'translatable' => FALSE,
'entity_types' => array(),
'settings' => array(
'target_type' => 'comment',
),
- 'field_name' => 'test_field',
'type' => 'entity_reference',
'cardinality' => '1',
));
$field->save();
$instance = entity_create('field_instance', array(
'field_name' => 'test_field',
- 'entity_type' => 'test_entity',
+ 'entity_type' => 'entity_test',
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
index b44c54d..6ed2ace 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
@@ -22,7 +22,7 @@ public static function getInfo() {
);
}
- public static $modules = array('node', 'entity_reference');
+ public static $modules = array('node', 'entity_reference', 'entity_test');
function setUp() {
parent::setUp();
@@ -37,7 +37,8 @@ function setUp() {
public function testSort() {
// Add text field to entity, to sort by.
entity_create('field_entity', array(
- 'field_name' => 'field_text',
+ 'name' => 'field_text',
+ 'entity_type' => 'node',
'type' => 'text',
'entity_types' => array('node'),
))->save();
@@ -54,19 +55,19 @@ public function testSort() {
// Create a field and instance.
$field = entity_create('field_entity', array(
+ 'name' => 'test_field',
+ 'entity_type' => 'entity_test',
'translatable' => FALSE,
- 'entity_types' => array(),
'settings' => array(
'target_type' => 'node',
),
- 'field_name' => 'test_field',
'type' => 'entity_reference',
'cardinality' => 1,
));
$field->save();
$instance = entity_create('field_instance', array(
'field_name' => 'test_field',
- 'entity_type' => 'test_entity',
+ 'entity_type' => 'entity_test',
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
index c9b8d62..7e6a652 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
@@ -14,7 +14,7 @@
*/
class SelectionTest extends WebTestBase {
- public static $modules = array('views', 'entity_reference', 'entity_reference_test');
+ public static $modules = array('views', 'entity_reference', 'entity_reference_test', 'entity_test');
public static function getInfo() {
return array(
@@ -41,19 +41,19 @@ public function testSelectionHandler() {
// Create a field and instance.
$field = entity_create('field_entity', array(
+ 'name' => 'test_field',
+ 'entity_type' => 'entity_test',
'translatable' => FALSE,
- 'entity_types' => array(),
'settings' => array(
'target_type' => 'node',
),
- 'field_name' => 'test_field',
'type' => 'entity_reference',
'cardinality' => '1',
));
$field->save();
$instance = entity_create('field_instance', array(
'field_name' => 'test_field',
- 'entity_type' => 'test_entity',
+ 'entity_type' => 'entity_test',
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'views',
diff --git a/core/modules/field/config/field.settings.yml b/core/modules/field/config/field.settings.yml
index 5ca0000..6b569a9 100644
--- a/core/modules/field/config/field.settings.yml
+++ b/core/modules/field/config/field.settings.yml
@@ -1,3 +1,2 @@
-default_storage: field_sql_storage
language_fallback: '1'
purge_batch_size: 10
diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml
index 1b23198..2a942ea 100644
--- a/core/modules/field/config/schema/field.schema.yml
+++ b/core/modules/field/config/schema/field.schema.yml
@@ -4,9 +4,6 @@ field.settings:
type: mapping
label: 'Field settings'
mapping:
- default_storage:
- type: string
- label: 'The default storage backend for a field'
language_fallback:
type: boolean
label: 'Whether the field display falls back to global language fallback configuration'
@@ -30,6 +27,9 @@ field.field.*:
langcode:
type: string
label: 'Default language'
+ entity_type:
+ type: string
+ label: 'Entity type'
type:
type: string
label: 'Type'
@@ -41,28 +41,6 @@ field.field.*:
active:
type: boolean
label: 'Active'
- entity_types:
- type: sequence
- label: 'Allowed entity types'
- sequence:
- - type: string
- label: 'Entity type'
- storage:
- type: mapping
- label: 'Storage'
- mapping:
- type:
- type: string
- label: 'Type'
- settings:
- type: field_storage.[%parent.type].settings
- label: 'Settings'
- module:
- type: string
- label: 'Module'
- active:
- type: boolean
- label: 'Active'
locked:
type: boolean
label: 'Locked'
@@ -101,9 +79,6 @@ field.instance.*.*.*:
field_uuid:
type: string
label: 'Field UUID'
- entity_type:
- type: string
- label: 'Allowed entity types'
bundle:
type: string
label: 'Bundle'
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 312ebb5..6f3c1d0 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -334,27 +334,6 @@ function hook_field_attach_form(\Drupal\Core\Entity\EntityInterface $entity, &$f
}
/**
- * Act on field_attach_load().
- *
- * This hook is invoked after the field module has performed the operation.
- *
- * Unlike other field_attach hooks, this hook accounts for 'multiple loads'.
- * Instead of the usual $entity parameter, it accepts an array of entities,
- * indexed by entity ID. For performance reasons, information for all available
- * entities should be loaded in a single query where possible.
- *
- * The changes made to the entities' field values get cached by the field cache
- * for subsequent loads.
- *
- * See field_attach_load() for details and arguments.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function hook_field_attach_load($entity_type, $entities, $age, $options) {
- // @todo Needs function body.
-}
-
-/**
* Act on field_attach_extract_form_values().
*
* This hook is invoked after the field module has performed the operation.
@@ -400,31 +379,6 @@ function hook_field_attach_preprocess_alter(&$variables, $context) {
}
/**
- * Act on field_purge_data().
- *
- * This hook is invoked in field_purge_data() and allows modules to act on
- * purging data from a single field pseudo-entity. For example, if a module
- * relates data in the field with its own data, it may purge its own data during
- * this process as well.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The pseudo-entity whose field data is being purged.
- * @param $field
- * The (possibly deleted) field whose data is being purged.
- * @param $instance
- * The deleted field instance whose data is being purged.
- *
- * @see @link field_purge Field API bulk data deletion @endlink
- * @see field_purge_data()
- */
-function hook_field_attach_purge(\Drupal\Core\Entity\EntityInterface $entity, $field, $instance) {
- // find the corresponding data in mymodule and purge it
- if ($entity->entityType() == 'node' && $field->field_name == 'my_field_name') {
- mymodule_remove_mydata($entity->id());
- }
-}
-
-/**
* Perform alterations on field_attach_view() or field_view_field().
*
* This hook is invoked after the field module has performed the operation.
@@ -512,681 +466,6 @@ function hook_field_available_languages_alter(&$langcodes, $context) {
*/
/**
- * @addtogroup field_storage
- * @{
- */
-
-/**
- * Expose Field API storage backends.
- *
- * @return
- * An array describing the storage backends implemented by the module. The
- * keys are storage backend names. To avoid name clashes, storage backend
- * names should be prefixed with the name of the module that exposes them. The
- * values are arrays describing the storage backend, with the following
- * key/value pairs:
- * - label: The human-readable name of the storage backend.
- * - description: A short description for the storage backend.
- * - settings: An array whose keys are the names of the settings available to
- * the storage backend, and whose values are the default values of those
- * settings.
- */
-function hook_field_storage_info() {
- return array(
- 'field_sql_storage' => array(
- 'label' => t('Default SQL storage'),
- 'description' => t('Stores fields in the local SQL database, using per-field tables.'),
- 'settings' => array(),
- ),
- );
-}
-
-/**
- * Perform alterations on Field API storage types.
- *
- * @param $info
- * Array of informations on storage types exposed by
- * hook_field_field_storage_info() implementations.
- */
-function hook_field_storage_info_alter(&$info) {
- // Add a setting to a storage type.
- $info['field_sql_storage']['settings'] += array(
- 'mymodule_additional_setting' => 'default value',
- );
-}
-
-/**
- * Reveal the internal details about the storage for a field.
- *
- * For example, an SQL storage module might return the Schema API structure for
- * the table. A key/value storage module might return the server name,
- * authentication credentials, and bin name.
- *
- * Field storage modules are not obligated to implement this hook. Modules that
- * rely on these details must only use them for read operations.
- *
- * @param $field
- * A field structure.
- *
- * @return
- * An array of details.
- * - The first dimension is a store type (sql, solr, etc).
- * - The second dimension indicates the age of the values in the store
- * FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
- * - Other dimensions are specific to the field storage module.
- *
- * @see hook_field_storage_details_alter()
- */
-function hook_field_storage_details($field) {
- $details = array();
-
- // Add field columns.
- foreach ((array) $field['columns'] as $column_name => $attributes) {
- $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
- $columns[$column_name] = $real_name;
- }
- return array(
- 'sql' => array(
- FIELD_LOAD_CURRENT => array(
- _field_sql_storage_tablename($field) => $columns,
- ),
- FIELD_LOAD_REVISION => array(
- _field_sql_storage_revision_tablename($field) => $columns,
- ),
- ),
- );
-}
-
-/**
- * Perform alterations on Field API storage details.
- *
- * @param $details
- * An array of storage details for fields as exposed by
- * hook_field_storage_details() implementations.
- * @param $field
- * A field structure.
- *
- * @see hook_field_storage_details()
- */
-function hook_field_storage_details_alter(&$details, $field) {
- if ($field['field_name'] == 'field_of_interest') {
- $columns = array();
- foreach ((array) $field['columns'] as $column_name => $attributes) {
- $columns[$column_name] = $column_name;
- }
- $details['drupal_variables'] = array(
- FIELD_LOAD_CURRENT => array(
- 'moon' => $columns,
- ),
- FIELD_LOAD_REVISION => array(
- 'mars' => $columns,
- ),
- );
- }
-}
-
-/**
- * Load field data for a set of entities.
- *
- * This hook is invoked from field_attach_load() to ask the field storage module
- * to load field data.
- *
- * Modules implementing this hook should load field values and add them to
- * objects in $entities. Fields with no values should be added as empty arrays.
- *
- * @param $entity_type
- * The type of entity, such as 'node' or 'user'.
- * @param $entities
- * The array of entity objects to add fields to, keyed by entity ID.
- * @param $age
- * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- * FIELD_LOAD_REVISION to load the version indicated by each entity.
- * @param $fields
- * An array listing the fields to be loaded. The keys of the array are field
- * UUIDs, and the values of the array are the entity IDs (or revision IDs,
- * depending on the $age parameter) to add each field to.
- * @param $options
- * An associative array of additional options, with the following keys:
- * - deleted: If TRUE, deleted fields should be loaded as well as non-deleted
- * fields. If unset or FALSE, only non-deleted fields should be loaded.
- */
-function hook_field_storage_load($entity_type, $entities, $age, $fields, $options) {
- $load_current = $age == FIELD_LOAD_CURRENT;
-
- foreach ($fields as $field_id => $ids) {
- // By the time this hook runs, the relevant field definitions have been
- // populated and cached in FieldInfo, so calling field_info_field_by_id()
- // on each field individually is more efficient than loading all fields in
- // memory upfront with field_info_field_by_ids().
- $field = field_info_field_by_id($field_id);
- $field_name = $field['field_name'];
- $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
-
- $query = db_select($table, 't')
- ->fields('t')
- ->condition('entity_type', $entity_type)
- ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
- ->condition('langcode', field_available_languages($entity_type, $field), 'IN')
- ->orderBy('delta');
-
- if (empty($options['deleted'])) {
- $query->condition('deleted', 0);
- }
-
- $results = $query->execute();
-
- $delta_count = array();
- foreach ($results as $row) {
- if (!isset($delta_count[$row->entity_id][$row->langcode])) {
- $delta_count[$row->entity_id][$row->langcode] = 0;
- }
-
- if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
- $item = array();
- // For each column declared by the field, populate the item
- // from the prefixed database column.
- foreach ($field['columns'] as $column => $attributes) {
- $column_name = _field_sql_storage_columnname($field_name, $column);
- $item[$column] = $row->$column_name;
- }
-
- // Add the item to the field values for the entity.
- $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
- $delta_count[$row->entity_id][$row->langcode]++;
- }
- }
- }
-}
-
-/**
- * Write field data for an entity.
- *
- * This hook is invoked from field_attach_insert() and field_attach_update(), to
- * ask the field storage module to save field data.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity on which to operate.
- * @param $op
- * FIELD_STORAGE_UPDATE when updating an existing entity,
- * FIELD_STORAGE_INSERT when inserting a new entity.
- * @param $fields
- * An array listing the fields to be written. The keys and values of the
- * array are field UUIDs.
- */
-function hook_field_storage_write(\Drupal\Core\Entity\EntityInterface $entity, $op, $fields) {
- $id = $entity->id();
- $vid = $entity->getRevisionId();
- $bundle = $entity->bundle();
- if (!isset($vid)) {
- $vid = $id;
- }
-
- foreach ($fields as $field_id) {
- $field = field_info_field_by_id($field_id);
- $field_name = $field['field_name'];
- $table_name = _field_sql_storage_tablename($field);
- $revision_name = _field_sql_storage_revision_tablename($field);
-
- $all_langcodes = field_available_languages($entity->entityType(), $field);
- $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
-
- // Delete and insert, rather than update, in case a value was added.
- if ($op == FIELD_STORAGE_UPDATE) {
- // Delete language codes present in the incoming $entity->$field_name.
- // Delete all language codes if $entity->$field_name is empty.
- $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
- if ($langcodes) {
- db_delete($table_name)
- ->condition('entity_type', $entity->entityType())
- ->condition('entity_id', $id)
- ->condition('langcode', $langcodes, 'IN')
- ->execute();
- db_delete($revision_name)
- ->condition('entity_type', $entity->entityType())
- ->condition('entity_id', $id)
- ->condition('revision_id', $vid)
- ->condition('langcode', $langcodes, 'IN')
- ->execute();
- }
- }
-
- // Prepare the multi-insert query.
- $do_insert = FALSE;
- $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
- foreach ($field['columns'] as $column => $attributes) {
- $columns[] = _field_sql_storage_columnname($field_name, $column);
- }
- $query = db_insert($table_name)->fields($columns);
- $revision_query = db_insert($revision_name)->fields($columns);
-
- foreach ($field_langcodes as $langcode) {
- $items = (array) $entity->{$field_name}[$langcode];
- $delta_count = 0;
- foreach ($items as $delta => $item) {
- // We now know we have someting to insert.
- $do_insert = TRUE;
- $record = array(
- 'entity_type' => $entity->entityType(),
- 'entity_id' => $id,
- 'revision_id' => $vid,
- 'bundle' => $bundle,
- 'delta' => $delta,
- 'langcode' => $langcode,
- );
- foreach ($field['columns'] as $column => $attributes) {
- $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
- }
- $query->values($record);
- if (isset($vid)) {
- $revision_query->values($record);
- }
-
- if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
- break;
- }
- }
- }
-
- // Execute the query if we have values to insert.
- if ($do_insert) {
- $query->execute();
- $revision_query->execute();
- }
- }
-}
-
-/**
- * Delete all field data for an entity.
- *
- * This hook is invoked from field_attach_delete() to ask the field storage
- * module to delete field data.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity on which to operate.
- * @param $fields
- * An array listing the fields to delete. The keys and values of the
- * array are field UUIDs.
- */
-function hook_field_storage_delete(\Drupal\Core\Entity\EntityInterface $entity, $fields) {
- foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
- if (isset($fields[$instance['field_id']])) {
- $field = field_info_field_by_id($instance['field_id']);
- field_sql_storage_field_storage_purge($entity, $field, $instance);
- }
- }
-}
-
-/**
- * Delete a single revision of field data for an entity.
- *
- * This hook is invoked from field_attach_delete_revision() to ask the field
- * storage module to delete field revision data.
- *
- * Deleting the current (most recently written) revision is not
- * allowed as has undefined results.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity on which to operate.
- * @param $fields
- * An array listing the fields to delete. The keys and values of the
- * array are field UUIDs.
- */
-function hook_field_storage_delete_revision(\Drupal\Core\Entity\EntityInterface $entity, $fields) {
- $vid = $entity->getRevisionId();
- if (isset($vid)) {
- foreach ($fields as $field_id) {
- $field = field_info_field_by_id($field_id);
- $revision_name = _field_sql_storage_revision_tablename($field);
- db_delete($revision_name)
- ->condition('entity_type', $entity_type)
- ->condition('entity_id', $entity->id())
- ->condition('revision_id', $vid)
- ->execute();
- }
- }
-}
-
-/**
- * Execute a Drupal\Core\Entity\EntityFieldQuery.
- *
- * This hook is called to find the entities having certain entity and field
- * conditions and sort them in the given field order. If the field storage
- * engine also handles property sorts and orders, it should unset those
- * properties in the called object to signal that those have been handled.
- *
- * @param Drupal\Core\Entity\EntityFieldQuery $query
- * An EntityFieldQuery.
- *
- * @return
- * See Drupal\Core\Entity\EntityFieldQuery::execute() for the return values.
- */
-function hook_field_storage_query($query) {
- $groups = array();
- if ($query->age == FIELD_LOAD_CURRENT) {
- $tablename_function = '_field_sql_storage_tablename';
- $id_key = 'entity_id';
- }
- else {
- $tablename_function = '_field_sql_storage_revision_tablename';
- $id_key = 'revision_id';
- }
- $table_aliases = array();
- // Add tables for the fields used.
- foreach ($query->fields as $key => $field) {
- $tablename = $tablename_function($field);
- // Every field needs a new table.
- $table_alias = $tablename . $key;
- $table_aliases[$key] = $table_alias;
- if ($key) {
- $select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
- }
- else {
- $select_query = db_select($tablename, $table_alias);
- $select_query->addTag('entity_field_access');
- $select_query->addMetaData('base_table', $tablename);
- $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
- $field_base_table = $table_alias;
- }
- if ($field['cardinality'] != 1) {
- $select_query->distinct();
- }
- }
-
- // Add field conditions.
- foreach ($query->fieldConditions as $key => $condition) {
- $table_alias = $table_aliases[$key];
- $field = $condition['field'];
- // Add the specified condition.
- $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $condition['column']);
- $query->addCondition($select_query, $sql_field, $condition);
- // Add delta / language group conditions.
- foreach (array('delta', 'langcode') as $column) {
- if (isset($condition[$column . '_group'])) {
- $group_name = $condition[$column . '_group'];
- if (!isset($groups[$column][$group_name])) {
- $groups[$column][$group_name] = $table_alias;
- }
- else {
- $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column");
- }
- }
- }
- }
-
- if (isset($query->deleted)) {
- $select_query->condition("$field_base_table.deleted", (int) $query->deleted);
- }
-
- // Is there a need to sort the query by property?
- $has_property_order = FALSE;
- foreach ($query->order as $order) {
- if ($order['type'] == 'property') {
- $has_property_order = TRUE;
- }
- }
-
- if ($query->propertyConditions || $has_property_order) {
- if (empty($query->entityConditions['entity_type']['value'])) {
- throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
- }
- $entity_type = $query->entityConditions['entity_type']['value'];
- $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table);
- $query->entityConditions['entity_type']['operator'] = '=';
- foreach ($query->propertyConditions as $property_condition) {
- $query->addCondition($select_query, "$entity_base_table." . $property_condition['column'], $property_condition);
- }
- }
- foreach ($query->entityConditions as $key => $condition) {
- $query->addCondition($select_query, "$field_base_table.$key", $condition);
- }
-
- // Order the query.
- foreach ($query->order as $order) {
- if ($order['type'] == 'entity') {
- $key = $order['specifier'];
- $select_query->orderBy("$field_base_table.$key", $order['direction']);
- }
- elseif ($order['type'] == 'field') {
- $specifier = $order['specifier'];
- $field = $specifier['field'];
- $table_alias = $table_aliases[$specifier['index']];
- $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']);
- $select_query->orderBy($sql_field, $order['direction']);
- }
- elseif ($order['type'] == 'property') {
- $select_query->orderBy("$entity_base_table." . $order['specifier'], $order['direction']);
- }
- }
-
- return $query->finishQuery($select_query, $id_key);
-}
-
-/**
- * Act on creation of a new field.
- *
- * This hook is invoked during the creation of a field to ask the field storage
- * module to save field information and prepare for storing field instances. If
- * there is a problem, the field storage module should throw an exception.
- *
- * @param $field
- * The field structure being created.
- */
-function hook_field_storage_create_field($field) {
- $schema = _field_sql_storage_schema($field);
- foreach ($schema as $name => $table) {
- db_create_table($name, $table);
- }
- drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Update the storage information for a field.
- *
- * This is invoked on the field's storage module when updating the field,
- * before the new definition is saved to the database. The field storage module
- * should update its storage tables according to the new field definition. If
- * there is a problem, the field storage module should throw an exception.
- *
- * @param $field
- * The updated field structure to be saved.
- * @param $prior_field
- * The previously-saved field structure.
- */
-function hook_field_storage_update_field($field, $prior_field) {
- if (!$field->hasData()) {
- // There is no data. Re-create the tables completely.
- $prior_schema = _field_sql_storage_schema($prior_field);
- foreach ($prior_schema as $name => $table) {
- db_drop_table($name, $table);
- }
- $schema = _field_sql_storage_schema($field);
- foreach ($schema as $name => $table) {
- db_create_table($name, $table);
- }
- }
- else {
- // There is data. See field_sql_storage_field_storage_update_field() for
- // an example of what to do to modify the schema in place, preserving the
- // old data as much as possible.
- }
- drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Act on deletion of a field.
- *
- * This hook is invoked during the deletion of a field to ask the field storage
- * module to mark all information stored in the field for deletion.
- *
- * @param $field
- * The field being deleted.
- */
-function hook_field_storage_delete_field($field) {
- // Mark all data associated with the field for deletion.
- $field['deleted'] = FALSE;
- $table = _field_sql_storage_tablename($field);
- $revision_table = _field_sql_storage_revision_tablename($field);
- db_update($table)
- ->fields(array('deleted' => 1))
- ->execute();
-
- // Move the table to a unique name while the table contents are being deleted.
- $field['deleted'] = TRUE;
- $new_table = _field_sql_storage_tablename($field);
- $revision_new_table = _field_sql_storage_revision_tablename($field);
- db_rename_table($table, $new_table);
- db_rename_table($revision_table, $revision_new_table);
- drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Act on deletion of a field instance.
- *
- * This hook is invoked during the deletion of a field instance to ask the
- * field storage module to mark all information stored for the field instance
- * for deletion.
- *
- * @param $instance
- * The instance being deleted.
- */
-function hook_field_storage_delete_instance($instance) {
- $field = field_info_field($instance['field_name']);
- $table_name = _field_sql_storage_tablename($field);
- $revision_name = _field_sql_storage_revision_tablename($field);
- db_update($table_name)
- ->fields(array('deleted' => 1))
- ->condition('entity_type', $instance['entity_type'])
- ->condition('bundle', $instance['bundle'])
- ->execute();
- db_update($revision_name)
- ->fields(array('deleted' => 1))
- ->condition('entity_type', $instance['entity_type'])
- ->condition('bundle', $instance['bundle'])
- ->execute();
-}
-
-/**
- * Act before the storage backends load field data.
- *
- * This hook allows modules to load data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * This lets 3rd party modules override, mirror, share, or otherwise store a
- * subset of fields in a different way than the current storage engine. Possible
- * use cases include per-bundle storage, per-combo-field storage, etc.
- *
- * Modules implementing this hook should load field values and add them to
- * objects in $entities. Fields with no values should be added as empty arrays.
- * In addition, fields loaded should be added as keys to $skip_fields.
- *
- * @param $entity_type
- * The type of entity, such as 'node' or 'user'.
- * @param $entities
- * The array of entity objects to add fields to, keyed by entity ID.
- * @param $age
- * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- * FIELD_LOAD_REVISION to load the version indicated by each entity.
- * @param $skip_fields
- * An array keyed by field UUIDs whose data has already been loaded and
- * therefore should not be loaded again. Add a key to this array to indicate
- * that your module has already loaded a field.
- * @param $options
- * An associative array of additional options, with the following keys:
- * - field_id: The field UUID that should be loaded. If unset, all fields
- * should be loaded.
- * - deleted: If TRUE, deleted fields should be loaded as well as non-deleted
- * fields. If unset or FALSE, only non-deleted fields should be loaded.
- */
-function hook_field_storage_pre_load($entity_type, $entities, $age, &$skip_fields, $options) {
- // @todo Needs function body.
-}
-
-/**
- * Act before the storage backends insert field data.
- *
- * This hook allows modules to store data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity with fields to save.
- * @param $skip_fields
- * An array keyed by field UUIDs whose data has already been written and
- * therefore should not be written again. The values associated with these
- * keys are not specified.
- * @return
- * Saved field UUIDs are set as keys in $skip_fields.
- */
-function hook_field_storage_pre_insert(\Drupal\Core\Entity\EntityInterface $entity, &$skip_fields) {
- if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
- $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
- foreach ($entity->taxonomy_forums as $language) {
- foreach ($language as $delta) {
- $query->values(array(
- 'nid' => $entity->id(),
- 'title' => $entity->title,
- 'tid' => $delta['value'],
- 'sticky' => $entity->sticky,
- 'created' => $entity->created,
- 'comment_count' => 0,
- 'last_comment_timestamp' => $entity->created,
- ));
- }
- }
- $query->execute();
- }
-}
-
-/**
- * Act before the storage backends update field data.
- *
- * This hook allows modules to store data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity with fields to save.
- * @param $skip_fields
- * An array keyed by field UUIDs whose data has already been written and
- * therefore should not be written again. The values associated with these
- * keys are not specified.
- * @return
- * Saved field UUIDs are set as keys in $skip_fields.
- */
-function hook_field_storage_pre_update(\Drupal\Core\Entity\EntityInterface $entity, &$skip_fields) {
- $first_call = &drupal_static(__FUNCTION__, array());
-
- if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
- // We don't maintain data for old revisions, so clear all previous values
- // from the table. Since this hook runs once per field, per entity, make
- // sure we only wipe values once.
- if (!isset($first_call[$entity->id()])) {
- $first_call[$entity->id()] = FALSE;
- db_delete('forum_index')->condition('nid', $entity->id())->execute();
- }
- // Only save data to the table if the node is published.
- if ($entity->status) {
- $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
- foreach ($entity->taxonomy_forums as $language) {
- foreach ($language as $delta) {
- $query->values(array(
- 'nid' => $entity->id(),
- 'title' => $entity->title,
- 'tid' => $delta['value'],
- 'sticky' => $entity->sticky,
- 'created' => $entity->created,
- 'comment_count' => 0,
- 'last_comment_timestamp' => $entity->created,
- ));
- }
- }
- $query->execute();
- // The logic for determining last_comment_count is fairly complex, so
- // call _forum_update_forum_index() too.
- _forum_update_forum_index($entity->id());
- }
- }
-}
-
-/**
* Returns the maximum weight for the entity components handled by the module.
*
* Field API takes care of fields and 'extra_fields'. This hook is intended for
@@ -1217,10 +496,6 @@ function hook_field_info_max_weight($entity_type, $bundle, $context, $context_mo
}
/**
- * @} End of "addtogroup field_storage".
- */
-
-/**
* @addtogroup field_crud
* @{
*/
@@ -1300,63 +575,6 @@ function hook_field_purge_instance($instance) {
}
/**
- * Remove field storage information when a field record is purged.
- *
- * Called from field_purge_field() to allow the field storage module to remove
- * field information when a field is being purged.
- *
- * @param $field
- * The field being purged.
- */
-function hook_field_storage_purge_field($field) {
- $table_name = _field_sql_storage_tablename($field);
- $revision_name = _field_sql_storage_revision_tablename($field);
- db_drop_table($table_name);
- db_drop_table($revision_name);
-}
-
-/**
- * Remove field storage information when a field instance is purged.
- *
- * Called from field_purge_instance() to allow the field storage module to
- * remove field instance information when a field instance is being purged.
- *
- * @param $instance
- * The instance being purged.
- */
-function hook_field_storage_purge_field_instance($instance) {
- db_delete('my_module_field_instance_info')
- ->condition('id', $instance['id'])
- ->execute();
-}
-
-/**
- * Remove field storage information when field data is purged.
- *
- * Called from field_purge_data() to allow the field storage module to delete
- * field data information.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The pseudo-entity whose field data to delete.
- * @param $field
- * The (possibly deleted) field whose data is being purged.
- * @param $instance
- * The deleted field instance whose data is being purged.
- */
-function hook_field_storage_purge(\Drupal\Core\Entity\EntityInterface $entity, $field, $instance) {
- $table_name = _field_sql_storage_tablename($field);
- $revision_name = _field_sql_storage_revision_tablename($field);
- db_delete($table_name)
- ->condition('entity_type', $entity->entityType())
- ->condition('entity_id', $entity->id())
- ->execute();
- db_delete($revision_name)
- ->condition('entity_type', $entity->entityType())
- ->condition('entity_id', $entity->id())
- ->execute();
-}
-
-/**
* @} End of "addtogroup field_crud".
*/
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 3125584..29036c7 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -9,45 +9,6 @@
use Drupal\entity\Entity\EntityFormDisplay;
/**
- * @defgroup field_storage Field Storage API
- * @{
- * Implements a storage engine for Field API data.
- *
- * The Field Attach API uses the Field Storage API to perform all "database
- * access". Each Field Storage API hook function defines a primitive database
- * operation such as read, write, or delete. The default field storage module,
- * field_sql_storage.module, uses the local SQL database to implement these
- * operations, but alternative field storage backends can choose to represent
- * the data in SQL differently or use a completely different storage mechanism
- * such as a cloud-based database.
- *
- * Each field defines which storage backend it uses. The Drupal configuration
- * 'field.settings.default_storage' identifies the storage backend used by
- * default.
- *
- * See @link field Field API @endlink for information about the other parts of
- * the Field API.
- */
-
-/**
- * Argument for an update operation.
- *
- * This is used in hook_field_storage_write when updating an existing entity.
- */
-const FIELD_STORAGE_UPDATE = 'update';
-
-/**
- * Argument for an insert operation.
- *
- * This is used in hook_field_storage_write when inserting a new entity.
- */
-const FIELD_STORAGE_INSERT = 'insert';
-
-/**
- * @} End of "defgroup field_storage".
- */
-
-/**
* @defgroup field_attach Field Attach API
* @{
* Operates on Field API data attached to Drupal entities.
@@ -166,7 +127,7 @@ function field_invoke_method($method, $target_function, EntityInterface $entity,
$target = call_user_func($target_function, $instance);
if (method_exists($target, $method)) {
- $field = field_info_field_by_id($instance['field_id']);
+ $field = $instance->getField();
$field_name = $field['field_name'];
// Determine the list of languages to iterate on.
@@ -273,7 +234,7 @@ function field_invoke_method_multiple($method, $target_function, array $entities
// Unless a language code suggestion is provided we iterate on all the
// available language codes.
- $field = field_info_field_by_id($instance['field_id']);
+ $field = $instance->getField();
$available_langcodes = field_available_languages($entity_type, $field);
$langcode = !empty($options['langcode'][$id]) ? $options['langcode'][$id] : $options['langcode'];
$langcodes = _field_language_suggestion($available_langcodes, $langcode, $field_name);
diff --git a/core/modules/field/field.deprecated.inc b/core/modules/field/field.deprecated.inc
index fb97eff..f2fe85a 100644
--- a/core/modules/field/field.deprecated.inc
+++ b/core/modules/field/field.deprecated.inc
@@ -164,18 +164,20 @@ function field_info_formatter_settings($type) {
* The function only returns active, non deleted fields.
*
* @return
- * An array keyed by field name. Each value is an array with two entries:
+ * An array keyed by entity type. Each value is an array which keys are
+ * field names and value is an array with two entries:
* - type: The field type.
- * - bundles: The bundles in which the field appears, as an array with entity
- * types as keys and the array of bundle names as values.
+ * - bundles: The bundles in which the field appears.
* Example:
* @code
* array(
- * 'body' => array(
- * 'bundles' => array(
- * 'node' => array('page', 'article'),
+ * 'node' => array(
+ * 'body' => array(
+ * 'bundles' => array(
+ * 'page', 'article'
+ * ),
+ * 'type' => 'text_with_summary',
* ),
- * 'type' => 'text_with_summary',
* ),
* );
* @endcode
@@ -188,8 +190,10 @@ function field_info_field_map() {
}
/**
- * Returns data about an individual field, given a field name.
+ * Returns data about an individual field.
*
+ * @param $entity_type
+ * The entity type.
* @param $field_name
* The name of the field to retrieve. $field_name can only refer to a
* non-deleted, active field. For deleted fields, use
@@ -207,8 +211,8 @@ function field_info_field_map() {
* @deprecated as of Drupal 8.0. Use
* Field::fieldInfo()->getField($field_name).
*/
-function field_info_field($field_name) {
- return Field::fieldInfo()->getField($field_name);
+function field_info_field($entity_type, $field_name) {
+ return Field::fieldInfo()->getField($entity_type, $field_name);
}
/**
@@ -334,6 +338,8 @@ function field_info_instance($entity_type, $field_name, $bundle_name) {
* This function will not return deleted fields. Use field_read_fields() instead
* for this purpose.
*
+ * @param $entity_type
+ * The entity type.
* @param $field_name
* The field name to read.
* @param array $include_additional
@@ -347,8 +353,8 @@ function field_info_instance($entity_type, $field_name, $bundle_name) {
* @deprecated as of Drupal 8.0. Use
* entity_load('field_entity', 'field_name').
*/
-function field_read_field($field_name, $include_additional = array()) {
- $fields = field_read_fields(array('field_name' => $field_name), $include_additional);
+function field_read_field($entity_type, $field_name, $include_additional = array()) {
+ $fields = field_read_fields(array('entity_type' => $entity_type, 'name' => $field_name), $include_additional);
return $fields ? current($fields) : FALSE;
}
@@ -583,215 +589,6 @@ function field_attach_form(EntityInterface $entity, &$form, &$form_state, $langc
}
/**
- * Loads fields for the current revisions of a group of entities.
- *
- * Loads all fields for each entity object in a group of a single entity type.
- * The loaded field values are added directly to the entity objects.
- *
- * field_attach_load() is automatically called by the default entity controller
- * class, and thus, in most cases, doesn't need to be explicitly called by the
- * entity type module.
- *
- * @param $entity_type
- * The type of entities in $entities; e.g., 'node' or 'user'.
- * @param $entities
- * An array of entities for which to load fields, keyed by entity ID. Each
- * entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
- * filled in. The function adds the loaded field data directly in the entity
- * objects of the $entities array.
- * @param $age
- * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- * FIELD_LOAD_REVISION to load the version indicated by each entity. Defaults
- * to FIELD_LOAD_CURRENT; use field_attach_load_revision() instead of passing
- * FIELD_LOAD_REVISION.
- * @param $options
- * An associative array of additional options, with the following keys:
- * - instance: A field instance entity, If provided, only values for the
- * corresponding field will be loaded, and no cache is written. This
- * option is only supported when all $entities are within the same bundle.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
- $load_current = $age == FIELD_LOAD_CURRENT;
- $load_deleted = !empty($options['instance']->deleted);
-
- // Merge default options.
- $options += array('instance' => NULL);
- // Set options for hook invocations.
- $hook_options = array(
- 'deleted' => $load_deleted,
- );
- if ($options['instance']) {
- $hook_options['field_id'] = $options['instance']->field_uuid;
- }
-
- $info = entity_get_info($entity_type);
- // Only the most current revision of non-deleted fields for cacheable entity
- // types can be cached.
- $cache_read = $load_current && $info['field_cache'] && !$load_deleted;
- // In addition, do not write to the cache when loading a single field.
- $cache_write = $cache_read && !isset($options['instance']);
-
- if (empty($entities)) {
- return;
- }
-
- // Ensure we are working with a BC mode entity.
- foreach ($entities as $id => $entity) {
- $entities[$id] = $entity->getBCEntity();
- }
-
- // Assume all entities will need to be queried. Entities found in the cache
- // will be removed from the list.
- $queried_entities = $entities;
-
- // Fetch available entities from cache, if applicable.
- if ($cache_read) {
- // Build the list of cache entries to retrieve.
- $cids = array();
- foreach ($entities as $id => $entity) {
- $cids[] = "field:$entity_type:$id";
- }
- $cache = cache('field')->getMultiple($cids);
- // Put the cached field values back into the entities and remove them from
- // the list of entities to query.
- foreach ($entities as $id => $entity) {
- $cid = "field:$entity_type:$id";
- if (isset($cache[$cid])) {
- unset($queried_entities[$id]);
- foreach ($cache[$cid]->data as $field_name => $values) {
- $entity->$field_name = $values;
- }
- }
- }
- }
-
- // Fetch other entities from their storage location.
- if ($queried_entities) {
- // The invoke order is:
- // - hook_field_storage_pre_load()
- // - storage backend's hook_field_storage_load()
- // - Field class's prepareCache() method.
- // - hook_field_attach_load()
-
- // Invoke hook_field_storage_pre_load(): let any module load field
- // data before the storage engine, accumulating along the way.
- $skip_fields = array();
- foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_load') as $module) {
- $function = $module . '_field_storage_pre_load';
- $function($entity_type, $queried_entities, $age, $skip_fields, $hook_options);
- }
-
- // Collect the storage backends used by the remaining fields in the entities.
- $storages = array();
- foreach ($queried_entities as $entity) {
- $id = $entity->id();
- $vid = $entity->getRevisionId();
-
- // Determine the list of field instances to work on.
- if ($options['instance']) {
- $instances = array($options['instance']);
- }
- else {
- $instances = field_info_instances($entity_type, $entity->bundle());
- }
-
- foreach ($instances as $instance) {
- $field = $instance->getField();
- $field_name = $field->id();
- if (!isset($queried_entities[$id]->{$field_name})) {
- $queried_entities[$id]->{$field_name} = array();
- }
- if (!isset($skip_fields[$field->uuid])) {
- $storages[$field->storage['type']][$field->uuid][] = $load_current ? $id : $vid;
- }
- }
- }
-
- // Invoke hook_field_storage_load() on the relevant storage backends.
- foreach ($storages as $storage => $fields) {
- $storage_info = field_info_storage_types($storage);
- module_invoke($storage_info['module'], 'field_storage_load', $entity_type, $queried_entities, $age, $fields, $hook_options);
- }
-
- // Invoke the field type's prepareCache() method.
- if (empty($options['instance'])) {
- foreach ($queried_entities as $entity) {
- \Drupal::entityManager()
- ->getStorageController($entity_type)
- ->invokeFieldItemPrepareCache($entity);
- }
- }
- else {
- // Do not rely on invokeFieldItemPrepareCache(), which only works on
- // fields listed in getFieldDefinitions(), and will fail if we are loading
- // values for a deleted field. Instead, generate FieldItem objects
- // directly, and call their prepareCache() method.
- foreach ($queried_entities as $entity) {
- $field = $options['instance']->getField();
- $field_name = $field->id();
- // Call the prepareCache() method on each item.
- foreach ($entity->{$field_name} as $langcode => $values) {
- $definition = _field_generate_entity_field_definition($field, $options['instance']);
- $items = \Drupal::typedData()->create($definition, $values, $field_name, $entity);
- foreach ($items as $item) {
- if ($item instanceof PrepareCacheInterface) {
- $item->prepareCache();
- }
- }
- $entity->{$field_name}[$langcode] = $items->getValue();
- }
- }
- }
-
- // Invoke hook_field_attach_load(): let other modules act on loading the
- // entity.
- Drupal::moduleHandler()->invokeAll('field_attach_load', $entity_type, $queried_entities, $age, $options);
-
- // Build cache data.
- if ($cache_write) {
- foreach ($queried_entities as $id => $entity) {
- $data = array();
- $instances = field_info_instances($entity_type, $entity->bundle());
- foreach ($instances as $instance) {
- $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
- }
- $cid = "field:$entity_type:$id";
- cache('field')->set($cid, $data);
- }
- }
- }
-}
-
-/**
- * Loads all fields for previous versions of a group of entities.
- *
- * Loading different versions of the same entities is not supported, and should
- * be done by separate calls to the function.
- *
- * field_attach_load_revision() is automatically called by the default entity
- * controller class, and thus, in most cases, doesn't need to be explicitly
- * called by the entity type module.
- *
- * @param $entity_type
- * The type of entities in $entities; e.g. 'node' or 'user'.
- * @param $entities
- * An array of entities for which to load fields, keyed by entity ID. Each
- * entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
- * filled. The function adds the loaded field data directly in the entity
- * objects of the $entities array.
- * @param $options
- * An associative array of additional options. See field_attach_load() for
- * details.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_load_revision($entity_type, $entities, $options = array()) {
- return field_attach_load($entity_type, $entities, FIELD_LOAD_REVISION, $options);
-}
-
-/**
* Performs field validation against form-submitted field values.
*
* There are two levels of validation for fields in forms: widget validation and
@@ -837,7 +634,7 @@ function field_attach_form_validate(EntityInterface $entity, $form, &$form_state
$has_violations = TRUE;
// Place violations in $form_state.
- $langcode = field_is_translatable($entity->entityType(), field_info_field($field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED;
+ $langcode = field_is_translatable($entity->entityType(), field_info_field($entity->entityType(), $field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED;
$field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
$field_state['constraint_violations'] = $field_violations;
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
@@ -889,165 +686,12 @@ function field_attach_extract_form_values(EntityInterface $entity, $form, &$form
}
/**
- * Save field data for a new entity.
- *
- * The passed-in entity must already contain its id and (if applicable)
- * revision id attributes.
- * Default values (if any) will be saved for fields not present in the
- * $entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity with fields to save.
- * @return
- * Default values (if any) will be added to the $entity parameter for fields
- * it leaves unspecified.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_insert(EntityInterface $entity) {
- // Ensure we are working with a BC mode entity.
- $entity = $entity->getBCEntity();
-
- // Let any module insert field data before the storage engine, accumulating
- // saved fields along the way.
- $skip_fields = array();
- foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_insert') as $module) {
- $function = $module . '_field_storage_pre_insert';
- $function($entity, $skip_fields);
- }
-
- // Collect the storage backends used by the remaining fields in the entities.
- $storages = array();
- foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
- $field = field_info_field_by_id($instance['field_id']);
- $field_id = $field['uuid'];
- $field_name = $field['field_name'];
- if (!empty($entity->$field_name)) {
- // Collect the storage backend if the field has not been written yet.
- if (!isset($skip_fields[$field_id])) {
- $storages[$field['storage']['type']][$field_id] = $field_id;
- }
- }
- }
-
- // Field storage backends save any remaining unsaved fields.
- foreach ($storages as $storage => $fields) {
- $storage_info = field_info_storage_types($storage);
- module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_INSERT, $fields);
- }
-}
-
-/**
- * Saves field data for an existing entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity with fields to save.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_update(EntityInterface $entity) {
- // Ensure we are working with a BC mode entity.
- $entity = $entity->getBCEntity();
-
- // Let any module update field data before the storage engine, accumulating
- // saved fields along the way.
- $skip_fields = array();
- foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_update') as $module) {
- $function = $module . '_field_storage_pre_update';
- $function($entity, $skip_fields);
- }
-
- // Collect the storage backends used by the remaining fields in the entities.
- $storages = array();
- foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
- $field = field_info_field_by_id($instance['field_id']);
- $field_id = $field['uuid'];
- // Collect the storage backend if the field has not been written yet.
- if (!isset($skip_fields[$field_id])) {
- $storages[$field['storage']['type']][$field_id] = $field_id;
- }
- }
-
- // Field storage backends save any remaining unsaved fields.
- foreach ($storages as $storage => $fields) {
- $storage_info = field_info_storage_types($storage);
- module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_UPDATE, $fields);
- }
-
- $entity_info = $entity->entityInfo();
- if ($entity_info['field_cache']) {
- cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
- }
-}
-
-/**
- * Deletes field data for an existing entity. This deletes all revisions of
- * field data for the entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity whose field data to delete.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_delete(EntityInterface $entity) {
- // Ensure we are working with a BC mode entity.
- $entity = $entity->getBCEntity();
-
- // Collect the storage backends used by the fields in the entities.
- $storages = array();
- foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
- $field = field_info_field_by_id($instance['field_id']);
- $field_id = $field['uuid'];
- $storages[$field['storage']['type']][$field_id] = $field_id;
- }
-
- // Field storage backends delete their data.
- foreach ($storages as $storage => $fields) {
- $storage_info = field_info_storage_types($storage);
- module_invoke($storage_info['module'], 'field_storage_delete', $entity, $fields);
- }
-
- $entity_info = $entity->entityInfo();
- if ($entity_info['field_cache']) {
- cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
- }
-}
-
-/**
- * Delete field data for a single revision of an existing entity. The passed
- * entity must have a revision ID attribute.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity with fields to save.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_delete_revision(EntityInterface $entity) {
- // Ensure we are working with a BC mode entity.
- $entity = $entity->getBCEntity();
-
- // Collect the storage backends used by the fields in the entities.
- $storages = array();
- foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
- $field = field_info_field_by_id($instance['field_id']);
- $field_id = $field['uuid'];
- $storages[$field['storage']['type']][$field_id] = $field_id;
- }
-
- // Field storage backends delete their data.
- foreach ($storages as $storage => $fields) {
- $storage_info = field_info_storage_types($storage);
- module_invoke($storage_info['module'], 'field_storage_delete_revision', $entity, $fields);
- }
-}
-
-/**
* Prepares field data prior to display.
*
* This function lets field types and formatters load additional data needed for
- * display that is not automatically loaded during field_attach_load(). It
- * accepts an array of entities to allow query optimization when displaying
- * lists of entities.
+ * display that is not automatically loaded during entity loading. It accepts an
+ * array of entities to allow query optimization when displaying lists of
+ * entities.
*
* field_attach_prepare_view() and field_attach_view() are two halves of the
* same operation. It is safe to call field_attach_prepare_view() multiple times
diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc
index f32fb95..9bb495c 100644
--- a/core/modules/field/field.info.inc
+++ b/core/modules/field/field.info.inc
@@ -41,77 +41,10 @@ function field_info_cache_clear() {
Drupal::typedData()->clearCachedDefinitions();
Drupal::service('plugin.manager.entity.field.field_type')->clearCachedDefinitions();
- _field_info_collate_types_reset();
Field::fieldInfo()->flush();
}
/**
- * Collates all information on field types, widget types and related structures.
- *
- * @return
- * An associative array containing:
- * - 'storage types': Array of hook_field_storage_info() results, keyed by
- * storage type names. Each element has the following components: label,
- * description, and settings from hook_field_storage_info(), as well as
- * module, giving the module that exposes the storage type.
- *
- * @see _field_info_collate_types_reset()
- */
-function _field_info_collate_types() {
- $language_interface = language(Language::TYPE_INTERFACE);
-
- // Use the advanced drupal_static() pattern, since this is called very often.
- static $drupal_static_fast;
-
- if (!isset($drupal_static_fast)) {
- $drupal_static_fast['field_info_collate_types'] = &drupal_static(__FUNCTION__);
- }
- $info = &$drupal_static_fast['field_info_collate_types'];
-
- // The _info() hooks invoked below include translated strings, so each
- // language is cached separately.
- $langcode = $language_interface->id;
-
- if (!isset($info)) {
- if ($cached = cache('field')->get("field_info_types:$langcode")) {
- $info = $cached->data;
- }
- else {
- $info = array(
- 'storage types' => array(),
- );
-
- // Populate storage types.
- foreach (Drupal::moduleHandler()->getImplementations('field_storage_info') as $module) {
- $storage_types = (array) module_invoke($module, 'field_storage_info');
- foreach ($storage_types as $name => $storage_info) {
- // Provide defaults.
- $storage_info += array(
- 'settings' => array(),
- );
- $info['storage types'][$name] = $storage_info;
- $info['storage types'][$name]['module'] = $module;
- }
- }
- drupal_alter('field_storage_info', $info['storage types']);
-
- cache('field')->set("field_info_types:$langcode", $info, CacheBackendInterface::CACHE_PERMANENT, array('field_info_types' => TRUE));
- }
- }
-
- return $info;
-}
-
-/**
- * Clears collated information on field and widget types and related structures.
- */
-function _field_info_collate_types_reset() {
- drupal_static_reset('_field_info_collate_types');
- // Clear all languages.
- cache('field')->deleteTags(array('field_info_types' => TRUE));
-}
-
-/**
* Determines the behavior of a widget with respect to an operation.
*
* @param string $op
@@ -135,31 +68,6 @@ function field_behaviors_widget($op, $instance) {
}
/**
- * Returns information about field storage from hook_field_storage_info().
- *
- * @param $storage_type
- * (optional) A storage type name. If omitted, all storage types will be
- * returned.
- *
- * @return
- * Either a storage type description, as provided by
- * hook_field_storage_info(), or an array of all existing storage types, keyed
- * by storage type name.
- */
-function field_info_storage_types($storage_type = NULL) {
- $info = _field_info_collate_types();
- $storage_types = $info['storage types'];
- if ($storage_type) {
- if (isset($storage_types[$storage_type])) {
- return $storage_types[$storage_type];
- }
- }
- else {
- return $storage_types;
- }
-}
-
-/**
* Returns all field definitions.
*
* Use of this function should be avoided when possible, since it loads and
@@ -171,27 +79,17 @@ function field_info_storage_types($storage_type = NULL) {
* field_info_field() on each individual field instead.
*
* @return
- * An array of field definitions, keyed by field name. Each field has an
- * additional property, 'bundles', which is an array of all the bundles to
- * which this field belongs, keyed by entity type.
+ * An array of field definitions, keyed by field UUID.
*
* @see field_info_field_map()
*/
function field_info_fields() {
- $info = Field::fieldInfo()->getFields();
-
- $fields = array();
- foreach ($info as $key => $field) {
- if (!$field['deleted']) {
- $fields[$field['field_name']] = $field;
- }
- }
-
- return $fields;
+ // Filter out deleted fields.
+ return array_filter(Field::fieldInfo()->getFields(), function ($field) {
+ return !$field->deleted;
+ });
}
-
-
/**
* Returns a list and settings of pseudo-field elements in a given bundle.
*
@@ -252,21 +150,5 @@ function field_info_extra_fields($entity_type, $bundle, $context) {
}
/**
- * Returns a field storage type's default settings.
- *
- * @param $type
- * A field storage type name.
- *
- * @return
- * The storage type's default settings, as provided by
- * hook_field_storage_info(), or an empty array if type or settings are
- * undefined.
- */
-function field_info_storage_settings($type) {
- $info = field_info_storage_types($type);
- return isset($info['settings']) ? $info['settings'] : array();
-}
-
-/**
* @} End of "defgroup field_info".
*/
diff --git a/core/modules/field/field.install b/core/modules/field/field.install
index d692373..f0ea11b 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -6,6 +6,7 @@
*/
use Drupal\Component\Uuid\Uuid;
+use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\field\Entity\Field;
/**
@@ -29,8 +30,8 @@ function _update_8003_field_create_field(array &$field_config) {
// Merge in default values.
$field_config += array(
+ 'id' => $field_config['entity_type'] . '.' . $field_config['name'],
'uuid' => $uuid->generate(),
- 'entity_types' => array(),
'cardinality' => 1,
'translatable' => FALSE,
'locked' => FALSE,
@@ -41,14 +42,6 @@ function _update_8003_field_create_field(array &$field_config) {
'langcode' => 'und',
);
- // Set the storage.
- $field_config['storage'] = array(
- 'type' => 'field_sql_storage',
- 'module' => 'field_sql_storage',
- 'active' => TRUE,
- 'settings' => array(),
- );
-
// Save in config.
Drupal::config('field.field.' . $field_config['id'])
->setData($field_config)
@@ -56,15 +49,17 @@ function _update_8003_field_create_field(array &$field_config) {
// Create storage for the field. This requires a field entity, but cannot use
// the regular entity_create() function here.
- $field_entity = new Field($field_config);
- field_sql_storage_field_storage_create_field($field_entity);
+ $schema = DatabaseStorageController::_fieldSqlSchema(new Field($field_config), $field_config['schema']);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
}
/**
* Writes a field instance directly to configuration.
*
* Upgrades using this function need to use hook_update_dependencies() to ensure
- * they get executed after field_update_8003().
+ * they get executed after field_update_8003().
*
* @param array $field_config
* An array of field properties.
@@ -78,6 +73,7 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc
// Merge in defaults.
$instance_config += array(
+ 'id' => $instance_config['entity_type'] . '.' . $instance_config['bundle'] . '.' . $field_config['name'],
'description' => '',
'required' => FALSE,
'uuid' => $uuid->generate(),
@@ -98,6 +94,73 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc
}
/**
+ * Writes field data directly to SQL storage.
+ *
+ * @param string $entity_type
+ * The entity type.
+ * @param string $bundle
+ * The bundle.
+ * @param int $entity_id
+ * The entity ID.
+ * @param int $revision_id
+ * The entity revision ID.
+ * @param string $field_name
+ * The field name.
+ * @param array $data
+ * The field values, as an array keyed by langcode, delta and property name.
+ *
+ * @ingroup update_api
+ */
+function _update_8006_field_write_data_sql($entity_type, $bundle, $entity_id, $revision_id, $field_name, array $data) {
+ $field_config = Drupal::config("field.field.$entity_type.$field_name");
+ $field = new Field($field_config);
+
+ $table_name = DatabaseStorageController::_fieldTableName($field);
+ $revision_name = DatabaseStorageController::_fieldRevisionTableName($field);
+
+ db_delete($table_name)
+ ->condition('entity_id', $entity_id)
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_id', $entity_id)
+ ->condition('revision_id', $revision_id)
+ ->execute();
+
+ $columns = array();
+ foreach ($data as $langcode => $items) {
+ foreach ($items as $delta => $item) {
+ $record = array(
+ 'entity_id' => $entity_id,
+ 'revision_id' => $revision_id,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'langcode' => $langcode,
+ );
+ foreach ($item as $column => $value) {
+ $record[DatabaseStorageController::_fieldColumnName($field_name, $column)] = $value;
+ }
+
+ $records[] = $record;
+ // Record the columns used.
+ $columns += $record;
+ }
+ }
+
+ if ($columns) {
+ $query = db_insert($table_name)->fields(array_keys($columns));
+ $revision_query = db_insert($revision_name)->fields(array_keys($columns));
+ foreach ($records as $record) {
+ $query->values($record);
+ if ($revision_id) {
+ $revision_query->values($record);
+ }
+ }
+ $query->execute();
+ $revision_query->execute();
+ }
+}
+
+/**
* @addtogroup updates-7.x-to-8.x
* @{
*/
@@ -255,27 +318,22 @@ function field_update_8003() {
$field_data = array();
// Migrate field definitions.
- $records = db_query("SELECT * FROM {field_config}")->fetchAll(PDO::FETCH_ASSOC);
+ $records = db_query("SELECT DISTINCT entity_type, fc.* FROM {field_config} fc INNER JOIN {field_config_instance} fci ON fc.id = fci.field_id")->fetchAll(PDO::FETCH_ASSOC);
foreach ($records as $record) {
$record['data'] = unserialize($record['data']);
$config = array(
- 'id' => $record['field_name'],
+ 'id' => $record['entity_type'] . '.' . $record['field_name'],
+ 'name' => $record['field_name'],
'uuid' => $uuid->generate(),
'type' => $record['type'],
'module' => $record['module'],
'active' => $record['active'],
+ 'entity_type' => $record['entity_type'],
'settings' => $record['data']['settings'],
- 'storage' => array(
- 'type' => $record['storage_type'],
- 'module' => $record['storage_module'],
- 'active' => $record['storage_active'],
- 'settings' => $record['data']['storage']['settings'],
- ),
'locked' => $record['locked'],
'cardinality' => $record['cardinality'],
'translatable' => $record['translatable'],
- 'entity_types' => $record['data']['entity_types'],
'indexes' => $record['data']['indexes'] ?: array(),
'status' => 1,
'langcode' => 'und',
@@ -295,19 +353,17 @@ function field_update_8003() {
else {
$config['deleted'] = TRUE;
$deleted_fields[$config['uuid']] = $config;
- // Additionally, rename the data tables for deleted fields. Technically
- // this would belong in an update in field_sql_storage.module, but it is
- // easier to do it now, when the old numeric ID is available.
- if ($config['storage']['type'] == 'field_sql_storage') {
- $field = new Field($config);
- $tables = array(
- "field_deleted_data_{$record['id']}" => _field_sql_storage_tablename($field),
- "field_deleted_revision_{$record['id']}" => _field_sql_storage_revision_tablename($field),
- );
- foreach ($tables as $table_old => $table_new) {
- if (db_table_exists($table_old)) {
- db_rename_table($table_old, $table_new);
- }
+ // This will not be saved but the DatabaseStorageController helpers need
+ // the field object.
+ $field = new Field($config);
+ // Additionally, rename the data tables for deleted fields.
+ $tables = array(
+ "field_deleted_data_{$record['id']}" => 'old_' . DatabaseStorageController::_fieldTableName($field),
+ "field_deleted_revision_{$record['id']}" => 'old_' . DatabaseStorageController::_fieldRevisionTableName($field),
+ );
+ foreach ($tables as $table_old => $table_new) {
+ if (db_table_exists($table_old)) {
+ db_rename_table($table_old, $table_new);
}
}
}
@@ -371,14 +427,13 @@ function field_update_8003() {
}
/**
- * Moves field_storage_default and field_language_fallback to config.
+ * Moves field_language_fallback to config.
*
* @ingroup config_upgrade
*/
function field_update_8004() {
update_variable_set('field_language_fallback', TRUE);
update_variables_to_config('field.settings', array(
- 'field_storage_default' => 'default_storage',
'field_language_fallback' => 'language_fallback',
));
}
@@ -416,6 +471,106 @@ function field_update_8005() {
->save();
}
+
+/**
+ * Splits the field storage tables by entity type and also migrate langcode.
+ */
+function field_update_8006(&$sandbox) {
+ // Get field definitions from config, and deleted fields from state system.
+ $config_names = config_get_storage_names_with_prefix('field.field');
+ $deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
+ // Ditch UUID keys, we will iterate through deleted fields using a numeric
+ // index.
+ $deleted_fields = array_values($deleted_fields);
+
+ if (empty($config_names) && empty($deleted_fields)) {
+ return;
+ }
+
+ if (!isset($sandbox['index'])) {
+ $sandbox['index'] = 0;
+ $sandbox['max'] = count($config_names) + count($deleted_fields);
+ }
+
+ // Retrieve the next field definition. When the index exceeds the number of
+ // 'configuration' fields, use it to iterate on deleted fields.
+ if (isset($config_names[$sandbox['index']])) {
+ $field_config = Drupal::config($config_names[$sandbox['index']])->get();
+ }
+ else {
+ $field_config = $deleted_fields[$sandbox['index'] - count($config_names)];
+ }
+
+ // Prepare updated schema data structures.
+ $field = new Field($field_config);
+ $tables = array(
+ array(
+ 'old_table' => 'field_data_' . $field_config['name'],
+ 'new_table' => DatabaseStorageController::_fieldTableName($field),
+ 'primary_key' => array(
+ 'entity_id',
+ 'deleted',
+ 'delta',
+ 'langcode',
+ ),
+ ),
+ array(
+ 'old_table' => 'field_revision_' . $field_config['name'],
+ 'new_table' => DatabaseStorageController::_fieldRevisionTableName($field),
+ 'primary_key' => array(
+ 'entity_id',
+ 'revision_id',
+ 'deleted',
+ 'delta',
+ 'langcode',
+ ),
+ ),
+ );
+
+ // Move the field data to the new table.
+ foreach ($tables as $table_data) {
+ // Split data from the old "per field" table to the new "per entity type and
+ // field" table.
+ $new_table = $table_data['new_table'];
+ $original_table = empty($field_config['deleted']) ? $table_data['old_table'] : 'old_' . $new_table;
+ if (db_table_exists($original_table)) {
+ // Create the new table, with the same schema as the old one.
+ if (!db_table_exists($new_table)) {
+ db_copy_table_schema($original_table, $new_table);
+ }
+ // Copy relevant rows.
+ $from_query = db_select($original_table, 'original')
+ ->fields('original')
+ ->condition('entity_type', $field_config['entity_type']);
+ db_insert($new_table)
+ ->from($from_query)
+ ->execute();
+ // Drop the 'entity_type' column and previous primary key.
+ db_drop_primary_key($new_table);
+ db_drop_field($new_table, 'entity_type');
+ // Rename 'language' to 'langcode'. Tables created during the upgrade
+ // before this update function might already have the langcode column.
+ if (db_field_exists($new_table, 'language')) {
+ db_drop_index($new_table, 'language');
+ db_change_field($new_table, 'language', 'langcode', array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ ));
+ db_add_index($new_table, 'langcode', array('langcode'));
+ }
+ // Create new primary key.
+ db_add_primary_key($new_table, $table_data['primary_key']);
+ }
+ }
+
+ $sandbox['index']++;
+ $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']);
+}
+
+
+
/**
* @} End of "addtogroup updates-7.x-to-8.x".
* The next series of updates should start at 9000.
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index c355e67..1a673bc 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -46,9 +46,7 @@
* type 'image'. The administrator (again, via a UI) creates two Field
* Instances, one attaching the field 'subtitle' to the 'node' bundle 'article'
* and one attaching the field 'photo' to the 'node' bundle 'article'. When the
- * node system uses the Field Attach API to load all fields for an Article node,
- * it passes the node's entity type (which is 'node') and content type (which is
- * 'article') as the node's bundle. field_attach_load() then loads the
+ * node storage controller loads an Article node, it loads the values of the
* 'subtitle' and 'photo' fields because they are both attached to the 'node'
* bundle 'article'.
*
@@ -272,7 +270,7 @@ function field_entity_field_info($entity_type) {
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->id())),
+ 'label' => t('Field !name', array('!name' => $field->name)),
'type' => 'field_item:' . $field->type,
'list' => TRUE,
'configurable' => TRUE,
@@ -399,24 +397,8 @@ function field_sync_field_status() {
// modules.
$changed = array();
$modules = $module_handler->getModuleList();
- foreach ($modules as $module => $module_info) {
- // Collect storage backends exposed by the module.
- $storage_types = (array) $module_handler->invoke($module, 'field_storage_info');
-
- if ($storage_types) {
- foreach ($fields as $uuid => &$field) {
- // Associate storage backends.
- if (isset($storage_types[$field['storage']['type']]) && ($field['storage']['module'] !== $module || !$field['storage']['active'])) {
- $field['storage']['module'] = $module;
- $field['storage']['active'] = TRUE;
- $changed[$uuid] = $field;
- }
- }
- }
- }
-
$field_types = Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
- // Set fields with missing field type or storage modules to inactive.
+ // Set fields with missing field type modules to inactive.
foreach ($fields as $uuid => &$field) {
// Associate field types.
if (isset($field_types[$field['type']]) && ($field['module'] != $field_types[$field['type']]['provider'] || !$field['active'])) {
@@ -428,11 +410,6 @@ function field_sync_field_status() {
$field['active'] = FALSE;
$changed[$uuid] = $field;
}
- // Disassociate storage backends.
- if (!isset($modules[$field['storage']['module']]) && $field['storage']['active']) {
- $field['storage']['active'] = FALSE;
- $changed[$uuid] = $field;
- }
}
// Store the updated field definitions.
@@ -444,8 +421,6 @@ function field_sync_field_status() {
Drupal::config('field.field.' . $field['id'])
->set('module', $field['module'])
->set('active', $field['active'])
- ->set('storage.module', $field['storage']['module'])
- ->set('storage.active', $field['storage']['active'])
->save();
}
}
@@ -641,7 +616,7 @@ function field_view_value(EntityInterface $entity, $field_name, $item, $display
// Ensure we are working with a BC mode entity.
$entity = $entity->getBCEntity();
- if ($field = field_info_field($field_name)) {
+ if ($field = field_info_field($entity->entityType(), $field_name)) {
// Determine the langcode that will be used by language fallback.
$langcode = field_language($entity, $field_name, $langcode);
@@ -731,7 +706,7 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options
$view_mode = '_custom';
// hook_field_attach_display_alter() needs to receive the 'prepared'
// $display_options, so we cannot let preparation happen internally.
- $field = field_info_field($field_name);
+ $field = field_info_field($entity_type, $field_name);
$formatter_manager = drupal_container()->get('plugin.manager.field.formatter');
$display_options = $formatter_manager->prepareConfiguration($field['type'], $display_options);
$formatter = $formatter_manager->getInstance(array(
@@ -876,10 +851,12 @@ function template_preprocess_field(&$variables, $hook) {
// Add default CSS classes. Since there can be many fields rendered on a page,
// save some overhead by calling strtr() directly instead of
// drupal_html_class().
+ $variables['entity_type_css'] = strtr($element['#entity_type'], '_', '-');
$variables['field_name_css'] = strtr($element['#field_name'], '_', '-');
$variables['field_type_css'] = strtr($element['#field_type'], '_', '-');
$variables['attributes']['class'] = array(
'field',
+ 'field-' . $variables['entity_type_css'] . '--' . $variables['field_name_css'],
'field-name-' . $variables['field_name_css'],
'field-type-' . $variables['field_type_css'],
'field-label-' . $element['#label_display'],
@@ -894,8 +871,9 @@ function template_preprocess_field(&$variables, $hook) {
$variables['theme_hook_suggestions'] = array(
'field__' . $element['#field_type'],
'field__' . $element['#field_name'],
- 'field__' . $element['#bundle'],
- 'field__' . $element['#field_name'] . '__' . $element['#bundle'],
+ 'field__' . $element['#entity_type'] . '__' . $element['#bundle'],
+ 'field__' . $element['#entity_type'] . '__' . $element['#field_name'],
+ 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'],
);
static $default_attributes;
diff --git a/core/modules/field/field.purge.inc b/core/modules/field/field.purge.inc
index e280187..7333f97 100644
--- a/core/modules/field/field.purge.inc
+++ b/core/modules/field/field.purge.inc
@@ -5,7 +5,6 @@
* Field CRUD API, handling field and field instance creation and deletion.
*/
-use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Entity\Field;
use Drupal\field\FieldException;
@@ -111,7 +110,7 @@ function field_purge_batch($batch_size) {
'bundle' => $instance['bundle'],
);
// field_purge_data() will need the field array.
- $field = field_info_field_by_id($instance['field_id']);
+ $field = $instance->getField();
// Retrieve some entities.
$query = $factory->get($entity_type)
->condition('id:' . $field['uuid'] . '.deleted', 1)
@@ -121,21 +120,12 @@ function field_purge_batch($batch_size) {
$query->condition($info[$entity_type]['entity_keys']['bundle'], $ids->bundle);
}
$results = $query->execute();
-
if ($results) {
- $entities = array();
foreach ($results as $revision_id => $entity_id) {
- // This might not be the revision id if the entity type does not support
- // revisions but _field_create_entity_from_ids() checks that and
- // disregards this value so there is no harm setting it.
$ids->revision_id = $revision_id;
$ids->entity_id = $entity_id;
- $entities[$entity_id] = _field_create_entity_from_ids($ids);
- }
- field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
- foreach ($entities as $entity) {
- // Purge the data for the entity.
- field_purge_data($entity, $field, $instance);
+ $entity = _field_create_entity_from_ids($ids);
+ Drupal::entityManager()->getStorageController($entity_type)->onFieldItemsPurge($entity, $instance);
}
}
else {
@@ -156,37 +146,6 @@ function field_purge_batch($batch_size) {
}
/**
- * Purges the field data for a single field on a single pseudo-entity.
- *
- * This is basically the same as field_attach_delete() except it only applies to
- * a single field. The entity itself is not being deleted, and it is quite
- * possible that other field data will remain attached to it.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The pseudo-entity whose field data is being purged.
- * @param $field
- * The (possibly deleted) field whose data is being purged.
- * @param $instance
- * The deleted field instance whose data is being purged.
- */
-function field_purge_data(EntityInterface $entity, $field, $instance) {
- foreach ($entity->{$field->id()} as $value) {
- $definition = _field_generate_entity_field_definition($field, $instance);
- $items = \Drupal::typedData()->create($definition, $value, $field->id(), $entity);
- $items->delete();
- }
-
- // Tell the field storage system to purge the data.
- module_invoke($field['storage']['module'], 'field_storage_purge', $entity, $field, $instance);
-
- // Let other modules act on purging the data.
- foreach (Drupal::moduleHandler()->getImplementations('field_attach_purge') as $module) {
- $function = $module . '_field_attach_purge';
- $function($entity, $field, $instance);
- }
-}
-
-/**
* Purges a field instance record from the database.
*
* This function assumes all data for the instance has already been purged and
@@ -196,10 +155,6 @@ function field_purge_data(EntityInterface $entity, $field, $instance) {
* The instance record to purge.
*/
function field_purge_instance($instance) {
- // Notify the storage engine.
- $field = field_info_field_by_id($instance['field_id']);
- module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance);
-
$state = Drupal::state();
$deleted_instances = $state->get('field.instance.deleted');
unset($deleted_instances[$instance['uuid']]);
@@ -232,8 +187,8 @@ function field_purge_field($field) {
unset($deleted_fields[$field['uuid']]);
$state->set('field.field.deleted', $deleted_fields);
- // Notify the storage engine.
- module_invoke($field['storage']['module'], 'field_storage_purge_field', $field);
+ // Notify the storage layer.
+ Drupal::entityManager()->getStorageController($field->entity_type)->onFieldPurge($field);
// Clear the cache.
field_info_cache_clear();
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index e5dea3b..7e24b44 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -8,30 +8,30 @@
*/
use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\FieldInterface;
/**
* Implements hook_views_data().
*
- * Field modules can implement hook_field_views_data() to override
- * the default behavior for adding fields.
+ * Field modules can implement hook_field_views_data() to override the default
+ * behavior for adding fields.
*/
function field_views_data() {
$data = array();
- foreach (field_info_fields() as $field) {
- if ($field['storage']['type'] != 'field_sql_storage') {
- continue;
- }
+ $module_handler = Drupal::moduleHandler();
- $module = $field['module'];
- $result = (array) module_invoke($module, 'field_views_data', $field);
-
- if (empty($result)) {
- $result = field_views_field_default_views_data($field);
- }
- Drupal::moduleHandler()->alter('field_views_data', $result, $field, $module);
+ foreach (field_info_fields() as $field) {
+ if (_field_views_is_sql_entity_type($field)) {
+ $result = (array) $module_handler->invoke($field['module'], 'field_views_data', array($field));
+ if (empty($result)) {
+ $result = field_views_field_default_views_data($field);
+ }
+ $module_handler->alter('field_views_data', $result, $field);
- if (is_array($result)) {
- $data = NestedArray::mergeDeep($result, $data);
+ if (is_array($result)) {
+ $data = NestedArray::mergeDeep($result, $data);
+ }
}
}
@@ -48,15 +48,34 @@ function field_views_data() {
*/
function field_views_data_alter(&$data) {
foreach (field_info_fields() as $field) {
- if ($field['storage']['type'] != 'field_sql_storage') {
- continue;
+ if (_field_views_is_sql_entity_type($field)) {
+ $function = $field['module'] . '_field_views_data_views_data_alter';
+ if (function_exists($function)) {
+ $function($data, $field);
+ }
}
+ }
+}
- $function = $field['module'] . '_field_views_data_views_data_alter';
- if (function_exists($function)) {
- $function($data, $field);
+/**
+ * Determines whether the entity type the field appears in is SQL based.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field definition.
+ *
+ * @return bool
+ * True if the entity type uses DatabaseStorageController.
+ */
+function _field_views_is_sql_entity_type(FieldInterface $field) {
+ $entity_manager = Drupal::entityManager();
+ try {
+ if ($entity_manager->getStorageController($field->entity_type) instanceof DatabaseStorageController) {
+ return TRUE;
}
}
+ catch (\InvalidArgumentException $e) {
+ // Disabled entity type, nothing to do.
+ }
}
/**
@@ -64,17 +83,16 @@ function field_views_data_alter(&$data) {
*
* Therefore it looks up in all bundles to find the most used instance.
*/
-function field_views_field_label($field_name) {
+function field_views_field_label($entity_type, $field_name) {
$label_counter = array();
$all_labels = array();
// Count the amount of instances per label per field.
- $instances = field_info_instances();
- foreach ($instances as $entity_name => $entity_type) {
- foreach ($entity_type as $bundle) {
- if (isset($bundle[$field_name])) {
- $label_counter[$bundle[$field_name]['label']] = isset($label_counter[$bundle[$field_name]['label']]) ? ++$label_counter[$bundle[$field_name]->label] : 1;
- $all_labels[$entity_name][$bundle[$field_name]['label']] = TRUE;
- }
+ $instances = field_info_instances($entity_type);
+ foreach ($instances as $bundle => $bundle_instances) {
+ if (isset($bundle_instances[$field_name])) {
+ $instance = $bundle_instances[$field_name];
+ $label_counter[$instance->label] = isset($label_counter[$instance->label]) ? ++$label_counter[$instance->label] : 1;
+ $all_labels[$instance->label] = TRUE;
}
}
if (empty($label_counter)) {
@@ -88,111 +106,105 @@ function field_views_field_label($field_name) {
/**
* Default views data implementation for a field.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ * The field definition.
+ *
+ * @return array
+ * The default views data for the field.
*/
-function field_views_field_default_views_data($field) {
- $field_types = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
+function field_views_field_default_views_data(FieldInterface $field) {
+ $data = array();
- // Check the field module is available.
- if (!isset($field_types[$field['type']])) {
- return;
+ // Check the field type is available.
+ if (!\Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field['type'])) {
+ return $data;
+ }
+ // Check the field has instances.
+ if (empty($field['bundles'])) {
+ return $data;
}
- $data = array();
-
- $current_table = _field_sql_storage_tablename($field);
- $revision_table = _field_sql_storage_revision_tablename($field);
+ $field_name = $field['field_name'];
- // The list of entity:bundle that this field is used in.
- $bundles_names = array();
- $supports_revisions = FALSE;
- $entity_tables = array();
- $current_tables = array();
- $revision_tables = array();
- $groups = array();
+ // Grab information about the entity type tables.
+ $entity_manager = Drupal::entityManager();
+ $entity_type = $field->entity_type;
+ $entity_info = $entity_manager->getDefinition($entity_type);
+ if (!isset($entity_info['base_table'])) {
+ return $data;
+ }
+ $entity_table = $entity_info['base_table'];
+ $entity_tables = array($entity_table => $entity_type);
+ $supports_revisions = !empty($entity_info['entity_keys']['revision']) && !empty($entity_info['revision_table']);
+ if ($supports_revisions) {
+ $entity_revision_table = $entity_info['revision_table'];
+ $entity_tables[$entity_revision_table] = $entity_type;
+ }
- $group_name = count($field['bundles']) > 1 ? t('Field') : NULL;
+ // Description of the field tables.
+ $field_tables = array(
+ FIELD_LOAD_CURRENT => array(
+ 'table' => DatabaseStorageController::_fieldTableName($field),
+ 'alias' => "{$entity_type}__{$field->name}",
+ ),
+ );
+ if ($supports_revisions) {
+ $field_tables[FIELD_LOAD_REVISION] = array(
+ 'table' => DatabaseStorageController::_fieldRevisionTableName($field),
+ 'alias' => "{$entity_type}_revision__{$field->name}",
+ );
+ }
// Build the relationships between the field table and the entity tables.
- foreach ($field['bundles'] as $entity => $bundles) {
- $entity_info = entity_get_info($entity);
- $groups[$entity] = $entity_info['label'];
-
- // Override Node to Content.
- if ($groups[$entity] == t('Node')) {
- $groups[$entity] = t('Content');
- }
-
- // If only one bundle use this as the default name.
- if (empty($group_name)) {
- $group_name = $groups[$entity];
- }
-
- if (!isset($entity_info['base_table'])) {
- continue;
- }
- $entity_tables[$entity_info['base_table']] = $entity;
- $current_tables[$entity] = $entity_info['base_table'];
- if (isset($entity_info['revision_table'])) {
- $entity_tables[$entity_info['revision_table']] = $entity;
- $revision_tables[$entity] = $entity_info['revision_table'];
- }
-
- $data[$current_table]['table']['join'][$entity_info['base_table']] = array(
- 'left_field' => $entity_info['entity_keys']['id'],
- 'field' => 'entity_id',
+ $table_alias = $field_tables[FIELD_LOAD_CURRENT]['alias'];
+ $data[$table_alias]['table']['join'][$entity_table] = array(
+ 'left_field' => $entity_info['entity_keys']['id'],
+ 'field' => 'entity_id',
+ 'extra' => array(
+ array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
+ ),
+ );
+ if ($supports_revisions) {
+ $table_alias = $field_tables[FIELD_LOAD_REVISION]['alias'];
+ $data[$table_alias]['table']['join'][$entity_revision_table] = array(
+ 'left_field' => $entity_info['entity_keys']['revision'],
+ 'field' => 'revision_id',
'extra' => array(
- array('field' => 'entity_type', 'value' => $entity),
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
),
);
-
- if (!empty($entity_info['entity_keys']['revision']) && !empty($entity_info['revision_table'])) {
- $data[$revision_table]['table']['join'][$entity_info['revision_table']] = array(
- 'left_field' => $entity_info['entity_keys']['revision'],
- 'field' => 'revision_id',
- 'extra' => array(
- array('field' => 'entity_type', 'value' => $entity),
- array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
- ),
- );
-
- $supports_revisions = TRUE;
- }
-
- foreach ($bundles as $bundle) {
- $bundles_names[] = t('@entity:@bundle', array('@entity' => $entity, '@bundle' => $bundle));
- }
- }
-
- $tables = array();
- $tables[FIELD_LOAD_CURRENT] = $current_table;
- if ($supports_revisions) {
- $tables[FIELD_LOAD_REVISION] = $revision_table;
}
+ // Override Node to Content.
+ $group_name = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label'];
+ // Get the list of bundles the field appears in.
+ $bundles_names = $field->getBundles();
+ // Build the list of additional fields to add to queries.
$add_fields = array('delta', 'langcode', 'bundle');
- foreach ($field['columns'] as $column_name => $attributes) {
- $add_fields[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+ foreach (array_keys($field['columns']) as $column) {
+ $add_fields[] = DatabaseStorageController::_fieldColumnName($field, $column);
}
+ // Determine the label to use for the field. We don't have a label available
+ // at the field level, so we just go through all instances and take the one
+ // which is used the most frequently.
+ list($label, $all_labels) = field_views_field_label($entity_type, $field_name);
+
+ // Expose data for the field as a whole.
+ foreach ($field_tables as $type => $table_info) {
+ $table = $table_info['table'];
+ $table_alias = $table_info['alias'];
- // Note: we don't have a label available here, because we are at the field
- // level, not at the instance level. So we just go through all instances
- // and take the one which is used the most frequently.
- $field_name = $field['field_name'];
- list($label, $all_labels) = field_views_field_label($field_name);
- foreach ($tables as $type => $table) {
if ($type == FIELD_LOAD_CURRENT) {
$group = $group_name;
- $old_column = 'entity_id';
- $column = $field['field_name'];
+ $field_alias = $field_name;
}
else {
$group = t('@group (historical data)', array('@group' => $group_name));
- $old_column = 'revision_id';
- $column = $field['field_name'] . '-' . $old_column;
+ $field_alias = $field_name . '-revision_id';
}
- $data[$table][$column] = array(
+ $data[$table_alias][$field_alias] = array(
'group' => $group,
'title' => $label,
'title short' => $label,
@@ -203,48 +215,42 @@ function field_views_field_default_views_data($field) {
// entity type + name.
$aliases = array();
$also_known = array();
- foreach ($all_labels as $entity_name => $labels) {
- if (!isset($current_tables[$entity_name])) {
- continue;
- }
- foreach ($labels as $label_name => $true) {
- if ($type == FIELD_LOAD_CURRENT) {
- if ($group_name != $groups[$entity_name] || $label != $label_name) {
- $aliases[] = array(
- 'base' => $current_tables[$entity_name],
- 'group' => $groups[$entity_name],
- 'title' => $label_name,
- 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
- );
- }
- $also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $label_name));
- }
- else {
- if ($group_name != $groups[$entity_name] && $label != $label_name && isset($revision_tables[$entity_name])) {
- $aliases[] = array(
- 'base' => $revision_tables[$entity_name],
- 'group' => t('@group (historical data)', array('@group' => $groups[$entity_name])),
- 'title' => $label_name,
- 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
- );
- }
- $also_known[] = t('@group (historical data): @field', array('@group' => $groups[$entity_name], '@field' => $label_name));
+ foreach ($all_labels as $label_name => $true) {
+ if ($type == FIELD_LOAD_CURRENT) {
+ if ($label != $label_name) {
+ $aliases[] = array(
+ 'base' => $entity_table,
+ 'group' => $group_name,
+ 'title' => $label_name,
+ 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
+ );
+ $also_known[] = t('@group: @field', array('@group' => $group_name, '@field' => $label_name));
}
}
+ elseif ($supports_revisions && $label != $label_name) {
+ $aliases[] = array(
+ 'base' => $table,
+ 'group' => t('@group (historical data)', array('@group' => $group_name)),
+ 'title' => $label_name,
+ 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
+ );
+ $also_known[] = t('@group (historical data): @field', array('@group' => $group_name, '@field' => $label_name));
+ }
}
if ($aliases) {
- $data[$table][$column]['aliases'] = $aliases;
- $data[$table][$column]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
+ $data[$table_alias][$field_alias]['aliases'] = $aliases;
+ $data[$table_alias][$field_alias]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
}
$keys = array_keys($field['columns']);
$real_field = reset($keys);
- $data[$table][$column]['field'] = array(
+ $data[$table_alias][$field_alias]['field'] = array(
'table' => $table,
'id' => 'field',
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
// Provide a real field for group by.
- 'real field' => $column . '_' . $real_field,
+ 'real field' => $field_alias . '_' . $real_field,
'additional fields' => $add_fields,
'entity_tables' => $entity_tables,
// Default the element type to div, let the UI change it if necessary.
@@ -253,6 +259,7 @@ function field_views_field_default_views_data($field) {
);
}
+ // Expose data for each field property individually.
foreach ($field['columns'] as $column => $attributes) {
$allow_sort = TRUE;
@@ -281,27 +288,32 @@ function field_views_field_default_views_data($field) {
}
if (count($field['columns']) == 1 || $column == 'value') {
- $title = t('@label (!name)', array('@label' => $label, '!name' => $field['field_name']));
+ $title = t('@label (!name)', array('@label' => $label, '!name' => $field_name));
$title_short = $label;
}
else {
- $title = t('@label (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $column));
+ $title = t('@label (!name:!column)', array('@label' => $label, '!name' => $field_name, '!column' => $column));
$title_short = t('@label:!column', array('@label' => $label, '!column' => $column));
}
- foreach ($tables as $type => $table) {
+ // Expose data for the property.
+ foreach ($field_tables as $type => $table_info) {
+ $table = $table_info['table'];
+ $table_alias = $table_info['alias'];
+
if ($type == FIELD_LOAD_CURRENT) {
$group = $group_name;
}
else {
$group = t('@group (historical data)', array('@group' => $group_name));
}
- $column_real_name = $field['storage_details']['sql'][$type][$table][$column];
+ $column_real_name = DatabaseStorageController::_fieldColumnName($field, $column);
// Load all the fields from the table by default.
- $additional_fields = array_values($field['storage_details']['sql'][$type][$table]);
+ $field_sql_schema = DatabaseStorageController::_fieldSqlSchema($field);
+ $additional_fields = array_keys($field_sql_schema[$table]['fields']);
- $data[$table][$column_real_name] = array(
+ $data[$table_alias][$column_real_name] = array(
'group' => $group,
'title' => $title,
'title short' => $title_short,
@@ -312,130 +324,137 @@ function field_views_field_default_views_data($field) {
// entity type + name.
$aliases = array();
$also_known = array();
- foreach ($all_labels as $entity_name => $labels) {
- foreach ($labels as $label_name => $true) {
- if ($group_name != $groups[$entity_name] || $label != $label_name) {
- if (count($field['columns']) == 1 || $column == 'value') {
- $alias_title = t('@label (!name)', array('@label' => $label_name, '!name' => $field['field_name']));
- }
- else {
- $alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field['field_name'], '!column' => $column));
- }
- $aliases[] = array(
- 'group' => $groups[$entity_name],
- 'title' => $alias_title,
- 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $title)),
- );
+ foreach ($all_labels as $label_name => $true) {
+ if ($label != $label_name) {
+ if (count($field['columns']) == 1 || $column == 'value') {
+ $alias_title = t('@label (!name)', array('@label' => $label_name, '!name' => $field_name));
+ }
+ else {
+ $alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field_name, '!column' => $column));
}
- $also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $title));
+ $aliases[] = array(
+ 'group' => $group_name,
+ 'title' => $alias_title,
+ 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $title)),
+ );
+ $also_known[] = t('@group: @field', array('@group' => $group_name, '@field' => $title));
}
}
if ($aliases) {
- $data[$table][$column_real_name]['aliases'] = $aliases;
- $data[$table][$column_real_name]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
+ $data[$table_alias][$column_real_name]['aliases'] = $aliases;
+ $data[$table_alias][$column_real_name]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
}
- $data[$table][$column_real_name]['argument'] = array(
+ $data[$table_alias][$column_real_name]['argument'] = array(
'field' => $column_real_name,
'table' => $table,
'id' => $argument,
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
'empty field name' => t('- No value -'),
);
- $data[$table][$column_real_name]['filter'] = array(
+ $data[$table_alias][$column_real_name]['filter'] = array(
'field' => $column_real_name,
'table' => $table,
'id' => $filter,
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
'allow empty' => TRUE,
);
if (!empty($allow_sort)) {
- $data[$table][$column_real_name]['sort'] = array(
+ $data[$table_alias][$column_real_name]['sort'] = array(
'field' => $column_real_name,
'table' => $table,
'id' => $sort,
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
);
}
// Expose additional delta column for multiple value fields.
if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
- $title_delta = t('@label (!name:delta)', array('@label' => $label, '!name' => $field['field_name']));
+ $title_delta = t('@label (!name:delta)', array('@label' => $label, '!name' => $field_name));
$title_short_delta = t('@label:delta', array('@label' => $label));
- $data[$table]['delta'] = array(
+ $data[$table_alias]['delta'] = array(
'group' => $group,
'title' => $title_delta,
'title short' => $title_short_delta,
'help' => t('Delta - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))),
);
- $data[$table]['delta']['field'] = array(
+ $data[$table_alias]['delta']['field'] = array(
'id' => 'numeric',
);
- $data[$table]['delta']['argument'] = array(
+ $data[$table_alias]['delta']['argument'] = array(
'field' => 'delta',
'table' => $table,
'id' => 'numeric',
'additional fields' => $additional_fields,
'empty field name' => t('- No value -'),
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
);
- $data[$table]['delta']['filter'] = array(
+ $data[$table_alias]['delta']['filter'] = array(
'field' => 'delta',
'table' => $table,
'id' => 'numeric',
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
'allow empty' => TRUE,
);
- $data[$table]['delta']['sort'] = array(
+ $data[$table_alias]['delta']['sort'] = array(
'field' => 'delta',
'table' => $table,
'id' => 'standard',
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
);
}
// Expose additional language column for translatable fields.
if (!empty($field['translatable'])) {
- $title_language = t('@label (!name:language)', array('@label' => $label, '!name' => $field['field_name']));
+ $title_language = t('@label (!name:language)', array('@label' => $label, '!name' => $field_name));
$title_short_language = t('@label:language', array('@label' => $label));
- $data[$table]['language'] = array(
+ $data[$table_alias]['language'] = array(
'group' => $group,
'title' => $title_language,
'title short' => $title_short_language,
'help' => t('Language - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))),
);
- $data[$table]['language']['field'] = array(
+ $data[$table_alias]['language']['field'] = array(
'id' => 'language',
);
- $data[$table]['language']['argument'] = array(
+ $data[$table_alias]['language']['argument'] = array(
'field' => 'language',
'table' => $table,
'id' => 'language',
'additional fields' => $additional_fields,
'empty field name' => t('- No value -'),
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
);
- $data[$table]['language']['filter'] = array(
+ $data[$table_alias]['language']['filter'] = array(
'field' => 'language',
'table' => $table,
'id' => 'language',
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
'allow empty' => TRUE,
);
- $data[$table]['language']['sort'] = array(
+ $data[$table_alias]['language']['sort'] = array(
'field' => 'language',
'table' => $table,
'id' => 'standard',
'additional fields' => $additional_fields,
- 'field_name' => $field['field_name'],
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
);
}
}
diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php
index 21e01c4..4a6bd11 100644
--- a/core/modules/field/lib/Drupal/field/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -7,6 +7,7 @@
namespace Drupal\field\Entity;
+use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Annotation\EntityType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Config\Entity\ConfigEntityBase;
@@ -38,24 +39,35 @@
class Field extends ConfigEntityBase implements FieldInterface {
/**
- * The maximum length of the field ID (machine name), in characters.
+ * The maximum length of the field name, in characters.
*
* For fields created through Field UI, this includes the 'field_' prefix.
*/
- const ID_MAX_LENGTH = 32;
+ const NAME_MAX_LENGTH = 32;
/**
- * The field ID (machine name).
+ * The field ID.
+ *
+ * The ID consists of 2 parts: the entity type and the field name.
+ *
+ * Example: node.body, user.field_main_image.
+ *
+ * @var string
+ */
+ public $id;
+
+ /**
+ * The field name.
*
* This is the name of the property under which the field values are placed in
- * an entity : $entity-{>$field_id}. The maximum length is
- * Field:ID_MAX_LENGTH.
+ * an entity: $entity->{$field_name}. The maximum length is
+ * Field:NAME_MAX_LENGTH.
*
* Example: body, field_main_image.
*
* @var string
*/
- public $id;
+ public $name;
/**
* The field UUID.
@@ -67,6 +79,13 @@ class Field extends ConfigEntityBase implements FieldInterface {
public $uuid;
/**
+ * The name of the entity type the field can be attached to.
+ *
+ * @var string
+ */
+ public $entity_type;
+
+ /**
* The field type.
*
* Example: text, number_integer.
@@ -119,16 +138,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
public $translatable = FALSE;
/**
- * The entity types on which the field is allowed to have instances.
- *
- * If empty or not specified, the field is allowed to have instances in any
- * entity type.
- *
- * @var array
- */
- public $entity_types = array();
-
- /**
* Flag indicating whether the field is available for editing.
*
* If TRUE, some actions not available though the UI (but are still possible
@@ -143,25 +152,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
public $locked = FALSE;
/**
- * The field storage definition.
- *
- * An array of key/value pairs identifying the storage backend to use for the
- * field:
- * - type: (string) The storage backend used by the field. Storage backends
- * are defined by modules that implement hook_field_storage_info().
- * - settings: (array) A sub-array of key/value pairs of settings. The keys
- * and default values are defined by the storage backend in the 'settings'
- * entry of hook_field_storage_info().
- * - module: (string, read-only) The name of the module that implements the
- * storage backend.
- * - active: (integer, read-only) TRUE if the module that implements the
- * storage backend is currently enabled, FALSE otherwise.
- *
- * @var array
- */
- public $storage = array();
-
- /**
* The custom storage indexes for the field data storage.
*
* This set of indexes is merged with the "default" indexes specified by the
@@ -202,13 +192,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
protected $schema;
/**
- * The storage information for the field.
- *
- * @var array
- */
- protected $storageDetails;
-
- /**
* The original field.
*
* @var \Drupal\field\Entity\Field
@@ -223,8 +206,9 @@ class Field extends ConfigEntityBase implements FieldInterface {
* elements will be used to set the corresponding properties on the class;
* see the class property documentation for details. Some array elements
* have special meanings and a few are required. Special elements are:
- * - id: required. As a temporary Backwards Compatibility layer right now,
+ * - name: required. As a temporary Backwards Compatibility layer right now,
* a 'field_name' property can be accepted in place of 'id'.
+ * - entity_type: required.
* - type: required.
*
* In most cases, Field entities are created via
@@ -237,22 +221,18 @@ class Field extends ConfigEntityBase implements FieldInterface {
*/
public function __construct(array $values, $entity_type = 'field_entity') {
// Check required properties.
- if (empty($values['type'])) {
- throw new FieldException('Attempt to create a field with no type.');
- }
- // Temporary BC layer: accept both 'id' and 'field_name'.
- // @todo $field_name and the handling for it will be removed in
- // http://drupal.org/node/1953408.
- if (empty($values['field_name']) && empty($values['id'])) {
+ if (empty($values['name'])) {
throw new FieldException('Attempt to create an unnamed field.');
}
- if (empty($values['id'])) {
- $values['id'] = $values['field_name'];
- unset($values['field_name']);
- }
- if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['id'])) {
+ if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['name'])) {
throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
}
+ if (empty($values['type'])) {
+ throw new FieldException('Attempt to create a field with no type.');
+ }
+ if (empty($values['entity_type'])) {
+ throw new FieldException('Attempt to create a field with no entity_type.');
+ }
parent::__construct($values, $entity_type);
}
@@ -260,18 +240,25 @@ public function __construct(array $values, $entity_type = 'field_entity') {
/**
* {@inheritdoc}
*/
+ public function id() {
+ return $this->entity_type . '.' . $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getExportProperties() {
$names = array(
'id',
'uuid',
'status',
'langcode',
+ 'name',
+ 'entity_type',
'type',
'settings',
'module',
'active',
- 'entity_types',
- 'storage',
'locked',
'cardinality',
'translatable',
@@ -297,7 +284,7 @@ public function getExportProperties() {
*/
public function save() {
// Clear the derived data about the field.
- unset($this->schema, $this->storageDetails);
+ unset($this->schema);
if ($this->isNew()) {
return $this->saveNew();
@@ -319,18 +306,20 @@ public function save() {
* In case of failures at the configuration storage level.
*/
protected function saveNew() {
- $module_handler = \Drupal::moduleHandler();
$entity_manager = \Drupal::entityManager();
$storage_controller = $entity_manager->getStorageController($this->entityType);
- // Field name cannot be longer than Field::ID_MAX_LENGTH characters. We
- // use drupal_strlen() because the DB layer assumes that column widths
+ // Assign the ID.
+ $this->id = $this->id();
+
+ // Field name cannot be longer than Field::NAME_MAX_LENGTH characters. We
+ // use Unicode::strlen() because the DB layer assumes that column widths
// are given in characters rather than bytes.
- if (drupal_strlen($this->id) > static::ID_MAX_LENGTH) {
+ if (Unicode::strlen($this->name) > static::NAME_MAX_LENGTH) {
throw new FieldException(format_string(
- 'Attempt to create a field with an ID longer than @max characters: %id', array(
- '@max' => static::ID_MAX_LENGTH,
- '%id' => $this->id,
+ 'Attempt to create a field with an ID longer than @max characters: %name', array(
+ '@max' => static::NAME_MAX_LENGTH,
+ '%name' => $this->name,
)
));
}
@@ -338,17 +327,17 @@ protected function saveNew() {
// Ensure the field name is unique (we do not care about deleted fields).
if ($prior_field = $storage_controller->load($this->id)) {
$message = $prior_field->active ?
- 'Attempt to create field name %id which already exists and is active.' :
- 'Attempt to create field name %id which already exists, although it is inactive.';
- throw new FieldException(format_string($message, array('%id' => $this->id)));
+ 'Attempt to create field name %name which already exists and is active.' :
+ 'Attempt to create field name %name which already exists, although it is inactive.';
+ throw new FieldException(format_string($message, array('%name' => $this->name)));
}
// Disallow reserved field names. This can't prevent all field name
// collisions with existing entity properties, but some is better than
// none.
foreach ($entity_manager->getDefinitions() as $type => $info) {
- if (in_array($this->id, $info['entity_keys'])) {
- throw new FieldException(format_string('Attempt to create field %id which is reserved by entity type %type.', array('%id' => $this->id, '%type' => $type)));
+ if (in_array($this->name, $info['entity_keys'])) {
+ throw new FieldException(format_string('Attempt to create field %name which is reserved by entity type %type.', array('%name' => $this->name, '%type' => $type)));
}
}
@@ -364,23 +353,8 @@ protected function saveNew() {
// definition is passed to the various hooks and written to config.
$this->settings += $field_type['settings'];
- // Provide default storage.
- $this->storage += array(
- 'type' => variable_get('field_storage_default', 'field_sql_storage'),
- 'settings' => array(),
- );
- // Check that the storage type is known.
- $storage_type = field_info_storage_types($this->storage['type']);
- if (!$storage_type) {
- throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array('%type' => $this->storage['type'])));
- }
- $this->storage['module'] = $storage_type['module'];
- $this->storage['active'] = TRUE;
- // Provide default storage settings.
- $this->storage['settings'] += $storage_type['settings'];
-
- // Invoke the storage backend's hook_field_storage_create_field().
- $module_handler->invoke($this->storage['module'], 'field_storage_create_field', array($this));
+ // Notify the entity storage controller.
+ $entity_manager->getStorageController($this->entity_type)->onFieldCreate($this);
// Save the configuration.
$result = parent::save();
@@ -402,7 +376,8 @@ protected function saveNew() {
*/
protected function saveUpdated() {
$module_handler = \Drupal::moduleHandler();
- $storage_controller = \Drupal::entityManager()->getStorageController($this->entityType);
+ $entity_manager = \Drupal::entityManager();
+ $storage_controller = $entity_manager->getStorageController($this->entityType);
$original = $storage_controller->loadUnchanged($this->id());
$this->original = $original;
@@ -411,11 +386,8 @@ protected function saveUpdated() {
if ($this->type != $original->type) {
throw new FieldException("Cannot change an existing field's type.");
}
- if ($this->entity_types != $original->entity_types) {
- throw new FieldException("Cannot change an existing field's entity_types property.");
- }
- if ($this->storage['type'] != $original->storage['type']) {
- throw new FieldException("Cannot change an existing field's storage type.");
+ if ($this->entity_type != $original->entity_type) {
+ throw new FieldException("Cannot change an existing field's entity_type.");
}
// Make sure all settings are present, so that a complete field definition
@@ -427,11 +399,10 @@ protected function saveUpdated() {
// invokes hook_field_update_forbid().
$module_handler->invokeAll('field_update_forbid', array($this, $original));
- // Tell the storage engine to update the field by invoking the
- // hook_field_storage_update_field(). The storage engine can reject the
- // definition update as invalid by raising an exception, which stops
- // execution before the definition is written to config.
- $module_handler->invoke($this->storage['module'], 'field_storage_update_field', array($this, $original));
+ // Notify the storage controller. The controller can reject the definition
+ // update as invalid by raising an exception, which stops execution before
+ // the definition is written to config.
+ $entity_manager->getStorageController($this->entity_type)->onFieldUpdate($this, $original);
// Save the configuration.
$result = parent::save();
@@ -445,16 +416,13 @@ protected function saveUpdated() {
*/
public function delete() {
if (!$this->deleted) {
- $module_handler = \Drupal::moduleHandler();
$instance_controller = \Drupal::entityManager()->getStorageController('field_instance');
$state = \Drupal::state();
// Delete all non-deleted instances.
$instance_ids = array();
- foreach ($this->getBundles() as $entity_type => $bundles) {
- foreach ($bundles as $bundle) {
- $instance_ids[] = "$entity_type.$bundle.$this->id";
- }
+ foreach ($this->getBundles() as $bundle) {
+ $instance_ids[] = "{$this->entity_type}.$bundle.{$this->name}";
}
foreach ($instance_controller->loadMultiple($instance_ids) as $instance) {
// By default, FieldInstance::delete() will automatically try to delete
@@ -464,9 +432,7 @@ public function delete() {
$instance->delete(FALSE);
}
- // Mark field data for deletion by invoking
- // hook_field_storage_delete_field().
- $module_handler->invoke($this->storage['module'], 'field_storage_delete_field', array($this));
+ \Drupal::entityManager()->getStorageController($this->entity_type)->onFieldDelete($this);
// Delete the configuration of this field and save the field configuration
// in the key_value table so we can use it later during
@@ -529,30 +495,11 @@ public function getColumns() {
/**
* {@inheritdoc}
*/
- public function getStorageDetails() {
- if (!isset($this->storageDetails)) {
- $module_handler = \Drupal::moduleHandler();
-
- // Collect the storage details from the storage backend, and let other
- // modules alter it. This invokes hook_field_storage_details() and
- // hook_field_storage_details_alter().
- $details = (array) $module_handler->invoke($this->storage['module'], 'field_storage_details', array($this));
- $module_handler->alter('field_storage_details', $details, $this);
-
- $this->storageDetails = $details;
- }
-
- return $this->storageDetails;
- }
-
- /**
- * {@inheritdoc}
- */
public function getBundles() {
if (empty($this->deleted)) {
$map = field_info_field_map();
- if (isset($map[$this->id]['bundles'])) {
- return $map[$this->id]['bundles'];
+ if (isset($map[$this->entity_type][$this->name]['bundles'])) {
+ return $map[$this->entity_type][$this->name]['bundles'];
}
}
return array();
@@ -562,7 +509,7 @@ public function getBundles() {
* {@inheritdoc}
*/
public function getFieldName() {
- return $this->id;
+ return $this->name;
}
/**
@@ -670,7 +617,7 @@ public function &offsetGet($offset) {
return $this->uuid;
case 'field_name':
- return $this->id;
+ return $this->name;
case 'columns':
$this->getSchema();
@@ -683,10 +630,6 @@ public function &offsetGet($offset) {
case 'bundles':
$bundles = $this->getBundles();
return $bundles;
-
- case 'storage_details':
- $this->getStorageDetails();
- return $this->storageDetails;
}
return $this->{$offset};
@@ -741,19 +684,19 @@ public static function getReservedColumns() {
* TRUE if the field has data for any entity; FALSE otherwise.
*/
public function hasData() {
- $storage_details = $this->getSchema();
- $columns = array_keys($storage_details['columns']);
- $factory = \Drupal::service('entity.query');
- foreach ($this->getBundles() as $entity_type => $bundle) {
+ if ($this->getBundles()) {
+ $storage_details = $this->getSchema();
+ $columns = array_keys($storage_details['columns']);
+ $factory = \Drupal::service('entity.query');
// Entity Query throws an exception if there is no base table.
- $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
+ $entity_info = \Drupal::entityManager()->getDefinition($this->entity_type);
if (!isset($entity_info['base_table'])) {
- continue;
+ return FALSE;
}
- $query = $factory->get($entity_type);
+ $query = $factory->get($this->entity_type);
$group = $query->orConditionGroup();
foreach ($columns as $column) {
- $group->exists($this->id() . '.' . $column);
+ $group->exists($this->name . '.' . $column);
}
$result = $query
->condition($group)
@@ -768,4 +711,5 @@ public function hasData() {
return FALSE;
}
+
}
diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
index f99fb1f..4d34da4 100644
--- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
@@ -7,6 +7,7 @@
namespace Drupal\field\Entity;
+use Drupal\Component\Utility\String;
use Drupal\Core\Entity\Annotation\EntityType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Config\Entity\ConfigEntityBase;
@@ -35,7 +36,7 @@
class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
/**
- * The instance ID (machine name).
+ * The instance ID.
*
* The ID consists of 3 parts: the entity type, bundle and the field name.
*
@@ -235,10 +236,10 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
public function __construct(array $values, $entity_type = 'field_instance') {
// Accept incoming 'field_name' instead of 'field_uuid', for easier DX on
// creation of new instances.
- if (isset($values['field_name']) && !isset($values['field_uuid'])) {
- $field = field_info_field($values['field_name']);
+ if (isset($values['field_name']) && isset($values['entity_type']) && !isset($values['field_uuid'])) {
+ $field = field_info_field($values['entity_type'], $values['field_name']);
if (!$field) {
- throw new FieldException(format_string('Attempt to create an instance of unknown, disabled, or deleted field @field_id', array('@field_id' => $values['field_name'])));
+ throw new FieldException(format_string('Attempt to create an instance of field @field_name that does not exist on entity type @entity_type.', array('@field_name' => $values['field_name'], '@entity_type' => $values['entity_type'])));
}
$values['field_uuid'] = $field->uuid;
}
@@ -267,16 +268,16 @@ public function __construct(array $values, $entity_type = 'field_instance') {
// Check required properties.
if (empty($values['entity_type'])) {
- throw new FieldException(format_string('Attempt to create an instance of field @field_id without an entity type.', array('@field_id' => $this->field->id)));
+ throw new FieldException(format_string('Attempt to create an instance of field @field_name without an entity_type.', array('@field_name' => $this->field->name)));
}
if (empty($values['bundle'])) {
- throw new FieldException(format_string('Attempt to create an instance of field @field_id without a bundle.', array('@field_id' => $this->field->id)));
+ throw new FieldException(format_string('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $this->field->name)));
}
- // 'Label' defaults to the field ID (mostly useful for field instances
+ // 'Label' defaults to the field name (mostly useful for field instances
// created in tests).
$values += array(
- 'label' => $this->field->id,
+ 'label' => $this->field->name,
);
parent::__construct($values, $entity_type);
}
@@ -285,7 +286,7 @@ public function __construct(array $values, $entity_type = 'field_instance') {
* {@inheritdoc}
*/
public function id() {
- return $this->entity_type . '.' . $this->bundle . '.' . $this->field->id;
+ return $this->entity_type . '.' . $this->bundle . '.' . $this->field->name;
}
/**
@@ -355,14 +356,9 @@ public function save() {
protected function saveNew() {
$instance_controller = \Drupal::entityManager()->getStorageController($this->entityType);
- // Check that the field can be attached to this entity type.
- if (!empty($this->field->entity_types) && !in_array($this->entity_type, $this->field->entity_types)) {
- throw new FieldException(format_string('Attempt to create an instance of field @field_id on forbidden entity type @entity_type.', array('@field_id' => $this->field->id, '@entity_type' => $this->entity_type)));
- }
-
// Ensure the field instance is unique within the bundle.
if ($prior_instance = $instance_controller->load($this->id())) {
- throw new FieldException(format_string('Attempt to create an instance of field @field_id on bundle @bundle that already has an instance of that field.', array('@field_id' => $this->field->id, '@bundle' => $this->bundle)));
+ throw new FieldException(format_string('Attempt to create an instance of field %name on bundle @bundle that already has an instance of that field.', array('%name' => $this->field->name, '@bundle' => $this->bundle)));
}
// Set the field UUID.
@@ -410,6 +406,9 @@ protected function saveUpdated() {
// Ensure default values are present.
$this->prepareSave();
+ // Notify the entity storage controller.
+ \Drupal::entityManager()->getStorageController($this->entity_type)->onInstanceUpdate($this);
+
// Save the configuration.
$result = parent::save();
field_cache_clear();
@@ -437,7 +436,6 @@ protected function prepareSave() {
*/
public function delete($field_cleanup = TRUE) {
if (!$this->deleted) {
- $module_handler = \Drupal::moduleHandler();
$state = \Drupal::state();
// Delete the configuration of this instance and save the configuration
@@ -451,16 +449,15 @@ public function delete($field_cleanup = TRUE) {
parent::delete();
+ // Notify the entity storage controller.
+ \Drupal::entityManager()->getStorageController($this->entity_type)->onInstanceDelete($this);
+
// Clear the cache.
field_cache_clear();
- // Mark instance data for deletion by invoking
- // hook_field_storage_delete_instance().
- $module_handler->invoke($this->field->storage['module'], 'field_storage_delete_instance', array($this));
-
// Remove the instance from the entity form displays.
if ($form_display = entity_load('entity_form_display', $this->entity_type . '.' . $this->bundle . '.default')) {
- $form_display->removeComponent($this->field->id())->save();
+ $form_display->removeComponent($this->field->name)->save();
}
// Remove the instance from the entity displays.
@@ -470,7 +467,7 @@ public function delete($field_cleanup = TRUE) {
$ids[] = $this->entity_type . '.' . $this->bundle . '.' . $view_mode;
}
foreach (entity_load_multiple('entity_display', $ids) as $display) {
- $display->removeComponent($this->field->id())->save();
+ $display->removeComponent($this->field->name)->save();
}
// Delete the field itself if we just deleted its last instance.
@@ -491,7 +488,7 @@ public function getField() {
* {@inheritdoc}
*/
public function getFieldName() {
- return $this->field->id;
+ return $this->field->name;
}
/**
@@ -618,7 +615,7 @@ public function &offsetGet($offset) {
return $this->field_uuid;
}
if ($offset == 'field_name') {
- return $this->field->id;
+ return $this->field->name;
}
return $this->{$offset};
}
diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php
index e97a412..8d6bc3a 100644
--- a/core/modules/field/lib/Drupal/field/FieldInfo.php
+++ b/core/modules/field/lib/Drupal/field/FieldInfo.php
@@ -165,10 +165,10 @@ public function flush() {
* Collects a lightweight map of fields across bundles.
*
* @return
- * An array keyed by field name. Each value is an array with two entries:
+ * An array keyed by entity type. Each value is an array which keys are
+ * field names and value is an array with two entries:
* - type: The field type.
- * - bundles: The bundles in which the field appears, as an array with
- * entity types as keys and the array of bundle names as values.
+ * - bundles: The bundles in which the field appears.
*/
public function getFieldMap() {
// Read from the "static" cache.
@@ -191,7 +191,7 @@ public function getFieldMap() {
// Get active fields.
foreach (config_get_storage_names_with_prefix('field.field.') as $config_id) {
$field_config = $this->config->get($config_id)->get();
- if ($field_config['active'] && $field_config['storage']['active']) {
+ if ($field_config['active']) {
$fields[$field_config['uuid']] = $field_config;
}
}
@@ -203,8 +203,8 @@ public function getFieldMap() {
// entity types.
if (isset($fields[$field_uuid])) {
$field = $fields[$field_uuid];
- $map[$field['id']]['bundles'][$instance_config['entity_type']][] = $instance_config['bundle'];
- $map[$field['id']]['type'] = $field['type'];
+ $map[$instance_config['entity_type']][$field['name']]['bundles'][] = $instance_config['bundle'];
+ $map[$instance_config['entity_type']][$field['name']]['type'] = $field['type'];
}
}
@@ -244,7 +244,7 @@ public function getFields() {
// Fill the name/ID map.
foreach ($this->fieldsById as $field) {
if (!$field['deleted']) {
- $this->fieldIdsByName[$field['id']] = $field['uuid'];
+ $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
}
}
@@ -280,7 +280,7 @@ public function getInstances($entity_type = NULL) {
$this->getFields();
foreach (field_read_instances() as $instance) {
- $field = $this->getField($instance['field_name']);
+ $field = $this->getField($instance['entity_type'], $instance['field_name']);
$instance = $this->prepareInstance($instance, $field['type']);
$this->bundleInstances[$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
}
@@ -305,36 +305,38 @@ public function getInstances($entity_type = NULL) {
*
* This method only retrieves active, non-deleted fields.
*
- * @param $field_name
+ * @param string $entity_type
+ * The entity type.
+ * @param string $field_name
* The field name.
*
* @return
* The field definition, or NULL if no field was found.
*/
- public function getField($field_name) {
+ public function getField($entity_type, $field_name) {
// Read from the "static" cache.
- if (isset($this->fieldIdsByName[$field_name])) {
- $field_id = $this->fieldIdsByName[$field_name];
+ if (isset($this->fieldIdsByName[$entity_type][$field_name])) {
+ $field_id = $this->fieldIdsByName[$entity_type][$field_name];
return $this->fieldsById[$field_id];
}
- if (isset($this->unknownFields[$field_name])) {
+ if (isset($this->unknownFields[$entity_type][$field_name])) {
return;
}
// Do not check the (large) persistent cache, but read the definition.
// Cache miss: read from definition.
- if ($field = field_read_field($field_name)) {
+ if ($field = entity_load('field_entity', $entity_type . '.' . $field_name)) {
$field = $this->prepareField($field);
// Save in the "static" cache.
$this->fieldsById[$field['uuid']] = $field;
- $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+ $this->fieldIdsByName[$entity_type][$field_name] = $field['uuid'];
return $field;
}
else {
- $this->unknownFields[$field_name] = TRUE;
+ $this->unknownFields[$entity_type][$field_name] = TRUE;
}
}
@@ -369,7 +371,7 @@ public function getFieldById($field_id) {
// Store in the static cache.
$this->fieldsById[$field['uuid']] = $field;
if (!$field['deleted']) {
- $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+ $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
}
return $field;
@@ -413,7 +415,7 @@ public function getBundleInstances($entity_type, $bundle) {
if (!isset($this->fieldsById[$field['uuid']])) {
$this->fieldsById[$field['uuid']] = $field;
if (!$field['deleted']) {
- $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+ $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
}
}
}
@@ -435,43 +437,46 @@ public function getBundleInstances($entity_type, $bundle) {
}
// Cache miss: collect from the definitions.
+ $field_map = $this->getFieldMap();
$instances = array();
$fields = array();
// Do not return anything for unknown entity types.
- if (entity_get_info($entity_type)) {
+ if (entity_get_info($entity_type) && !empty($field_map[$entity_type])) {
// Collect names of fields and instances involved in the bundle, using the
// field map. The field map is already filtered to active, non-deleted
// fields and instances, so those are kept out of the persistent caches.
$config_ids = array();
- foreach ($this->getFieldMap() as $field_name => $field_data) {
- if (isset($field_data['bundles'][$entity_type]) && in_array($bundle, $field_data['bundles'][$entity_type])) {
- $config_ids[$field_name] = "$entity_type.$bundle.$field_name";
+ foreach ($field_map[$entity_type] as $field_name => $field_data) {
+ if (in_array($bundle, $field_data['bundles'])) {
+ $config_ids["$entity_type.$field_name"] = "$entity_type.$bundle.$field_name";
}
}
// Load and prepare the corresponding fields and instances entities.
if ($config_ids) {
+ // Place the fields in our global "static".
$loaded_fields = entity_load_multiple('field_entity', array_keys($config_ids));
- $loaded_instances = entity_load_multiple('field_instance', array_values($config_ids));
-
- foreach ($loaded_instances as $instance) {
- $field = $loaded_fields[$instance['field_name']];
-
- $instance = $this->prepareInstance($instance, $field['type']);
- $instances[$field['field_name']] = $instance;
-
- // If the field is not in our global "static" list yet, add it.
+ foreach ($loaded_fields as $field) {
if (!isset($this->fieldsById[$field['uuid']])) {
$field = $this->prepareField($field);
$this->fieldsById[$field['uuid']] = $field;
- $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+ $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
}
$fields[] = $this->fieldsById[$field['uuid']];
}
+
+ // Then collect the instances.
+ $loaded_instances = entity_load_multiple('field_instance', array_values($config_ids));
+ foreach ($loaded_instances as $instance) {
+ $field = $instance->getField();
+
+ $instance = $this->prepareInstance($instance, $field['type']);
+ $instances[$field['field_name']] = $instance;
+ }
}
}
@@ -567,7 +572,6 @@ public function getBundleExtraFields($entity_type, $bundle) {
public function prepareField($field) {
// Make sure all expected field settings are present.
$field['settings'] += $this->fieldTypeManager->getDefaultSettings($field['type']);
- $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
return $field;
}
diff --git a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
index f9ce249..2649c6a 100644
--- a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
@@ -136,7 +136,6 @@ public function loadByProperties(array $conditions = array()) {
// Translate "do not include inactive fields" into actual conditions.
if (!$include_inactive) {
$conditions['field.active'] = TRUE;
- $conditions['field.storage.active'] = TRUE;
}
// Collect matching instances.
@@ -155,17 +154,13 @@ public function loadByProperties(array $conditions = array()) {
// Extract the actual value against which the condition is checked.
switch ($key) {
case 'field_name':
- $checked_value = $field->id;
+ $checked_value = $field->name;
break;
case 'field.active':
$checked_value = $field->active;
break;
- case 'field.storage.active':
- $checked_value = $field->storage['active'];
- break;
-
case 'field_id':
$checked_value = $instance->field_uuid;
break;
diff --git a/core/modules/field/lib/Drupal/field/FieldInterface.php b/core/modules/field/lib/Drupal/field/FieldInterface.php
index f1a2ff7..cc78d35 100644
--- a/core/modules/field/lib/Drupal/field/FieldInterface.php
+++ b/core/modules/field/lib/Drupal/field/FieldInterface.php
@@ -45,34 +45,6 @@ public function getSchema();
public function getColumns();
/**
- * Returns information about how the storage backend stores the field data.
- *
- * The content of the returned value depends on the storage backend, and some
- * storage backends might provide no information.
- *
- * It is strongly discouraged to use this information to perform direct write
- * operations to the field data storage, bypassing the regular field saving
- * APIs.
- *
- * Example return value for the default field_sql_storage backend:
- * - 'sql'
- * - FIELD_LOAD_CURRENT
- * - Table name (string).
- * - Table schema (array)
- * - FIELD_LOAD_REVISION
- * - Table name (string).
- * - Table schema (array).
- *
- * @return array
- * The storage details.
- * - The first dimension is a store type (sql, solr, etc).
- * - The second dimension indicates the age of the values in the store
- * FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
- * - Other dimensions are specific to the field storage backend.
- */
- public function getStorageDetails();
-
- /**
* Returns the list of bundles where the field has instances.
*
* @return array
diff --git a/core/modules/field/lib/Drupal/field/FieldStorageController.php b/core/modules/field/lib/Drupal/field/FieldStorageController.php
index 41efe60..8b0f92e 100644
--- a/core/modules/field/lib/Drupal/field/FieldStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldStorageController.php
@@ -98,9 +98,10 @@ public function loadByProperties(array $conditions = array()) {
unset($conditions['include_deleted']);
// Get fields stored in configuration.
- if (isset($conditions['field_name'])) {
+ if (isset($conditions['entity_type']) && isset($conditions['field_name'])) {
// Optimize for the most frequent case where we do have a specific ID.
- $fields = $this->entityManager->getStorageController($this->entityType)->loadMultiple(array($conditions['field_name']));
+ $id = $conditions['entity_type'] . $conditions['field_name'];
+ $fields = $this->entityManager->getStorageController($this->entityType)->loadMultiple(array($id));
}
else {
// No specific ID, we need to examine all existing fields.
@@ -118,7 +119,6 @@ public function loadByProperties(array $conditions = array()) {
// Translate "do not include inactive instances" into actual conditions.
if (!$include_inactive) {
$conditions['active'] = TRUE;
- $conditions['storage.active'] = TRUE;
}
// Collect matching fields.
@@ -127,12 +127,8 @@ public function loadByProperties(array $conditions = array()) {
foreach ($conditions as $key => $value) {
// Extract the actual value against which the condition is checked.
switch ($key) {
- case 'storage.active':
- $checked_value = $field->storage['active'];
- break;
-
case 'field_name';
- $checked_value = $field->id;
+ $checked_value = $field->name;
break;
default:
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php
index 31e65ca..da703fa 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php
@@ -42,8 +42,8 @@ public function validate() {
$entity = $this->getParent();
$langcode = $entity->language()->id;
- if (isset($legacy_errors[$this->getInstance()->getField()->id()][$langcode])) {
- foreach ($legacy_errors[$this->getInstance()->getField()->id()][$langcode] as $delta => $item_errors) {
+ if (isset($legacy_errors[$this->getInstance()->getField()->name][$langcode])) {
+ foreach ($legacy_errors[$this->getInstance()->getField()->name][$langcode] as $delta => $item_errors) {
foreach ($item_errors as $item_error) {
// We do not have the information about which column triggered the
// error, so assume the first column...
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php
index 8407cbf..7c7e032 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php
@@ -90,8 +90,8 @@ public function prepareCache() {
$langcode = $entity->language()->id;
$entity_id = $entity->id();
- // hook_field_attach_load() receives items keyed by entity id, and alter
- // then by reference.
+ // hook_field_load() receives items keyed by entity id, and alters then by
+ // reference.
$items = array($entity_id => array(0 => $this->getValue(TRUE)));
$args = array(
$entity->entityType(),
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php b/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php
index d6ddcac..75b7252 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php
@@ -35,7 +35,7 @@ class FieldList extends Numeric {
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
- $field = field_info_field($this->definition['field_name']);
+ $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
$this->allowed_values = options_allowed_values($field);
}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php b/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php
index 0c34d1b..83e24d9 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php
@@ -35,7 +35,7 @@ class ListString extends String {
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
- $field = field_info_field($this->definition['field_name']);
+ $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
$this->allowed_values = options_allowed_values($field);
}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index a487a7a..6f09d6f 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -7,8 +7,9 @@
namespace Drupal\field\Plugin\views\field;
-use Drupal\Core\Language\Language;
+use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
use Drupal\field\Plugin\Type\Formatter\FormatterPluginManager;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
@@ -111,7 +112,7 @@ public static function create(ContainerInterface $container, array $configuratio
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
- $this->field_info = $field = field_info_field($this->definition['field_name']);
+ $this->field_info = $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
$this->multiple = FALSE;
$this->limit_values = FALSE;
@@ -286,7 +287,8 @@ public function clickSort($order) {
}
$this->ensureMyTable();
- $column = _field_sql_storage_columnname($this->definition['field_name'], $this->options['click_sort_column']);
+ $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
+ $column = DatabaseStorageController::_fieldColumnName($field, $this->options['click_sort_column']);
if (!isset($this->aliases[$column])) {
// Column is not in query; add a sort on it (without adding the column).
$this->aliases[$column] = $this->tableAlias . '.' . $column;
@@ -298,7 +300,7 @@ protected function defineOptions() {
$options = parent::defineOptions();
// defineOptions runs before init/construct, so no $this->field_info
- $field = field_info_field($this->definition['field_name']);
+ $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
$field_type = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field['type']);
$column_names = array_keys($field['columns']);
$default_column = '';
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php b/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php
index cfd2f6c..2b1d57c 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php
@@ -20,7 +20,7 @@
class FieldList extends ManyToOne {
public function getValueOptions() {
- $field = field_info_field($this->definition['field_name']);
+ $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
$this->value_options = list_allowed_values($field);
}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
index 481105f..3103945 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
@@ -28,7 +28,7 @@ class EntityReverse extends RelationshipPluginBase {
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
- $this->field_info = field_info_field($this->definition['field_name']);
+ $this->field_info = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
}
/**
diff --git a/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php b/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
index c74d426..7b021e2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
@@ -14,7 +14,7 @@ class ActiveTest extends FieldTestBase {
*
* @var array
*/
- public static $modules = array('field_test');
+ public static $modules = array('field_test', 'entity_test');
public static function getInfo() {
return array(
@@ -28,62 +28,28 @@ public static function getInfo() {
* Test that fields are properly marked active or inactive.
*/
function testActive() {
- $field_definition = array(
- 'field_name' => 'field_1',
+ $field_name = 'field_1';
+ entity_create('field_entity', array(
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
- // For this test, we need a storage backend provided by a different
- // module than field_test.module.
- 'storage' => array(
- 'type' => 'field_sql_storage',
- ),
- );
- entity_create('field_entity', $field_definition)->save();
-
- // Test disabling and enabling:
- // - the field type module,
- // - the storage module,
- // - both.
- $this->_testActiveHelper($field_definition, array('field_test'));
- $this->_testActiveHelper($field_definition, array('field_sql_storage'));
- $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
- }
-
- /**
- * Helper function for testActive().
- *
- * Test dependency between a field and a set of modules.
- *
- * @param $field_definition
- * A field definition.
- * @param $modules
- * An aray of module names. The field will be tested to be inactive as long
- * as any of those modules is disabled.
- */
- function _testActiveHelper($field_definition, $modules) {
- $field_name = $field_definition['field_name'];
-
- // Read the field.
- $field = field_read_field($field_name);
- $this->assertTrue($field_definition <= $field, 'The field was properly read.');
-
- module_disable($modules, FALSE);
-
- $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
- $this->assertTrue(isset($fields[$field_name]) && $field_definition < $field, 'The field is properly read when explicitly fetching inactive fields.');
-
- // Re-enable modules one by one, and check that the field is still inactive
- // while some modules remain disabled.
- while ($modules) {
- $field = field_read_field($field_name);
- $this->assertTrue(empty($field), format_string('%modules disabled. The field is marked inactive.', array('%modules' => implode(', ', $modules))));
-
- $module = array_shift($modules);
- module_enable(array($module), FALSE);
- }
-
- // Check that the field is active again after all modules have been
- // enabled.
- $field = field_read_field($field_name);
- $this->assertTrue($field_definition <= $field, 'The field was was marked active.');
+ ))->save();
+
+ // Check that the field is correctly found.
+ $field = field_read_field('entity_test', $field_name);
+ $this->assertFalse(empty($field), 'The field was properly read.');
+
+ // Disable the module providing the field type, and check that the field is
+ // found only if explicitly requesting inactive fields.
+ module_disable(array('field_test'));
+ $field = field_read_field('entity_test', $field_name);
+ $this->assertTrue(empty($field), 'The field is marked inactive when the field type is absent.');
+ $field = field_read_field('entity_test', $field_name, array('include_inactive' => TRUE));
+ $this->assertFalse(empty($field), 'The field is properly read when explicitly fetching inactive fields.');
+
+ // Re-enable the module, and check that the field is active again.
+ module_enable(array('field_test'));
+ $field = field_read_field('entity_test', $field_name);
+ $this->assertFalse(empty($field), 'The field was was marked active.');
}
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
index 58799f5..0baa951 100644
--- a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
@@ -7,10 +7,10 @@
namespace Drupal\field\Tests;
-use Drupal\field\Entity\FieldInstance;
+use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\FieldInterface;
-use Drupal\Core\Language\Language;
/**
* Unit test class for field bulk delete and batch purge functionality.
@@ -50,7 +50,7 @@ class BulkDeleteTest extends FieldUnitTestBase {
*
* @var array
*/
- protected $entity_type = 'test_entity';
+ protected $entity_type = 'entity_test';
public static function getInfo() {
return array(
@@ -114,14 +114,16 @@ function setUp() {
// Create two fields.
$field = entity_create('field_entity', array(
- 'field_name' => 'bf_1',
+ 'name' => 'bf_1',
+ 'entity_type' => $this->entity_type,
'type' => 'test_field',
'cardinality' => 1
));
$field->save();
$this->fields[] = $field;
$field = entity_create('field_entity', array(
- 'field_name' => 'bf_2',
+ 'name' => 'bf_2',
+ 'entity_type' => $this->entity_type,
'type' => 'test_field',
'cardinality' => 4
));
@@ -130,11 +132,10 @@ function setUp() {
// For each bundle, create an instance of each field, and 10
// entities with values for each field.
- $this->entity_type = 'entity_test';
foreach ($this->bundles as $bundle) {
foreach ($this->fields as $field) {
entity_create('field_instance', array(
- 'field_name' => $field->id(),
+ 'field_name' => $field->name,
'entity_type' => $this->entity_type,
'bundle' => $bundle,
))->save();
@@ -165,7 +166,7 @@ function setUp() {
function testDeleteFieldInstance() {
$bundle = reset($this->bundles);
$field = reset($this->fields);
- $field_name = $field->id();
+ $field_name = $field->name;
$factory = \Drupal::service('entity.query');
// There are 10 entities of this bundle.
@@ -175,7 +176,7 @@ function testDeleteFieldInstance() {
$this->assertEqual(count($found), 10, 'Correct number of entities found before deleting');
// Delete the instance.
- $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+ $instance = field_info_instance($this->entity_type, $field->name, $bundle);
$instance->delete();
// The instance still exists, deleted.
@@ -184,6 +185,17 @@ function testDeleteFieldInstance() {
$instance = $instances[0];
$this->assertEqual($instance['bundle'], $bundle, 'The deleted instance is for the correct bundle');
+ // Check that the actual stored content did not change during delete.
+ $schema = DatabaseStorageController::_fieldSqlSchema($field);
+ $table = DatabaseStorageController::_fieldTableName($field);
+ $column = DatabaseStorageController::_fieldColumnName($field, 'value');
+ $result = db_select($table, 't')
+ ->fields('t', array_keys($schema[$table]['fields']))
+ ->execute();
+ foreach ($result as $row) {
+ $this->assertEqual($this->entities[$row->entity_id]->{$field->name}->value, $row->$column);
+ }
+
// There are 0 entities of this bundle with non-deleted data.
$found = $factory->get('entity_test')
->condition('type', $bundle)
@@ -198,20 +210,8 @@ function testDeleteFieldInstance() {
->condition("$field_name.deleted", 1)
->sort('id')
->execute();
- $ids = (object) array(
- 'entity_type' => 'entity_test',
- 'bundle' => $bundle,
- );
- $entities = array();
- foreach ($found as $entity_id) {
- $ids->entity_id = $entity_id;
- $entities[$entity_id] = _field_create_entity_from_ids($ids);
- }
- field_attach_load($this->entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
$this->assertEqual(count($found), 10, 'Correct number of entities found after deleting');
- foreach ($entities as $id => $entity) {
- $this->assertEqual($this->entities[$id]->{$field->id()}->value, $entity->{$field->id()}[Language::LANGCODE_NOT_SPECIFIED][0]['value'], "Entity $id with deleted data loaded correctly");
- }
+ $this->assertFalse(array_diff($found, array_keys($this->entities)));
}
/**
@@ -226,7 +226,7 @@ function testPurgeInstance() {
$field = reset($this->fields);
// Delete the instance.
- $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+ $instance = field_info_instance($this->entity_type, $field->name, $bundle);
$instance->delete();
// No field hooks were called.
@@ -241,19 +241,18 @@ function testPurgeInstance() {
// There are $count deleted entities left.
$found = \Drupal::entityQuery('entity_test')
->condition('type', $bundle)
- ->condition($field->id() . '.deleted', 1)
+ ->condition($field->name . '.deleted', 1)
->execute();
$this->assertEqual(count($found), $count, 'Correct number of entities found after purging 2');
}
// Check hooks invocations.
- // hook_field_load() and hook_field_delete() should have been called once
- // for each entity in the bundle.
+ // hook_field_delete() should have been called once for each entity in the
+ // bundle.
$actual_hooks = field_test_memorize();
$hooks = array();
$entities = $this->entities_by_bundles[$bundle];
foreach ($entities as $id => $entity) {
- $hooks['field_test_field_load'][] = array($id => $entity);
$hooks['field_test_field_delete'][] = $entity;
}
$this->checkHooksInvocations($hooks, $actual_hooks);
@@ -286,7 +285,7 @@ function testPurgeField() {
// Delete the first instance.
$bundle = reset($this->bundles);
- $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+ $instance = field_info_instance($this->entity_type, $field->name, $bundle);
$instance->delete();
// Assert that hook_field_delete() was not called yet.
@@ -297,13 +296,12 @@ function testPurgeField() {
field_purge_batch(10);
// Check hooks invocations.
- // hook_field_load() and hook_field_delete() should have been called once
- // for each entity in the bundle.
+ // hook_field_delete() should have been called once for each entity in the
+ // bundle.
$actual_hooks = field_test_memorize();
$hooks = array();
$entities = $this->entities_by_bundles[$bundle];
foreach ($entities as $id => $entity) {
- $hooks['field_test_field_load'][] = array($id => $entity);
$hooks['field_test_field_delete'][] = $entity;
}
$this->checkHooksInvocations($hooks, $actual_hooks);
@@ -317,7 +315,7 @@ function testPurgeField() {
// Delete the second instance.
$bundle = next($this->bundles);
- $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+ $instance = field_info_instance($this->entity_type, $field->name, $bundle);
$instance->delete();
// Assert that hook_field_delete() was not called yet.
@@ -332,7 +330,6 @@ function testPurgeField() {
$hooks = array();
$entities = $this->entities_by_bundles[$bundle];
foreach ($entities as $id => $entity) {
- $hooks['field_test_field_load'][] = array($id => $entity);
$hooks['field_test_field_delete'][] = $entity;
}
$this->checkHooksInvocations($hooks, $actual_hooks);
diff --git a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
index 8e16c3e..ece8547 100644
--- a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
@@ -7,7 +7,6 @@
namespace Drupal\field\Tests;
-use Drupal\Core\Language\Language;
use Drupal\field\FieldException;
class CrudTest extends FieldUnitTestBase {
@@ -37,14 +36,15 @@ public static function getInfo() {
*/
function testCreateField() {
$field_definition = array(
- 'field_name' => 'field_2',
+ 'name' => 'field_2',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
field_test_memorize();
$field = entity_create('field_entity', $field_definition);
$field->save();
$mem = field_test_memorize();
- $this->assertIdentical($mem['field_test_field_entity_create'][0][0]['field_name'], $field_definition['field_name'], 'hook_entity_create() called with correct arguments.');
+ $this->assertIdentical($mem['field_test_field_entity_create'][0][0]['field_name'], $field_definition['name'], 'hook_entity_create() called with correct arguments.');
$this->assertIdentical($mem['field_test_field_entity_create'][0][0]['type'], $field_definition['type'], 'hook_entity_create() called with correct arguments.');
// Read the configuration. Check against raw configuration data rather than
@@ -53,7 +53,9 @@ function testCreateField() {
$field_config = \Drupal::config('field.field.' . $field->id())->get();
// Ensure that basic properties are preserved.
- $this->assertEqual($field_config['id'], $field_definition['field_name'], 'The field name is properly saved.');
+ $this->assertEqual($field_config['name'], $field_definition['name'], 'The field name is properly saved.');
+ $this->assertEqual($field_config['entity_type'], $field_definition['entity_type'], 'The field entity type is properly saved.');
+ $this->assertEqual($field_config['id'], $field_definition['entity_type'] . '.' . $field_definition['name'], 'The field id is properly saved.');
$this->assertEqual($field_config['type'], $field_definition['type'], 'The field type is properly saved.');
// Ensure that cardinality defaults to 1.
@@ -63,9 +65,6 @@ function testCreateField() {
$field_type = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field_definition['type']);
$this->assertEqual($field_config['settings'], $field_type['settings'], 'Default field settings have been written.');
- // Ensure that default storage was set.
- $this->assertEqual($field_config['storage']['type'], \Drupal::config('field.settings')->get('default_storage'), 'The field type is properly saved.');
-
// Guarantee that the name is unique.
try {
entity_create('field_entity', $field_definition)->save();
@@ -78,7 +77,8 @@ function testCreateField() {
// Check that field type is required.
try {
$field_definition = array(
- 'field_name' => 'field_1',
+ 'name' => 'field_1',
+ 'entity_type' => 'entity_type',
);
entity_create('field_entity', $field_definition)->save();
$this->fail(t('Cannot create a field with no type.'));
@@ -90,7 +90,8 @@ function testCreateField() {
// Check that field name is required.
try {
$field_definition = array(
- 'type' => 'test_field'
+ 'type' => 'test_field',
+ 'entity_type' => 'entity_test',
);
entity_create('field_entity', $field_definition)->save();
$this->fail(t('Cannot create an unnamed field.'));
@@ -98,11 +99,24 @@ function testCreateField() {
catch (FieldException $e) {
$this->pass(t('Cannot create an unnamed field.'));
}
+ // Check that entity type is required.
+ try {
+ $field_definition = array(
+ 'name' => 'test_field',
+ 'type' => 'test_field'
+ );
+ entity_create('field_entity', $field_definition)->save();
+ $this->fail('Cannot create a field without an entity type.');
+ }
+ catch (FieldException $e) {
+ $this->pass('Cannot create a field without an entity type.');
+ }
// Check that field name must start with a letter or _.
try {
$field_definition = array(
- 'field_name' => '2field_2',
+ 'name' => '2field_2',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
entity_create('field_entity', $field_definition)->save();
@@ -115,7 +129,8 @@ function testCreateField() {
// Check that field name must only contain lowercase alphanumeric or _.
try {
$field_definition = array(
- 'field_name' => 'field#_3',
+ 'name' => 'field#_3',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
entity_create('field_entity', $field_definition)->save();
@@ -128,7 +143,8 @@ function testCreateField() {
// Check that field name cannot be longer than 32 characters long.
try {
$field_definition = array(
- 'field_name' => '_12345678901234567890123456789012',
+ 'name' => '_12345678901234567890123456789012',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
entity_create('field_entity', $field_definition)->save();
@@ -143,7 +159,8 @@ function testCreateField() {
try {
$field_definition = array(
'type' => 'test_field',
- 'field_name' => 'id',
+ 'name' => 'id',
+ 'entity_type' => 'entity_test',
);
entity_create('field_entity', $field_definition)->save();
$this->fail(t('Cannot create a field bearing the name of an entity key.'));
@@ -161,7 +178,8 @@ function testCreateField() {
*/
function testCreateFieldWithExplicitSchema() {
$field_definition = array(
- 'field_name' => 'field_2',
+ 'name' => 'field_2',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
'schema' => array(
'dummy' => 'foobar'
@@ -172,68 +190,32 @@ function testCreateFieldWithExplicitSchema() {
}
/**
- * Test failure to create a field.
- */
- function testCreateFieldFail() {
- $field_name = 'duplicate';
- $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
- $field = entity_load('field_entity', $field_name);
-
- // The field does not exist.
- $this->assertFalse($field, 'The field does not exist.');
-
- // Try to create the field.
- try {
- entity_create('field_entity', $field_definition)->save();
- $this->assertTrue(FALSE, 'Field creation (correctly) fails.');
- }
- catch (\Exception $e) {
- $this->assertTrue(TRUE, 'Field creation (correctly) fails.');
- }
-
- // The field does not exist.
- $field = entity_load('field_entity', $field_name);
- $this->assertFalse($field, 'The field does not exist.');
- }
-
- /**
- * Tests reading a single field definition.
- */
- function testReadField() {
- $field_definition = array(
- 'field_name' => 'field_1',
- 'type' => 'test_field',
- );
- entity_create('field_entity', $field_definition)->save();
-
- // Read the field back.
- $field = field_read_field($field_definition['field_name']);
- $this->assertTrue($field_definition < $field, 'The field was properly read.');
- }
-
- /**
* Tests reading field definitions.
*/
function testReadFields() {
$field_definition = array(
- 'field_name' => 'field_1',
+ 'name' => 'field_1',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
- entity_create('field_entity', $field_definition)->save();
+ $field = entity_create('field_entity', $field_definition);
+ $field->save();
+ $id = $field->id();
+ field_cache_clear();
// Check that 'single column' criteria works.
- $fields = field_read_fields(array('field_name' => $field_definition['field_name']));
- $this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
+ $fields = field_read_fields(array('id' => $id));
+ $this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
// Check that 'multi column' criteria works.
- $fields = field_read_fields(array('field_name' => $field_definition['field_name'], 'type' => $field_definition['type']));
- $this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
- $fields = field_read_fields(array('field_name' => $field_definition['field_name'], 'type' => 'foo'));
+ $fields = field_read_fields(array('id' => $id, 'type' => $field_definition['type']));
+ $this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
+ $fields = field_read_fields(array('name' => $field_definition['name'], 'type' => 'foo'));
$this->assertTrue(empty($fields), 'No field was found.');
// Create an instance of the field.
$instance_definition = array(
- 'field_name' => $field_definition['field_name'],
+ 'field_name' => $field_definition['name'],
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
);
@@ -246,11 +228,14 @@ function testReadFields() {
function testFieldIndexes() {
// Check that indexes specified by the field type are used by default.
$field_definition = array(
- 'field_name' => 'field_1',
+ 'name' => 'field_1',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
- entity_create('field_entity', $field_definition)->save();
- $field = field_read_field($field_definition['field_name']);
+ $field = entity_create('field_entity', $field_definition);
+ $field->save();
+ field_cache_clear();
+ $field = entity_load('field_entity', $field->id());
$schema = $field->getSchema();
$expected_indexes = array('value' => array('value'));
$this->assertEqual($schema['indexes'], $expected_indexes, 'Field type indexes saved by default');
@@ -258,14 +243,17 @@ function testFieldIndexes() {
// Check that indexes specified by the field definition override the field
// type indexes.
$field_definition = array(
- 'field_name' => 'field_2',
+ 'name' => 'field_2',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
'indexes' => array(
'value' => array(),
),
);
- entity_create('field_entity', $field_definition)->save();
- $field = field_read_field($field_definition['field_name']);
+ $field = entity_create('field_entity', $field_definition);
+ $field->save();
+ field_cache_clear();
+ $field = entity_load('field_entity', $field->id());
$schema = $field->getSchema();
$expected_indexes = array('value' => array());
$this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes override field type indexes');
@@ -273,14 +261,18 @@ function testFieldIndexes() {
// Check that indexes specified by the field definition add to the field
// type indexes.
$field_definition = array(
- 'field_name' => 'field_3',
+ 'name' => 'field_3',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
'indexes' => array(
'value_2' => array('value'),
),
);
- entity_create('field_entity', $field_definition)->save();
- $field = field_read_field($field_definition['field_name']);
+ $field = entity_create('field_entity', $field_definition);
+ $field->save();
+ $id = $field->id();
+ field_cache_clear();
+ $field = entity_load('field_entity', $id);
$schema = $field->getSchema();
$expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
$this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes are merged with field type indexes');
@@ -293,30 +285,38 @@ function testDeleteField() {
// TODO: Also test deletion of the data stored in the field ?
// Create two fields (so we can test that only one is deleted).
- $this->field = array('field_name' => 'field_1', 'type' => 'test_field');
+ $this->field = array(
+ 'name' => 'field_1',
+ 'type' => 'test_field',
+ 'entity_type' => 'entity_test',
+ );
entity_create('field_entity', $this->field)->save();
- $this->another_field = array('field_name' => 'field_2', 'type' => 'test_field');
+ $this->another_field = array(
+ 'name' => 'field_2',
+ 'type' => 'test_field',
+ 'entity_type' => 'entity_test',
+ );
entity_create('field_entity', $this->another_field)->save();
// Create instances for each.
$this->instance_definition = array(
- 'field_name' => $this->field['field_name'],
+ 'field_name' => $this->field['name'],
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
);
entity_create('field_instance', $this->instance_definition)->save();
$another_instance_definition = $this->instance_definition;
- $another_instance_definition['field_name'] = $this->another_field['field_name'];
+ $another_instance_definition['field_name'] = $this->another_field['name'];
entity_create('field_instance', $another_instance_definition)->save();
// Test that the first field is not deleted, and then delete it.
- $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
+ $field = field_read_field('entity_test', $this->field['name'], array('include_deleted' => TRUE));
$this->assertTrue(!empty($field) && empty($field['deleted']), 'A new field is not marked for deletion.');
- field_info_field($this->field['field_name'])->delete();
+ field_info_field('entity_test', $this->field['name'])->delete();
// Make sure that the field is marked as deleted when it is specifically
// loaded.
- $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
+ $field = field_read_field('entity_test', $this->field['name'], array('include_deleted' => TRUE));
$this->assertTrue(!empty($field['deleted']), 'A deleted field is marked for deletion.');
// Make sure that this field's instance is marked as deleted when it is
@@ -325,7 +325,7 @@ function testDeleteField() {
$this->assertTrue(!empty($instance['deleted']), 'An instance for a deleted field is marked for deletion.');
// Try to load the field normally and make sure it does not show up.
- $field = field_read_field($this->field['field_name']);
+ $field = field_read_field('entity_test', $this->field['name']);
$this->assertTrue(empty($field), 'A deleted field is not loaded by default.');
// Try to load the instance normally and make sure it does not show up.
@@ -333,7 +333,7 @@ function testDeleteField() {
$this->assertTrue(empty($instance), 'An instance for a deleted field is not loaded by default.');
// Make sure the other field (and its field instance) are not deleted.
- $another_field = field_read_field($this->another_field['field_name']);
+ $another_field = field_read_field('entity_test', $this->another_field['name']);
$this->assertTrue(!empty($another_field) && empty($another_field['deleted']), 'A non-deleted field is not marked for deletion.');
$another_instance = field_read_instance('entity_test', $another_instance_definition['field_name'], $another_instance_definition['bundle']);
$this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), 'An instance of a non-deleted field is not marked for deletion.');
@@ -342,21 +342,18 @@ function testDeleteField() {
// write data into it.
entity_create('field_entity', $this->field)->save();
entity_create('field_instance', $this->instance_definition)->save();
- $field = field_read_field($this->field['field_name']);
+ $field = field_read_field('entity_test', $this->field['name']);
$this->assertTrue(!empty($field) && empty($field['deleted']), 'A new field with a previously used name is created.');
$instance = field_read_instance('entity_test', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
$this->assertTrue(!empty($instance) && empty($instance['deleted']), 'A new instance for a previously used field name is created.');
// Save an entity with data for the field
- $entity = entity_create('entity_test', array('id' => 0, 'revision_id' => 0));
- $langcode = Language::LANGCODE_NOT_SPECIFIED;
+ $entity = entity_create('entity_test', array());
$values[0]['value'] = mt_rand(1, 127);
$entity->{$field['field_name']}->value = $values[0]['value'];
- field_attach_insert($entity);
+ $entity = $this->entitySaveReload($entity);
// Verify the field is present on load
- $entity = entity_create('entity_test', array('id' => 0, 'revision_id' => 0));
- field_attach_load('entity_test', array(0 => $entity));
$this->assertIdentical(count($entity->{$field['field_name']}), count($values), "Data in previously deleted field saves and loads correctly");
foreach ($values as $delta => $value) {
$this->assertEqual($entity->{$field['field_name']}[$delta]->value, $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
@@ -364,7 +361,11 @@ function testDeleteField() {
}
function testUpdateFieldType() {
- $field_definition = array('field_name' => 'field_type', 'type' => 'number_decimal');
+ $field_definition = array(
+ 'name' => 'field_type',
+ 'entity_type' => 'entity_test',
+ 'type' => 'number_decimal',
+ );
$field = entity_create('field_entity', $field_definition);
$field->save();
@@ -387,7 +388,8 @@ function testUpdateField() {
// systems, it makes a good test case.
$cardinality = 4;
$field = entity_create('field_entity', array(
- 'field_name' => 'field_update',
+ 'name' => 'field_update',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
'cardinality' => $cardinality,
));
@@ -400,22 +402,18 @@ function testUpdateField() {
$instance->save();
do {
- // We need a unique ID for our entity. $cardinality will do.
- $id = $cardinality;
- $entity = entity_create('entity_test', array('id' => $id, 'revision_id' => $id));
+ $entity = entity_create('entity_test', array());
// Fill in the entity with more values than $cardinality.
for ($i = 0; $i < 20; $i++) {
- $entity->field_update[$i]->value = $i;
+ // We can not use $i here because 0 values are filtered out.
+ $entity->field_update[$i]->value = $i + 1;
}
- // Save the entity.
- field_attach_insert($entity);
// Load back and assert there are $cardinality number of values.
- $entity = entity_create('entity_test', array('id' => $id, 'revision_id' => $id));
- field_attach_load('entity_test', array($id => $entity));
- $this->assertEqual(count($entity->field_update), $field->cardinality, 'Cardinality is kept');
+ $entity = $this->entitySaveReload($entity);
+ $this->assertEqual(count($entity->field_update), $field->cardinality);
// Now check the values themselves.
for ($delta = 0; $delta < $cardinality; $delta++) {
- $this->assertEqual($entity->field_update[$delta]->value, $delta, 'Value is kept');
+ $this->assertEqual($entity->field_update[$delta]->value, $delta + 1);
}
// Increase $cardinality and set the field cardinality to the new value.
$field->cardinality = ++$cardinality;
@@ -428,7 +426,8 @@ function testUpdateField() {
*/
function testUpdateFieldForbid() {
$field = entity_create('field_entity', array(
- 'field_name' => 'forbidden',
+ 'name' => 'forbidden',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
'settings' => array(
'changeable' => 0,
diff --git a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php
index e2fb2f9..3bfc7d2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php
@@ -70,7 +70,8 @@ function setUp() {
$this->cardinality = 4;
$field = array(
- 'field_name' => $this->field_name,
+ 'name' => $this->field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
'cardinality' => $this->cardinality,
);
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php
index 0f86ac5..0ce394d 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php
@@ -52,12 +52,13 @@ function setUp() {
$content_type = $content_type_info->type;
$field = array(
- 'field_name' => 'test_view_field',
+ 'name' => 'test_view_field',
+ 'entity_type' => 'node',
'type' => 'text',
);
entity_create('field_entity', $field)->save();
$instance = array(
- 'field_name' => $field['field_name'],
+ 'field_name' => $field['name'],
'entity_type' => 'node',
'bundle' => $content_type,
);
@@ -66,7 +67,7 @@ function setUp() {
// Assign display properties for the 'default' and 'teaser' view modes.
foreach (array('default', 'teaser') as $view_mode) {
entity_get_display('node', $content_type, $view_mode)
- ->setComponent($field['field_name'])
+ ->setComponent($field['name'])
->save();
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 4739d5f..03490a5 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
@@ -236,7 +236,7 @@ function testFieldAttachPrepareViewMultiple() {
*/
function testFieldAttachCache() {
// Initialize random values and a test entity.
- $entity_init = entity_create('entity_test', array('id' => 1, 'revision_id' => 1, 'type' => $this->instance['bundle']));
+ $entity_init = entity_create('entity_test', array('type' => $this->instance['bundle']));
$langcode = Language::LANGCODE_NOT_SPECIFIED;
$values = $this->_generateTestFieldValues($this->field['cardinality']);
@@ -250,87 +250,72 @@ function testFieldAttachCache() {
// Save, and check that no cache entry is present.
$entity = clone($entity_init);
$entity->{$this->field_name}->setValue($values);
- field_attach_insert($entity);
- $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on insert');
-
- // Load, and check that no cache entry is present.
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
- $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on load');
-
+ $entity = $this->entitySaveReload($entity);
+ $cid = "field:$entity_type:" . $entity->id();
+ $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on insert and load');
// Cacheable entity type.
$entity_type = 'entity_test_cache';
- $cid = "field:$entity_type:" . $entity_init->id();
- $instance_definition = $this->instance_definition;
- $instance_definition['entity_type'] = $entity_type;
- $instance_definition['bundle'] = $entity_type;
- entity_create('field_instance', $instance_definition)->save();
-
+ $this->createFieldWithInstance('_2', 'entity_test_cache');
entity_info_cache_clear();
$entity_init = entity_create($entity_type, array(
- 'id' => 1,
- 'revision_id' => 1,
'type' => $entity_type,
));
// Check that no initial cache entry is present.
+ $cid = "field:$entity_type:" . $entity->id();
$this->assertFalse(cache('field')->get($cid), 'Cached: no initial cache entry');
// Save, and check that no cache entry is present.
$entity = clone($entity_init);
- $entity->{$this->field_name} = $values;
- field_attach_insert($entity);
+ $entity->{$this->field_name_2} = $values;
+ $entity->save();
+ $cid = "field:$entity_type:" . $entity->id();
$this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on insert');
- // Load a single field, and check that no cache entry is present.
- $entity = clone($entity_init);
- $instance = field_info_instance($entity->entityType(), $this->field_name, $entity->bundle());
- field_attach_load($entity_type, array($entity->id() => $entity), FIELD_LOAD_CURRENT, array('instance' => $instance));
- $cache = cache('field')->get($cid);
- $this->assertFalse($cache, 'Cached: no cache entry on loading a single field');
-
// Load, and check that a cache entry is present with the expected values.
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+ $controller->resetCache();
+ $controller->load($entity->id());
$cache = cache('field')->get($cid);
- $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+ $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
// Update with different values, and check that the cache entry is wiped.
- $values = $this->_generateTestFieldValues($this->field['cardinality']);
- $entity = clone($entity_init);
- $entity->{$this->field_name} = $values;
- field_attach_update($entity);
+ $values = $this->_generateTestFieldValues($this->field_name_2['cardinality']);
+ $entity = entity_create($entity_type, array(
+ 'type' => $entity_type,
+ 'id' => $entity->id(),
+ ));
+ $entity->{$this->field_name_2} = $values;
+ $entity->save();
$this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on update');
// Load, and check that a cache entry is present with the expected values.
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $controller->resetCache();
+ $controller->load($entity->id());
$cache = cache('field')->get($cid);
- $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+ $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
// Create a new revision, and check that the cache entry is wiped.
- $entity_init = entity_create($entity_type, array(
- 'id' => 1,
- 'revision_id' => 2,
+ $entity = entity_create($entity_type, array(
'type' => $entity_type,
+ 'id' => $entity->id(),
));
- $values = $this->_generateTestFieldValues($this->field['cardinality']);
- $entity = clone($entity_init);
+ $values = $this->_generateTestFieldValues($this->field_name_2['cardinality']);
$entity->{$this->field_name} = $values;
- field_attach_update($entity);
- $cache = cache('field')->get($cid);
+ $entity->setNewRevision();
+ $entity->save();
$this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on new revision creation');
// Load, and check that a cache entry is present with the expected values.
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $controller->resetCache();
+ $controller->load($entity->id());
$cache = cache('field')->get($cid);
- $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+ $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
// Delete, and check that the cache entry is wiped.
- field_attach_delete($entity);
+ $entity->delete();
$this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry after delete');
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
index 5d422af..1c0c390 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -62,26 +62,22 @@ function testFieldAttachSaveLoad() {
// TODO : test empty values filtering and "compression" (store consecutive deltas).
// Preparation: create three revisions and store them in $revision array.
$values = array();
+ $entity = entity_create($entity_type, array());
for ($revision_id = 0; $revision_id < 3; $revision_id++) {
- $revision[$revision_id] = entity_create($entity_type, array('id' => 0, 'revision_id' => $revision_id));
// Note: we try to insert one extra value.
- $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
- $current_revision = $revision_id;
- // If this is the first revision do an insert.
- if (!$revision_id) {
- $revision[$revision_id]->{$this->field_name}->setValue($values[$revision_id]);
- field_attach_insert($revision[$revision_id]);
- }
- else {
- // Otherwise do an update.
- $revision[$revision_id]->{$this->field_name}->setValue($values[$revision_id]);
- field_attach_update($revision[$revision_id]);
- }
+ $current_values = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+ $entity->{$this->field_name}->setValue($current_values);
+ $entity->setNewRevision();
+ $entity->save();
+ $entity_id = $entity->id();
+ $current_revision = $entity->getRevisionId();
+ $values[$current_revision] = $current_values;
}
+ $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController($entity_type);
+ $storage_controller->resetCache();
+ $entity = $storage_controller->load($entity_id);
// Confirm current revision loads the correct data.
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0));
- field_attach_load($entity_type, array(0 => $entity));
// Number of values per field loaded equals the field cardinality.
$this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], 'Current revision: expected number of values');
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -92,9 +88,8 @@ function testFieldAttachSaveLoad() {
}
// Confirm each revision loads the correct data.
- foreach (array_keys($revision) as $revision_id) {
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => $revision_id));
- field_attach_load_revision($entity_type, array(0 => $entity));
+ foreach (array_keys($values) as $revision_id) {
+ $entity = $storage_controller->loadRevision($revision_id);
// Number of values per field loaded equals the field cardinality.
$this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -130,7 +125,11 @@ function testFieldAttachLoadMultiple() {
);
for ($i = 1; $i <= 3; $i++) {
$field_names[$i] = 'field_' . $i;
- $field = entity_create('field_entity', array('field_name' => $field_names[$i], 'type' => 'test_field'));
+ $field = entity_create('field_entity', array(
+ 'name' => $field_names[$i],
+ 'entity_type' => $entity_type,
+ 'type' => 'test_field',
+ ));
$field->save();
$field_ids[$i] = $field['uuid'];
foreach ($field_bundles_map[$i] as $bundle) {
@@ -156,11 +155,14 @@ function testFieldAttachLoadMultiple() {
$values[$index][$field_name] = mt_rand(1, 127);
$entity->$field_name->setValue(array('value' => $values[$index][$field_name]));
}
- field_attach_insert($entity);
+ $entity->enforceIsnew();
+ $entity->save();
}
// Check that a single load correctly loads field values for both entities.
- field_attach_load($entity_type, $entities);
+ $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+ $controller->resetCache();
+ $entities = $controller->loadMultiple();
foreach ($entities as $index => $entity) {
$instances = field_info_instances($entity_type, $bundles[$index]);
foreach ($instances as $field_name => $instance) {
@@ -170,165 +172,48 @@ function testFieldAttachLoadMultiple() {
$this->assertEqual($entity->{$field_name}->additional_key, 'additional_value', format_string('Entity %index: extra information was found', array('%index' => $index)));
}
}
-
- // Check that the single-field load option works.
- $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $bundles[1]));
- $instance = field_info_instance($entity->entityType(), $field_names[1], $entity->bundle());
- field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('instance' => $instance));
- $this->assertEqual($entity->{$field_names[1]}->value, $values[1][$field_names[1]], format_string('Entity %index: expected value was found.', array('%index' => 1)));
- $this->assertEqual($entity->{$field_names[1]}->additional_key, 'additional_value', format_string('Entity %index: extra information was found', array('%index' => 1)));
- $this->assert(empty($entity->{$field_names[2]}->value), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
- $this->assert(!isset($entity->{$field_names[3]}), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
- }
-
- /**
- * Test saving and loading fields using different storage backends.
- */
- function testFieldAttachSaveLoadDifferentStorage() {
- $entity_type = 'entity_test';
-
- // Create two fields using different storage backends, and their instances.
- $fields = array(
- array(
- 'field_name' => 'field_1',
- 'type' => 'test_field',
- 'cardinality' => 4,
- 'storage' => array('type' => 'field_sql_storage')
- ),
- array(
- 'field_name' => 'field_2',
- 'type' => 'test_field',
- 'cardinality' => 4,
- 'storage' => array('type' => 'field_test_storage')
- ),
- );
- foreach ($fields as $field) {
- entity_create('field_entity', $field)->save();
- $instance = array(
- 'field_name' => $field['field_name'],
- 'entity_type' => $entity_type,
- 'bundle' => $entity_type,
- );
- entity_create('field_instance', $instance)->save();
- }
-
- $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
-
- // Create entity and insert random values.
- $entity = clone($entity_init);
- $values = array();
- foreach ($fields as $field) {
- $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
- $entity->{$field['field_name']} = $values[$field['field_name']];
- }
- field_attach_insert($entity);
-
- // Check that values are loaded as expected.
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
- foreach ($fields as $field) {
- $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}->getValue(), format_string('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
- }
- }
-
- /**
- * Test storage details alteration.
- *
- * @see field_test_storage_details_alter()
- */
- function testFieldStorageDetailsAlter() {
- $field_name = 'field_test_change_my_details';
- $field = entity_create('field_entity', array(
- 'field_name' => $field_name,
- 'type' => 'test_field',
- 'cardinality' => 4,
- 'storage' => array('type' => 'field_test_storage'),
- ));
- $field->save();
- $instance = entity_create('field_instance', array(
- 'field_name' => $field_name,
- 'entity_type' => 'entity_test',
- 'bundle' => 'entity_test',
- ));
- $instance->save();
-
- // The storage details are indexed by a storage engine type.
- $this->assertTrue(array_key_exists('drupal_variables', $field['storage_details']), 'The storage type is Drupal variables.');
-
- $details = $field['storage_details']['drupal_variables'];
-
- // The field_test storage details are indexed by variable name. The details
- // are altered, so moon and mars are correct for this test.
- $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), 'Moon is available in the instance array.');
- $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), 'Mars is available in the instance array.');
-
- // Test current and revision storage details together because the columns
- // are the same.
- foreach ($field['columns'] as $column_name => $attributes) {
- $this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
- $this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
- }
}
/**
* Tests insert and update with empty or NULL fields.
*/
function testFieldAttachSaveEmptyData() {
- $entity_type = 'entity_test_rev';
+ $entity_type = 'entity_test';
$this->createFieldWithInstance('', $entity_type);
- $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
+ $entity_init = entity_create($entity_type, array('id' => 1));
// Insert: Field is NULL.
- field_cache_clear();
- $entity = clone($entity_init);
+ $entity = clone $entity_init;
$entity->{$this->field_name} = NULL;
- field_attach_insert($entity);
-
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $entity->enforceIsNew();
+ $entity = $this->entitySaveReload($entity);
$this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
// Add some real data.
- field_cache_clear();
$entity = clone($entity_init);
$values = $this->_generateTestFieldValues(1);
$entity->{$this->field_name} = $values;
- field_attach_insert($entity);
-
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $entity = $this->entitySaveReload($entity);
$this->assertEqual($entity->{$this->field_name}->getValue(), $values, 'Field data saved');
// Update: Field is NULL. Data should be wiped.
- field_cache_clear();
$entity = clone($entity_init);
$entity->{$this->field_name} = NULL;
- field_attach_update($entity);
-
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $entity = $this->entitySaveReload($entity);
$this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Update: NULL field removes existing values');
// Re-add some data.
- field_cache_clear();
$entity = clone($entity_init);
$values = $this->_generateTestFieldValues(1);
$entity->{$this->field_name} = $values;
- field_attach_update($entity);
-
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $entity = $this->entitySaveReload($entity);
$this->assertEqual($entity->{$this->field_name}->getValue(), $values, 'Field data saved');
// Update: Field is empty array. Data should be wiped.
- field_cache_clear();
$entity = clone($entity_init);
$entity->{$this->field_name} = array();
- field_attach_update($entity);
-
- $entity = clone($entity_init);
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $entity = $this->entitySaveReload($entity);
$this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Update: empty array removes existing values');
}
@@ -351,11 +236,8 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
// Insert: Field is NULL.
$entity = clone($entity_init);
$entity->getBCEntity()->{$this->field_name} = NULL;
- field_attach_insert($entity);
-
- $entity = clone($entity_init);
- $entity->getBCEntity()->{$this->field_name} = array();
- field_attach_load($entity_type, array($entity->id() => $entity));
+ $entity->enforceIsNew();
+ $entity = $this->entitySaveReload($entity);
$this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
// Verify that prepopulated field values are not overwritten by defaults.
@@ -365,58 +247,60 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
}
/**
- * Test field_attach_delete().
+ * Test entity deletion.
*/
function testFieldAttachDelete() {
$entity_type = 'entity_test_rev';
$this->createFieldWithInstance('', $entity_type);
- $rev[0] = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+ $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
+ $vids = array();
// Create revision 0
$values = $this->_generateTestFieldValues($this->field['cardinality']);
- $rev[0]->{$this->field_name} = $values;
- field_attach_insert($rev[0]);
+ $entity->{$this->field_name} = $values;
+ $entity->save();
+ $vids[] = $entity->getRevisionId();
// Create revision 1
- $rev[1] = entity_create($entity_type, array('id' => 0, 'revision_id' => 1, 'type' => $this->instance['bundle']));
- $rev[1]->{$this->field_name} = $values;
- field_attach_update($rev[1]);
+ $entity->setNewRevision();
+ $entity->save();
+ $vids[] = $entity->getRevisionId();
// Create revision 2
- $rev[2] = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
- $rev[2]->{$this->field_name} = $values;
- field_attach_update($rev[2]);
+ $entity->setNewRevision();
+ $entity->save();
+ $vids[] = $entity->getRevisionId();
+ $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+ $controller->resetCache();
// Confirm each revision loads
- foreach (array_keys($rev) as $vid) {
- $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
- field_attach_load_revision($entity_type, array(0 => $read));
- $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+ foreach ($vids as $vid) {
+ $revision = $controller->loadRevision($vid);
+ $this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
}
// Delete revision 1, confirm the other two still load.
- field_attach_delete_revision($rev[1]);
- foreach (array(0, 2) as $vid) {
- $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
- field_attach_load_revision($entity_type, array(0 => $read));
- $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+ $controller->deleteRevision($vids[1]);
+ $controller->resetCache();
+ foreach (array(0, 2) as $key) {
+ $vid = $vids[$key];
+ $revision = $controller->loadRevision($vid);
+ $this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
}
// Confirm the current revision still loads
- $read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
- field_attach_load($entity_type, array(0 => $read));
- $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
+ $controller->resetCache();
+ $current = $controller->load($entity->id());
+ $this->assertEqual(count($current->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
// Delete all field data, confirm nothing loads
- field_attach_delete($rev[2]);
+ $entity->delete();
+ $controller->resetCache();
foreach (array(0, 1, 2) as $vid) {
- $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
- field_attach_load_revision($entity_type, array(0 => $read));
- $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), "The test entity revision $vid is deleted.");
+ $revision = $controller->loadRevision($vid);
+ $this->assertFalse($revision);
}
- $read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
- field_attach_load($entity_type, array(0 => $read));
- $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), 'The test entity current revision is deleted.');
+ $this->assertFalse($controller->load($entity->id()));
}
/**
@@ -435,14 +319,12 @@ function testEntityCreateRenameBundle() {
entity_create('field_instance', $this->instance_definition)->save();
// Save an entity with data in the field.
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+ $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity->{$this->field_name} = $values;
- field_attach_insert($entity);
// Verify the field data is present on load.
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
- field_attach_load($entity_type, array(0 => $entity));
+ $entity = $this->entitySaveReload($entity);
$this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Data is retrieved for the new bundle");
// Rename the bundle.
@@ -454,8 +336,9 @@ function testEntityCreateRenameBundle() {
$this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
// Verify the field data is present on load.
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
- field_attach_load($entity_type, array(0 => $entity));
+ $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+ $controller->resetCache();
+ $entity = $controller->load($entity->id());
$this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Bundle name has been updated in the field storage");
}
@@ -476,7 +359,12 @@ function testEntityDeleteBundle() {
// Create a second field for the test bundle
$field_name = drupal_strtolower($this->randomName() . '_field_name');
- $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
+ $field = array(
+ 'name' => $field_name,
+ 'entity_type' => $entity_type,
+ 'type' => 'test_field',
+ 'cardinality' => 1,
+ );
entity_create('field_entity', $field)->save();
$instance = array(
'field_name' => $field_name,
@@ -489,15 +377,13 @@ function testEntityDeleteBundle() {
entity_create('field_instance', $instance)->save();
// Save an entity with data for both fields
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+ $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity->{$this->field_name} = $values;
$entity->{$field_name} = $this->_generateTestFieldValues(1);
- field_attach_insert($entity);
+ $entity = $this->entitySaveReload($entity);
// Verify the fields are present on load
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
- field_attach_load($entity_type, array(0 => $entity));
$this->assertEqual(count($entity->{$this->field_name}), 4, 'First field got loaded');
$this->assertEqual(count($entity->{$field_name}), 1, 'Second field got loaded');
@@ -505,8 +391,10 @@ function testEntityDeleteBundle() {
entity_test_delete_bundle($this->instance['bundle'], $entity_type);
// Verify no data gets loaded
- $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
- field_attach_load($entity_type, array(0 => $entity));
+ $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+ $controller->resetCache();
+ $entity= $controller->load($entity->id());
+
$this->assertTrue(empty($entity->{$this->field_name}), 'No data for first field');
$this->assertTrue(empty($entity->{$field_name}), 'No data for second field');
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php
index b1a88fb..b6697e7 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php
@@ -26,11 +26,13 @@ public static function getInfo() {
* Tests creating fields and instances during default config import.
*/
function testImportCreateDefault() {
- $field_id = 'field_test_import';
- $instance_id = "entity_test.entity_test.$field_id";
- $field_id_2 = 'field_test_import_2';
- $instance_id_2a = "entity_test.entity_test.$field_id_2";
- $instance_id_2b = "entity_test.entity_test.$field_id_2";
+ $field_name = 'field_test_import';
+ $field_id = "entity_test.$field_name";
+ $instance_id = "entity_test.entity_test.$field_name";
+ $field_name_2 = 'field_test_import_2';
+ $field_id_2 = "entity_test.$field_name_2";
+ $instance_id_2a = "entity_test.entity_test.$field_name_2";
+ $instance_id_2b = "entity_test.test_bundle.$field_name_2";
// Check that the fields and instances do not exist yet.
$this->assertFalse(entity_load('field_entity', $field_id));
@@ -72,15 +74,17 @@ function testImportCreateDefault() {
*/
function testImportCreate() {
// One field with one field instance.
- $field_id = 'field_test_import_staging';
- $instance_id = "entity_test.entity_test.$field_id";
+ $field_name = 'field_test_import_staging';
+ $field_id = "entity_test.$field_name";
+ $instance_id = "entity_test.entity_test.$field_name";
$field_config_name = "field.field.$field_id";
$instance_config_name = "field.instance.$instance_id";
// One field with two field instances.
- $field_id_2 = 'field_test_import_staging_2';
- $instance_id_2a = "entity_test.test_bundle.$field_id_2";
- $instance_id_2b = "entity_test.test_bundle_2.$field_id_2";
+ $field_name_2 = 'field_test_import_staging_2';
+ $field_id_2 = "entity_test.$field_name_2";
+ $instance_id_2a = "entity_test.test_bundle.$field_name_2";
+ $instance_id_2b = "entity_test.test_bundle_2.$field_name_2";
$field_config_name_2 = "field.field.$field_id_2";
$instance_config_name_2a = "field.instance.$instance_id_2a";
$instance_config_name_2b = "field.instance.$instance_id_2b";
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php
index 216d36a..23ae199 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php
@@ -31,11 +31,13 @@ public static function getInfo() {
* Tests deleting fields and instances as part of config import.
*/
public function testImportDelete() {
- $field_id = 'field_test_import';
- $field_id_2 = 'field_test_import_2';
- $instance_id = "entity_test.test_bundle.$field_id";
- $instance_id_2a = "entity_test.test_bundle.$field_id_2";
- $instance_id_2b = "entity_test.test_bundle_2.$field_id_2";
+ $field_name = 'field_test_import';
+ $field_id = "entity_test.$field_name";
+ $field_name_2 = 'field_test_import_2';
+ $field_id_2 = "entity_test.$field_name_2";
+ $instance_id = "entity_test.test_bundle.$field_name";
+ $instance_id_2a = "entity_test.test_bundle.$field_name_2";
+ $instance_id_2b = "entity_test.test_bundle_2.$field_name_2";
$field_config_name = "field.field.$field_id";
$field_config_name_2 = "field.field.$field_id_2";
$instance_config_name = "field.instance.$instance_id";
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 70f99f1..9c433aa 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -32,15 +32,6 @@ function testFieldInfo() {
$this->assertEqual($info[$t_key]['provider'], 'field_test', 'Field type field_test module appears.');
}
- $storage_info = field_test_field_storage_info();
- $info = field_info_storage_types();
- foreach ($storage_info as $s_key => $storage) {
- foreach ($storage as $key => $val) {
- $this->assertEqual($info[$s_key][$key], $val, format_string('Storage type %s_key key %key is %value', array('%s_key' => $s_key, '%key' => $key, '%value' => print_r($val, TRUE))));
- }
- $this->assertEqual($info[$s_key]['module'], 'field_test', 'Storage type field_test module appears.');
- }
-
// Verify that no unexpected instances exist.
$instances = field_info_instances('entity_test');
$expected = array();
@@ -51,25 +42,26 @@ function testFieldInfo() {
// Create a field, verify it shows up.
$core_fields = field_info_fields();
$field = entity_create('field_entity', array(
- 'field_name' => drupal_strtolower($this->randomName()),
+ 'name' => drupal_strtolower($this->randomName()),
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
));
$field->save();
$fields = field_info_fields();
$this->assertEqual(count($fields), count($core_fields) + 1, 'One new field exists');
- $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], 'info fields contains field name');
- $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], 'info fields contains field type');
- $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', 'info fields contains field module');
+ $this->assertEqual($fields[$field['uuid']]['field_name'], $field['field_name'], 'info fields contains field name');
+ $this->assertEqual($fields[$field['uuid']]['type'], $field['type'], 'info fields contains field type');
+ $this->assertEqual($fields[$field['uuid']]['module'], 'field_test', 'info fields contains field module');
$settings = array('test_field_setting' => 'dummy test string');
foreach ($settings as $key => $val) {
- $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, format_string('Field setting %key has correct default value %value', array('%key' => $key, '%value' => $val)));
+ $this->assertEqual($fields[$field['uuid']]['settings'][$key], $val, format_string('Field setting %key has correct default value %value', array('%key' => $key, '%value' => $val)));
}
- $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, 'info fields contains cardinality 1');
- $this->assertEqual($fields[$field['field_name']]['active'], TRUE, 'info fields contains active 1');
+ $this->assertEqual($fields[$field['uuid']]['cardinality'], 1, 'info fields contains cardinality 1');
+ $this->assertEqual($fields[$field['uuid']]['active'], TRUE, 'info fields contains active 1');
// Create an instance, verify that it shows up
$instance_definition = array(
- 'field_name' => $field['field_name'],
+ 'field_name' => $field['name'],
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'label' => $this->randomName(),
@@ -121,7 +113,8 @@ function testFieldInfo() {
*/
function testFieldPrepare() {
$field_definition = array(
- 'field_name' => 'field',
+ 'name' => 'field',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
$field = entity_create('field_entity', $field_definition);
@@ -136,7 +129,7 @@ function testFieldPrepare() {
field_info_cache_clear();
// Read the field back.
- $field = field_info_field($field_definition['field_name']);
+ $field = field_info_field('entity_test', $field_definition['name']);
// Check that all expected settings are in place.
$field_type = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field_definition['type']);
@@ -148,12 +141,13 @@ function testFieldPrepare() {
*/
function testInstancePrepare() {
$field_definition = array(
- 'field_name' => 'field',
+ 'name' => 'field',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
entity_create('field_entity', $field_definition)->save();
$instance_definition = array(
- 'field_name' => $field_definition['field_name'],
+ 'field_name' => $field_definition['name'],
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
);
@@ -182,8 +176,10 @@ function testInstancePrepare() {
function testInstanceDisabledEntityType() {
// For this test the field type and the entity type must be exposed by
// different modules.
+ $this->enableModules(array('node', 'comment'));
$field_definition = array(
- 'field_name' => 'field',
+ 'name' => 'field',
+ 'entity_type' => 'comment',
'type' => 'test_field',
);
entity_create('field_entity', $field_definition)->save();
@@ -192,7 +188,7 @@ function testInstanceDisabledEntityType() {
'entity_type' => 'comment',
'bundle' => 'comment_node_article',
);
- entity_create('field_instance', $instance_definition)->save();
+ entity_create('field_instance', $instance_definition);
// Disable coment module. This clears field_info cache.
module_disable(array('comment'));
@@ -212,11 +208,18 @@ function testFieldMap() {
// Create a couple fields.
$fields = array(
array(
- 'field_name' => 'field_1',
+ 'name' => 'field_1',
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
),
array(
- 'field_name' => 'field_2',
+ 'name' => 'field_2',
+ 'entity_type' => 'entity_test',
+ 'type' => 'hidden_test_field',
+ ),
+ array(
+ 'name' => 'field_2',
+ 'entity_type' => 'entity_test_cache',
'type' => 'hidden_test_field',
),
);
@@ -252,17 +255,20 @@ function testFieldMap() {
}
$expected = array(
- 'field_1' => array(
- 'type' => 'test_field',
- 'bundles' => array(
- 'entity_test' => array('entity_test', 'test_bundle_2'),
+ 'entity_test' => array(
+ 'field_1' => array(
+ 'type' => 'test_field',
+ 'bundles' => array('entity_test', 'test_bundle_2'),
+ ),
+ 'field_2' => array(
+ 'type' => 'hidden_test_field',
+ 'bundles' => array('entity_test'),
),
),
- 'field_2' => array(
- 'type' => 'hidden_test_field',
- 'bundles' => array(
- 'entity_test' => array('entity_test'),
- 'entity_test_cache' => array('entity_test'),
+ 'entity_test_cache' => array(
+ 'field_2' => array(
+ 'type' => 'hidden_test_field',
+ 'bundles' => array('entity_test')
),
),
);
@@ -293,12 +299,13 @@ function testFieldInfoCache() {
// field_info_fields().
$field_name = drupal_strtolower($this->randomName());
$field = entity_create('field_entity', array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
));
$field->save();
$fields = field_info_fields();
- $this->assertTrue(isset($fields[$field_name]), 'The test field is initially found in the array returned by field_info_fields().');
+ $this->assertTrue(isset($fields[$field->uuid]), 'The test field is initially found in the array returned by field_info_fields().');
// Now rebuild the field info cache, and set a variable which will cause
// the cache to be cleared while it's being rebuilt; see
@@ -307,7 +314,7 @@ function testFieldInfoCache() {
field_info_cache_clear();
\Drupal::state()->set('field_test.clear_info_cache_in_hook_entity_info', TRUE);
$fields = field_info_fields();
- $this->assertTrue(isset($fields[$field_name]), 'The test field is found in the array returned by field_info_fields() even if its cache is cleared while being rebuilt.');
+ $this->assertTrue(isset($fields[$field->uuid]), 'The test field is found in the array returned by field_info_fields() even if its cache is cleared while being rebuilt.');
}
/**
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
index 0ba7eb5..8c4afcc 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
@@ -44,13 +44,14 @@ function setUp() {
parent::setUp();
$this->field_definition = array(
- 'field_name' => drupal_strtolower($this->randomName()),
+ 'name' => drupal_strtolower($this->randomName()),
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
$this->field = entity_create('field_entity', $this->field_definition);
$this->field->save();
$this->instance_definition = array(
- 'field_name' => $this->field['field_name'],
+ 'field_name' => $this->field->name,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
);
@@ -103,40 +104,6 @@ function testCreateFieldInstance() {
$this->pass(t('Cannot create an instance of a non-existing field.'));
}
- // Create a field restricted to a specific entity type.
- $field_restricted_definition = array(
- 'field_name' => drupal_strtolower($this->randomName()),
- 'type' => 'test_field',
- 'entity_types' => array('entity_test_cache'),
- );
- $field_restricted = entity_create('field_entity', $field_restricted_definition);
- $field_restricted->save();
-
- // Check that an instance can be added to an entity type allowed
- // by the field.
- try {
- $instance = $this->instance_definition;
- $instance['field_name'] = $field_restricted_definition['field_name'];
- $instance['entity_type'] = 'entity_test_cache';
- entity_create('field_instance', $instance)->save();
- $this->pass(t('Can create an instance on an entity type allowed by the field.'));
- }
- catch (FieldException $e) {
- $this->fail(t('Can create an instance on an entity type allowed by the field.'));
- }
-
- // Check that an instance cannot be added to an entity type
- // forbidden by the field.
- try {
- $instance = $this->instance_definition;
- $instance['field_name'] = $field_restricted_definition['field_name'];
- entity_create('field_instance', $instance)->save();
- $this->fail(t('Cannot create an instance on an entity type forbidden by the field.'));
- }
- catch (FieldException $e) {
- $this->pass(t('Cannot create an instance on an entity type forbidden by the field.'));
- }
-
// TODO: test other failures.
}
@@ -212,7 +179,7 @@ function testDeleteFieldInstance() {
$another_instance->delete();
$deleted_fields = \Drupal::state()->get('field.field.deleted');
$this->assertTrue(isset($deleted_fields[$another_instance['field_id']]), 'A deleted field is marked for deletion.');
- $field = field_read_field($another_instance['field_name']);
+ $field = field_read_field($another_instance['entity_type'], $another_instance['field_name']);
$this->assertFalse($field, 'The field marked to be deleted is not found anymore in the configuration.');
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
index 4580e7b..7ff7c9d 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
@@ -20,7 +20,7 @@
*
* @var array
*/
- public static $modules = array('user', 'entity', 'system', 'field', 'text', 'field_sql_storage', 'entity_test', 'field_test');
+ public static $modules = array('user', 'entity', 'system', 'field', 'text', 'entity_test', 'field_test');
/**
* A string for assert raw and text helper methods.
@@ -65,7 +65,12 @@ function createFieldWithInstance($suffix = '', $entity_type = 'entity_test', $bu
$instance_definition = 'instance_definition' . $suffix;
$this->$field_name = drupal_strtolower($this->randomName() . '_field_name' . $suffix);
- $this->$field = entity_create('field_entity', array('field_name' => $this->$field_name, 'type' => 'test_field', 'cardinality' => 4));
+ $this->$field = entity_create('field_entity', array(
+ 'name' => $this->$field_name,
+ 'entity_type' => $entity_type,
+ 'type' => 'test_field',
+ 'cardinality' => 4,
+ ));
$this->$field->save();
$this->$field_id = $this->{$field}['uuid'];
$this->$instance_definition = array(
@@ -92,6 +97,22 @@ function createFieldWithInstance($suffix = '', $entity_type = 'entity_test', $bu
}
/**
+ * Saves and reloads an entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity to save.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface
+ * The entity, freshly reloaded from storage.
+ */
+ protected function entitySaveReload(EntityInterface $entity) {
+ $entity->save();
+ $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+ $controller->resetCache();
+ return $controller->load($entity->id());
+ }
+
+ /**
* Generate random values for a field_test field.
*
* @param $cardinality
@@ -125,9 +146,13 @@ function _generateTestFieldValues($cardinality) {
* (Optional) the name of the column to check.
*/
function assertFieldValues(EntityInterface $entity, $field_name, $langcode, $expected_values, $column = 'value') {
- $e = clone $entity;
- field_attach_load('entity_test', array($e->id() => $e));
- $values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
+ // Re-load the entity to make sure we have the latest changes.
+ entity_get_controller($entity->entityType())->resetCache(array($entity->id()));
+ $e = entity_load($entity->entityType(), $entity->id());
+ $field = $values = $e->getTranslation($langcode)->$field_name;
+ // Filter out empty values so that they don't mess with the assertions.
+ $field->filterEmptyValues();
+ $values = $field->getValue();
$this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
foreach ($expected_values as $key => $value) {
$this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
index b263859..4ad739a 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
@@ -61,9 +61,23 @@ function setUp() {
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
$this->drupalLogin($web_user);
- $this->field_single = array('field_name' => 'field_single', 'type' => 'test_field');
- $this->field_multiple = array('field_name' => 'field_multiple', 'type' => 'test_field', 'cardinality' => 4);
- $this->field_unlimited = array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
+ $this->field_single = array(
+ 'name' => 'field_single',
+ 'entity_type' => 'entity_test',
+ 'type' => 'test_field',
+ );
+ $this->field_multiple = array(
+ 'name' => 'field_multiple',
+ 'entity_type' => 'entity_test',
+ 'type' => 'test_field',
+ 'cardinality' => 4,
+ );
+ $this->field_unlimited = array(
+ 'name' => 'field_unlimited',
+ 'entity_type' => 'entity_test',
+ 'type' => 'test_field',
+ 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+ );
$this->instance = array(
'entity_type' => 'entity_test',
@@ -79,7 +93,7 @@ function setUp() {
function testFieldFormSingle() {
$field = $this->field_single;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
entity_create('field_entity', $field)->save();
entity_create('field_instance', $this->instance)->save();
@@ -162,7 +176,7 @@ function testFieldFormSingle() {
*/
function testFieldFormDefaultValue() {
$field = $this->field_single;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
$default = rand(1, 127);
$this->instance['default_value'] = array(array('value' => $default));
@@ -194,7 +208,7 @@ function testFieldFormDefaultValue() {
function testFieldFormSingleRequired() {
$field = $this->field_single;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
$this->instance['required'] = TRUE;
entity_create('field_entity', $field)->save();
@@ -244,7 +258,7 @@ function testFieldFormSingleRequired() {
function testFieldFormUnlimited() {
$field = $this->field_unlimited;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
entity_create('field_entity', $field)->save();
entity_create('field_instance', $this->instance)->save();
@@ -331,7 +345,7 @@ function testFieldFormUnlimited() {
function testFieldFormMultivalueWithRequiredRadio() {
// Create a multivalue test field.
$field = $this->field_unlimited;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
entity_create('field_entity', $field)->save();
entity_create('field_instance', $this->instance)->save();
@@ -342,7 +356,8 @@ function testFieldFormMultivalueWithRequiredRadio() {
// Add a required radio field.
entity_create('field_entity', array(
- 'field_name' => 'required_radio_test',
+ 'name' => 'required_radio_test',
+ 'entity_type' => 'entity_test',
'type' => 'list_text',
'settings' => array(
'allowed_values' => array('yes' => 'yes', 'no' => 'no'),
@@ -378,7 +393,7 @@ function testFieldFormMultivalueWithRequiredRadio() {
function testFieldFormJSAddMore() {
$field = $this->field_unlimited;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
entity_create('field_entity', $field)->save();
entity_create('field_instance', $this->instance)->save();
@@ -440,7 +455,7 @@ function testFieldFormMultipleWidget() {
// Create a field with fixed cardinality and an instance using a multiple
// widget.
$field = $this->field_multiple;
- $field_name = $field['field_name'];
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
entity_create('field_entity', $field)->save();
entity_create('field_instance', $this->instance)->save();
@@ -485,10 +500,11 @@ function testFieldFormMultipleWidget() {
* Tests fields with no 'edit' access.
*/
function testFieldFormAccess() {
+ $entity_type = 'entity_test_rev';
// Create a "regular" field.
$field = $this->field_single;
- $field_name = $field['field_name'];
- $entity_type = 'entity_test_rev';
+ $field['entity_type'] = $entity_type;
+ $field_name = $field['name'];
$instance = $this->instance;
$instance['field_name'] = $field_name;
$instance['entity_type'] = $entity_type;
@@ -501,10 +517,11 @@ function testFieldFormAccess() {
// Create a field with no edit access - see field_test_field_access().
$field_no_access = array(
- 'field_name' => 'field_no_edit_access',
+ 'name' => 'field_no_edit_access',
+ 'entity_type' => $entity_type,
'type' => 'test_field',
);
- $field_name_no_access = $field_no_access['field_name'];
+ $field_name_no_access = $field_no_access['name'];
$instance_no_access = array(
'field_name' => $field_name_no_access,
'entity_type' => $entity_type,
@@ -577,7 +594,8 @@ function testFieldFormAccess() {
function testFieldFormHiddenWidget() {
$entity_type = 'entity_test_rev';
$field = $this->field_single;
- $field_name = $field['field_name'];
+ $field['entity_type'] = $entity_type;
+ $field_name = $field['name'];
$this->instance['field_name'] = $field_name;
$this->instance['default_value'] = array(0 => array('value' => 99));
$this->instance['entity_type'] = $entity_type;
diff --git a/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php b/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php
index 3d180f6..e812ff5 100644
--- a/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php
@@ -33,8 +33,17 @@ public function setUp() {
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
$this->drupalLogin($web_user);
- $this->field_single = array('field_name' => 'field_single', 'type' => 'test_field');
- $this->field_unlimited = array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
+ $this->field_single = array(
+ 'name' => 'field_single',
+ 'entity_type' => 'entity_test',
+ 'type' => 'test_field',
+ );
+ $this->field_unlimited = array(
+ 'name' => 'field_unlimited',
+ 'entity_type' => 'entity_test',
+ 'type' => 'test_field',
+ 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+ );
$this->instance = array(
'entity_type' => 'entity_test',
diff --git a/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php b/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php
index 6f7f3a0..b54174f 100644
--- a/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php
@@ -42,7 +42,8 @@ public function setUp() {
// Create an field field and instance for validation.
$field = array(
- 'field_name' => $this->field_name,
+ 'name' => $this->field_name,
+ 'entity_type' => 'entity_test',
'type' => 'shape',
);
entity_create('field_entity', $field)->save();
diff --git a/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php b/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php
index e8b85d1..b4c4aa4 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php
@@ -42,7 +42,8 @@ public function setUp() {
// Create an field field and instance for validation.
$field = array(
- 'field_name' => $this->field_name,
+ 'name' => $this->field_name,
+ 'entity_type' => 'entity_test',
'type' => 'test_field',
);
entity_create('field_entity', $field)->save();
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
index 85c14e2..0830af4 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
@@ -85,13 +85,14 @@ function setUp() {
$this->entity_type = 'entity_test';
$this->field_definition = array(
- 'field_name' => $this->field_name,
+ 'name' => $this->field_name,
+ 'entity_type' => $this->entity_type,
'type' => 'test_field',
'cardinality' => 4,
'translatable' => TRUE,
);
entity_create('field_entity', $this->field_definition)->save();
- $this->field = field_read_field($this->field_name);
+ $this->field = field_read_field($this->entity_type, $this->field_name);
$this->instance_definition = array(
'field_name' => $this->field_name,
@@ -153,8 +154,7 @@ function testTranslatableFieldSaveLoad() {
// Prepare the field translations.
$entity_type = 'entity_test';
field_test_entity_info_translatable($entity_type, TRUE);
- $id = $revision_id = 1;
- $entity = entity_create($entity_type, array('id' => $id, 'revision_id' => $revision_id, 'type' => $this->instance['bundle']));
+ $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
$field_translations = array();
$available_langcodes = field_available_languages($entity_type, $this->field);
$this->assertTrue(count($available_langcodes) > 1, 'Field is translatable.');
@@ -165,10 +165,7 @@ function testTranslatableFieldSaveLoad() {
}
// Save and reload the field translations.
- field_attach_insert($entity);
- $entity = entity_create($entity_type, array('id' => $id, 'revision_id' => $revision_id, 'type' => $this->instance['bundle']));
- $entity->langcode->value = reset($available_langcodes);
- field_attach_load($entity_type, array($id => $entity));
+ $entity = $this->entitySaveReload($entity);
// Check if the correct values were saved/loaded.
foreach ($field_translations as $langcode => $items) {
@@ -182,7 +179,7 @@ function testTranslatableFieldSaveLoad() {
// Test default values.
$field_name_default = drupal_strtolower($this->randomName() . '_field_name');
$field_definition = $this->field_definition;
- $field_definition['field_name'] = $field_name_default;
+ $field_definition['name'] = $field_name_default;
entity_create('field_entity', $field_definition)->save();
$instance_definition = $this->instance_definition;
@@ -197,9 +194,7 @@ function testTranslatableFieldSaveLoad() {
asort($translation_langcodes);
$translation_langcodes = array_values($translation_langcodes);
- $id++;
- $revision_id++;
- $values = array('id' => $id, 'revision_id' => $revision_id, 'type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
+ $values = array('type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
$entity = entity_create($entity_type, $values);
foreach ($translation_langcodes as $langcode) {
$values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
@@ -217,9 +212,7 @@ function testTranslatableFieldSaveLoad() {
// Check that explicit empty values are not overridden with default values.
foreach (array(NULL, array()) as $empty_items) {
- $id++;
- $revision_id++;
- $values = array('id' => $id, 'revision_id' => $revision_id, 'type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
+ $values = array('type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
$entity = entity_create($entity_type, $values);
foreach ($translation_langcodes as $langcode) {
$values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
@@ -244,7 +237,8 @@ function testFieldDisplayLanguage() {
// We need an additional field here to properly test display language
// suggestions.
$field = array(
- 'field_name' => $field_name,
+ 'name' => $field_name,
+ 'entity_type' => $entity_type,
'type' => 'test_field',
'cardinality' => 2,
'translatable' => TRUE,
@@ -252,7 +246,7 @@ function testFieldDisplayLanguage() {
entity_create('field_entity', $field)->save();
$instance = array(
- 'field_name' => $field['field_name'],
+ 'field_name' => $field['name'],
'entity_type' => $entity_type,
'bundle' => 'entity_test',
);
@@ -272,7 +266,7 @@ function testFieldDisplayLanguage() {
// enabled.
foreach ($instances as $instance) {
$field_name = $instance['field_name'];
- $field = field_info_field($field_name);
+ $field = $instance->getField();
do {
// Index 0 is reserved for the requested language, this way we ensure
// that no field is actually populated with it.
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
index 69b4c48..d4b95da 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
@@ -33,7 +33,7 @@ class TranslationWebTest extends FieldTestBase {
*
* @var string
*/
- protected $entity_type = 'test_entity';
+ protected $entity_type = 'entity_test_rev';
/**
* The field to use in this test.
@@ -62,16 +62,15 @@ function setUp() {
$this->field_name = drupal_strtolower($this->randomName() . '_field_name');
- $this->entity_type = 'entity_test_rev';
-
$field = array(
- 'field_name' => $this->field_name,
+ 'name' => $this->field_name,
+ 'entity_type' => $this->entity_type,
'type' => 'test_field',
'cardinality' => 4,
'translatable' => TRUE,
);
entity_create('field_entity', $field)->save();
- $this->field = field_read_field($this->field_name);
+ $this->field = field_read_field($this->entity_type, $this->field_name);
$instance = array(
'field_name' => $this->field_name,
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
index 4bf2625..1f60380 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
@@ -6,6 +6,7 @@
*/
namespace Drupal\field\Tests\Views;
+use Drupal\Core\Entity\DatabaseStorageController;
/**
* Test the produced views_data.
@@ -28,9 +29,9 @@ public static function getInfo() {
function setUp() {
parent::setUp();
- $field_names = $this->setUpFields();
+ $field_names = $this->setUpFields(1);
- // The first one will be attached to nodes only.
+ // Attach the field to nodes only.
$instance = array(
'field_name' => $field_names[0],
'entity_type' => 'node',
@@ -38,33 +39,10 @@ function setUp() {
);
entity_create('field_instance', $instance)->save();
- // The second one will be attached to users only.
- $instance = array(
- 'field_name' => $field_names[1],
- 'entity_type' => 'user',
- 'bundle' => 'user',
- );
- entity_create('field_instance', $instance)->save();
-
- // The third will be attached to both nodes and users.
- $instance = array(
- 'field_name' => $field_names[2],
- 'entity_type' => 'node',
- 'bundle' => 'page',
- );
- entity_create('field_instance', $instance)->save();
- $instance = array(
- 'field_name' => $field_names[2],
- 'entity_type' => 'user',
- 'bundle' => 'user',
- );
- entity_create('field_instance', $instance)->save();
-
// Now create some example nodes/users for the view result.
for ($i = 0; $i < 5; $i++) {
$edit = array(
- 'field_name_0' => array((array('value' => $this->randomName()))),
- 'field_name_2' => array((array('value' => $this->randomName()))),
+ $field_names[0] => array((array('value' => $this->randomName()))),
);
$nodes[] = $this->drupalCreateNode($edit);
}
@@ -84,8 +62,8 @@ function testViewsData() {
// Check the table and the joins of the first field.
// Attached to node only.
$field = $this->fields[0];
- $current_table = _field_sql_storage_tablename($field);
- $revision_table = _field_sql_storage_revision_tablename($field);
+ $current_table = DatabaseStorageController::_fieldTableName($field);
+ $revision_table = DatabaseStorageController::_fieldRevisionTableName($field);
$data[$current_table] = $views_data->get($current_table);
$data[$revision_table] = $views_data->get($revision_table);
@@ -99,7 +77,6 @@ function testViewsData() {
'left_field' => 'nid',
'field' => 'entity_id',
'extra' => array(
- array('field' => 'entity_type', 'value' => 'node'),
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
),
);
@@ -108,54 +85,10 @@ function testViewsData() {
'left_field' => 'vid',
'field' => 'revision_id',
'extra' => array(
- array('field' => 'entity_type', 'value' => 'node'),
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
),
);
$this->assertEqual($expected_join, $data[$revision_table]['table']['join']['node_field_revision']);
-
- // Check the table and the joins of the second field.
- // Attached to both node and user.
- $field_2 = $this->fields[2];
- $current_table_2 = _field_sql_storage_tablename($field_2);
- $revision_table_2 = _field_sql_storage_revision_tablename($field_2);
- $data[$current_table_2] = $views_data->get($current_table_2);
- $data[$revision_table_2] = $views_data->get($revision_table_2);
-
- $this->assertTrue(isset($data[$current_table_2]));
- $this->assertTrue(isset($data[$revision_table_2]));
- // The second field should join against both node and users.
- $this->assertTrue(isset($data[$current_table_2]['table']['join']['node']));
- $this->assertTrue(isset($data[$revision_table_2]['table']['join']['node_field_revision']));
- $this->assertTrue(isset($data[$current_table_2]['table']['join']['users']));
-
- $expected_join = array(
- 'left_field' => 'nid',
- 'field' => 'entity_id',
- 'extra' => array(
- array('field' => 'entity_type', 'value' => 'node'),
- array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
- )
- );
- $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['node']);
- $expected_join = array(
- 'left_field' => 'vid',
- 'field' => 'revision_id',
- 'extra' => array(
- array('field' => 'entity_type', 'value' => 'node'),
- array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
- )
- );
- $this->assertEqual($expected_join, $data[$revision_table_2]['table']['join']['node_field_revision']);
- $expected_join = array(
- 'left_field' => 'uid',
- 'field' => 'entity_id',
- 'extra' => array(
- array('field' => 'entity_type', 'value' => 'user'),
- array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
- )
- );
- $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['users']);
}
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
index 3cba44c..2f8e5a6 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
@@ -59,7 +59,11 @@ function setUpFields($amount = 3) {
$field_names = array();
for ($i = 0; $i < $amount; $i++) {
$field_names[$i] = 'field_name_' . $i;
- $field = array('field_name' => $field_names[$i], 'type' => 'text');
+ $field = array(
+ 'name' => $field_names[$i],
+ 'entity_type' => 'node',
+ 'type' => 'text',
+ );
$this->fields[$i] = $field = entity_create('field_entity', $field);
$field->save();
@@ -70,7 +74,7 @@ function setUpFields($amount = 3) {
function setUpInstances($bundle = 'page') {
foreach ($this->fields as $key => $field) {
$instance = array(
- 'field_name' => $field['field_name'],
+ 'field_name' => $field['name'],
'entity_type' => 'node',
'bundle' => 'page',
);
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
index c42950d..985dc7f 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
@@ -47,10 +47,20 @@ protected function setUp() {
$this->setUpFields(3);
// Setup a field with cardinality > 1.
- $this->fields[3] = $field = entity_create('field_entity', array('field_name' => 'field_name_3', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED));
+ $this->fields[3] = $field = entity_create('field_entity', array(
+ 'name' => 'field_name_3',
+ 'entity_type' => 'node',
+ 'type' => 'text',
+ 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+ ));
$field->save();
// Setup a field that will have no value.
- $this->fields[4] = $field = entity_create('field_entity', array('field_name' => 'field_name_4', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED));
+ $this->fields[4] = $field = entity_create('field_entity', array(
+ 'name' => 'field_name_4',
+ 'entity_type' => 'node',
+ 'type' => 'text',
+ 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+ ));
$field->save();
$this->setUpInstances();
@@ -86,7 +96,7 @@ protected function prepareView(ViewExecutable $view) {
$view->initDisplay();
foreach ($this->fields as $key => $field) {
$view->display_handler->options['fields'][$field['field_name']]['id'] = $field['field_name'];
- $view->display_handler->options['fields'][$field['field_name']]['table'] = 'field_data_' . $field['field_name'];
+ $view->display_handler->options['fields'][$field['field_name']]['table'] = 'node__' . $field['field_name'];
$view->display_handler->options['fields'][$field['field_name']]['field'] = $field['field_name'];
}
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php
index 6d4ed4a..0b5400d 100644
--- a/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php
@@ -21,7 +21,6 @@ class reEnableModuleFieldTest extends WebTestBase {
*/
public static $modules = array(
'field',
- 'field_sql_storage',
'node',
// We use telephone module instead of test_field because test_field is
// hidden and does not display on the admin/modules page.
@@ -51,7 +50,8 @@ function testReEnabledField() {
// Add a telephone field to the article content type.
$field = entity_create('field_entity', array(
- 'field_name' => 'field_telephone',
+ 'name' => 'field_telephone',
+ 'entity_type' => 'node',
'type' => 'telephone',
));
$field->save();
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 800f4d4..3ab0c7e 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -18,7 +18,6 @@
require_once __DIR__ . '/field_test.entity.inc';
require_once __DIR__ . '/field_test.field.inc';
-require_once __DIR__ . '/field_test.storage.inc';
/**
* Implements hook_permission().
diff --git a/core/modules/field/tests/modules/field_test/field_test.storage.inc b/core/modules/field/tests/modules/field_test/field_test.storage.inc
index 66c9911..3bbbae2 100644
--- a/core/modules/field/tests/modules/field_test/field_test.storage.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.storage.inc
@@ -195,7 +195,7 @@ function field_test_field_storage_delete(EntityInterface $entity, $fields) {
// does, is highly inefficient in our case...
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
if (isset($fields[$instance['field_id']])) {
- $field = field_info_field_by_id($instance['field_id']);
+ $field = $instance->getField();
field_test_field_storage_purge($entity, $field, $instance);
}
}
@@ -403,7 +403,7 @@ function field_test_field_storage_delete_field($field) {
function field_test_field_storage_delete_instance($instance) {
$data = _field_test_storage_data();
- $field = field_info_field($instance['field_name']);
+ $field = $instance->getField();
$field_data = &$data[$field['uuid']];
foreach (array('current', 'revisions') as $sub_table) {
foreach ($field_data[$sub_table] as &$row) {
@@ -424,8 +424,8 @@ function field_test_entity_bundle_rename($entity_type, $bundle_old, $bundle_new)
// We need to account for deleted or inactive fields and instances.
$instances = field_read_instances(array('bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
- foreach ($instances as $field_name => $instance) {
- $field = field_info_field_by_id($instance['field_id']);
+ foreach ($instances as $instance) {
+ $field = $instance->getField();
if ($field && $field['storage']['type'] == 'field_test_storage') {
$field_data = &$data[$field['uuid']];
foreach (array('current', 'revisions') as $sub_table) {
diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml
similarity index 54%
rename from core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml
rename to core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml
index f472164..ae64a88 100644
--- a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml
+++ b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml
@@ -1,16 +1,12 @@
-id: field_test_import
+id: entity_test.field_test_import
langcode: und
+name: field_test_import
+entity_type: entity_test
type: text
settings:
max_length: '255'
module: text
active: '1'
-entity_types: { }
-storage:
- type: field_sql_storage
- settings: { }
- module: field_sql_storage
- active: '1'
locked: '0'
cardinality: '1'
translatable: false
diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import_2.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml
similarity index 54%
rename from core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import_2.yml
rename to core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml
index 4d508a9..38c855d 100644
--- a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import_2.yml
+++ b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml
@@ -1,16 +1,12 @@
-id: field_test_import_2
+id: entity_test.field_test_import_2
langcode: und
+name: field_test_import_2
+entity_type: entity_test
type: text
settings:
max_length: '255'
module: text
active: '1'
-entity_types: { }
-storage:
- type: field_sql_storage
- settings: { }
- module: field_sql_storage
- active: '1'
locked: '0'
cardinality: '1'
translatable: false
diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml
similarity index 58%
rename from core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging.yml
rename to core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml
index 4226f72..265e180 100644
--- a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging.yml
+++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml
@@ -1,17 +1,13 @@
-id: field_test_import_staging
+id: entity_test.field_test_import_staging
uuid: 0bf654cc-f14a-4881-b94c-76959e47466b
langcode: und
+name: field_test_import_staging
+entity_type: entity_test
type: text
settings:
max_length: '255'
module: text
active: '1'
-entity_types: { }
-storage:
- type: field_sql_storage
- settings: { }
- module: field_sql_storage
- active: '1'
locked: '0'
cardinality: '1'
translatable: '0'
diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging_2.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml
similarity index 58%
rename from core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging_2.yml
rename to core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml
index a0eb94e..ba36c64 100644
--- a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging_2.yml
+++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml
@@ -1,17 +1,13 @@
-id: field_test_import_staging_2
+id: entity_test.field_test_import_staging_2
uuid: 2165d9aa-9a0c-41a1-be02-2a49f3405c00
langcode: und
+name: field_test_import_staging_2
+entity_type: entity_test
type: text
settings:
max_length: '255'
module: text
active: '1'
-entity_types: { }
-storage:
- type: field_sql_storage
- settings: { }
- module: field_sql_storage
- active: '1'
locked: '0'
cardinality: '1'
translatable: '0'
diff --git a/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml b/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml
index 940848f..b370ab1 100644
--- a/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml
+++ b/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml
@@ -16,7 +16,7 @@ display:
provider: node
field_name_0:
id: field_name_0
- table: field_data_field_name_0
+ table: node__field_name_0
field: field_name_0
plugin_id: field
provider: field
diff --git a/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml b/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml
deleted file mode 100644
index 9a68dd9..0000000
--- a/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-# Schema for configuration files of the Field SQL Storage module.
-
-field_storage.field_sql_storage.settings:
- type: sequence
- label: 'Settings'
- sequence:
- - type: string
diff --git a/core/modules/field_sql_storage/field_sql_storage.info.yml b/core/modules/field_sql_storage/field_sql_storage.info.yml
deleted file mode 100644
index e5172b5..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.info.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: 'Field SQL Storage'
-type: module
-description: 'Stores field data in an SQL database.'
-package: Core
-version: VERSION
-core: 8.x
-dependencies:
- - field
-required: true
diff --git a/core/modules/field_sql_storage/field_sql_storage.install b/core/modules/field_sql_storage/field_sql_storage.install
deleted file mode 100644
index 523e0e0..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.install
+++ /dev/null
@@ -1,153 +0,0 @@
-condition('entity_type', $entity_type)
- ->condition('entity_id', $entity_id)
- ->execute();
- db_delete($revision_name)
- ->condition('entity_type', $entity_type)
- ->condition('entity_id', $entity_id)
- ->condition('revision_id', $revision_id)
- ->execute();
-
- $columns = array();
- foreach ($data as $langcode => $items) {
- foreach ($items as $delta => $item) {
- $record = array(
- 'entity_type' => $entity_type,
- 'entity_id' => $entity_id,
- 'revision_id' => $revision_id,
- 'bundle' => $bundle,
- 'delta' => $delta,
- 'langcode' => $langcode,
- );
- foreach ($item as $column => $value) {
- $record[_field_sql_storage_columnname($field_name, $column)] = $value;
- }
-
- $records[] = $record;
- // Record the columns used.
- $columns += $record;
- }
- }
-
- if ($columns) {
- $query = db_insert($table_name)->fields(array_keys($columns));
- $revision_query = db_insert($revision_name)->fields(array_keys($columns));
- foreach ($records as $record) {
- $query->values($record);
- if ($revision_id) {
- $revision_query->values($record);
- }
- }
- $query->execute();
- $revision_query->execute();
- }
-}
-
-/**
- * Implements hook_update_dependencies().
- */
-function field_sql_storage_update_dependencies() {
- // Convert storage tables after field definitions have moved to
- // ConfigEntities.
- $dependencies['field_sql_storage'][8000] = array(
- 'field' => 8003,
- );
- return $dependencies;
-}
-
-/**
- * Renames the 'language' column to 'langcode' in field data tables.
- */
-function field_sql_storage_update_8000(&$sandbox) {
- // Get field definitions from config, and deleted fields from state system.
- $config_names = config_get_storage_names_with_prefix('field.field');
- $deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
- // Ditch UUID keys, we will iterate through deleted fields using a numeric
- // index.
- $deleted_fields = array_values($deleted_fields);
-
- if (empty($config_names) && empty($deleted_fields)) {
- return;
- }
-
- if (!isset($sandbox['index'])) {
- $sandbox['index'] = 0;
- $sandbox['max'] = count($config_names) + count($deleted_fields);
- }
-
- // Retrieve the next field definition. When the index exceeds the number of
- // 'configuration' fields, use it to iterate on deleted fields.
- if (isset($config_names[$sandbox['index']])) {
- $field_config = Drupal::config($config_names[$sandbox['index']])->get();
- }
- else {
- $field_config = $deleted_fields[$sandbox['index'] - count($config_names)];
- }
-
- if ($field_config['storage']['type'] == 'field_sql_storage') {
- $field = new Field($field_config);
-
- // Prepare updated schema data structures.
- $primary_key_data = array(
- 'entity_type',
- 'entity_id',
- 'deleted',
- 'delta',
- 'langcode',
- );
- $primary_key_revision = array(
- 'entity_type',
- 'entity_id',
- 'revision_id',
- 'deleted',
- 'delta',
- 'langcode',
- );
- $langcode_index = array(
- 'langcode',
- );
- $field_langcode = array(
- 'type' => 'varchar',
- 'length' => 32,
- 'not null' => TRUE,
- 'default' => '',
- );
-
- $table_info = array(
- _field_sql_storage_tablename($field) => $primary_key_data,
- _field_sql_storage_revision_tablename($field) => $primary_key_revision,
- );
- foreach ($table_info as $table => $primary_key) {
- // Do not update tables which already have the langcode column,
- // created during the upgrade before this update function.
- if (db_field_exists($table, 'language')) {
- db_drop_primary_key($table);
- db_drop_index($table, 'language');
- db_change_field($table, 'language', 'langcode', $field_langcode);
- db_add_primary_key($table, $primary_key);
- db_add_index($table, 'langcode', $langcode_index);
- }
- }
- }
-
- $sandbox['index']++;
- $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']);
-}
diff --git a/core/modules/field_sql_storage/field_sql_storage.module b/core/modules/field_sql_storage/field_sql_storage.module
deleted file mode 100644
index 3e5e25f..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.module
+++ /dev/null
@@ -1,652 +0,0 @@
-' . t('About') . '';
- $output .= '
' . t('The Field SQL Storage module stores field data in the database. It is the default field storage module; other field storage mechanisms may be available as contributed modules. See the Field module help page for more information about fields.', array('@field-help' => url('admin/help/field'))) . '
'; - return $output; - } -} - -/** - * Implements hook_field_storage_info(). - */ -function field_sql_storage_field_storage_info() { - return array( - 'field_sql_storage' => array( - 'label' => t('Default SQL storage'), - 'description' => t('Stores fields in the local SQL database, using per-field tables.'), - ), - ); -} - -/** - * Generates a table name for a field data table. - * - * When a field is a deleted, the table is renamed to - * {field_deleted_data_FIELD_UUID}. To make sure we don't end up with table - * names longer than 64 characters, we hash the uuid and return the first 10 - * characters so we end up with a short unique ID. - * - * @param $field - * The field structure. - * - * @return - * A string containing the generated name for the database table. - */ -function _field_sql_storage_tablename($field) { - if ($field['deleted']) { - return "field_deleted_data_" . substr(hash('sha256', $field['uuid']), 0, 10); - } - else { - return "field_data_{$field['field_name']}"; - } -} - -/** - * Generates a table name for a field revision archive table. - * - * When a field is a deleted, the table is renamed to - * {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with table - * names longer than 64 characters, we hash the uuid and return the first - * 10 characters so we end up with a short unique ID. - * - * @param $name - * The field structure. - * - * @return - * A string containing the generated name for the database table. - */ -function _field_sql_storage_revision_tablename($field) { - if ($field['deleted']) { - return "field_deleted_revision_" . substr(hash('sha256', $field['uuid']), 0, 10); - } - else { - return "field_revision_{$field['field_name']}"; - } -} - -/** - * Generates a column name for a field data table. - * - * @param $name - * The name of the field. - * @param $column - * The name of the column. - * - * @return - * A string containing a generated column name for a field data table that is - * unique among all other fields. - */ -function _field_sql_storage_columnname($name, $column) { - return in_array($column, Field::getReservedColumns()) ? $column : $name . '_' . $column; -} - -/** - * Generates an index name for a field data table. - * - * @param $name - * The name of the field. - * @param $column - * The name of the index. - * - * @return - * A string containing a generated index name for a field data table that is - * unique among all other fields. - */ -function _field_sql_storage_indexname($name, $index) { - return $name . '_' . $index; -} - -/** - * Returns the database schema for a field. - * - * This may contain one or more tables. Each table will contain the columns - * relevant for the specified field. Leave the $field's 'columns' and 'indexes' - * keys empty to get only the base schema. - * - * @param $field - * The field structure for which to generate a database schema. - * @return - * One or more tables representing the schema for the field. - */ -function _field_sql_storage_schema($field) { - $deleted = $field['deleted'] ? 'deleted ' : ''; - $current = array( - 'description' => "Data storage for {$deleted}field {$field['id']} ({$field['field_name']})", - 'fields' => array( - 'entity_type' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The entity type this data is attached to', - ), - 'bundle' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance', - ), - 'deleted' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'A boolean indicating whether this data item has been deleted' - ), - 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The entity id this data is attached to', - ), - 'revision_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned', - ), - // @todo Consider storing langcode as integer. - 'langcode' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The language code for this data item.', - ), - 'delta' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The sequence number for this data item, used for multi-value fields', - ), - ), - 'primary key' => array('entity_type', 'entity_id', 'deleted', 'delta', 'langcode'), - 'indexes' => array( - 'entity_type' => array('entity_type'), - 'bundle' => array('bundle'), - 'deleted' => array('deleted'), - 'entity_id' => array('entity_id'), - 'revision_id' => array('revision_id'), - 'langcode' => array('langcode'), - ), - ); - - $schema = $field->getSchema(); - - // Add field columns. - foreach ($schema['columns'] as $column_name => $attributes) { - $real_name = _field_sql_storage_columnname($field['field_name'], $column_name); - $current['fields'][$real_name] = $attributes; - } - - // Add indexes. - foreach ($schema['indexes'] as $index_name => $columns) { - $real_name = _field_sql_storage_indexname($field['field_name'], $index_name); - foreach ($columns as $column_name) { - // Indexes can be specified as either a column name or an array with - // column name and length. Allow for either case. - if (is_array($column_name)) { - $current['indexes'][$real_name][] = array( - _field_sql_storage_columnname($field['field_name'], $column_name[0]), - $column_name[1], - ); - } - else { - $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name); - } - } - } - - // Add foreign keys. - foreach ($schema['foreign keys'] as $specifier => $specification) { - $real_name = _field_sql_storage_indexname($field['field_name'], $specifier); - $current['foreign keys'][$real_name]['table'] = $specification['table']; - foreach ($specification['columns'] as $column_name => $referenced) { - $sql_storage_column = _field_sql_storage_columnname($field['field_name'], $column_name); - $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced; - } - } - - // Construct the revision table. - $revision = $current; - $revision['description'] = "Revision archive storage for {$deleted}field {$field['id']} ({$field['field_name']})"; - $revision['primary key'] = array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'langcode'); - $revision['fields']['revision_id']['not null'] = TRUE; - $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to'; - - return array( - _field_sql_storage_tablename($field) => $current, - _field_sql_storage_revision_tablename($field) => $revision, - ); -} - -/** - * Implements hook_field_storage_create_field(). - */ -function field_sql_storage_field_storage_create_field($field) { - $schema = _field_sql_storage_schema($field); - foreach ($schema as $name => $table) { - db_create_table($name, $table); - } - // Do not rebuild the schema right now, since the field definition has not - // been saved yet. This will be done in hook_field_create_field(). -} - -/** - * Implements hook_field_update_forbid(). - * - * Forbids any field update that changes column definitions if there is any - * data. - */ -function field_sql_storage_field_update_forbid($field, $prior_field) { - if ($field->hasData() && $field['columns'] != $prior_field['columns']) { - throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data."); - } -} - -/** - * Implements hook_field_storage_update_field(). - */ -function field_sql_storage_field_storage_update_field($field, $prior_field) { - if (!$field->hasData()) { - // There is no data. Re-create the tables completely. - - if (Database::getConnection()->supportsTransactionalDDL()) { - // If the database supports transactional DDL, we can go ahead and rely - // on it. If not, we will have to rollback manually if something fails. - $transaction = db_transaction(); - } - - try { - $prior_schema = _field_sql_storage_schema($prior_field); - foreach ($prior_schema as $name => $table) { - db_drop_table($name, $table); - } - $schema = _field_sql_storage_schema($field); - foreach ($schema as $name => $table) { - db_create_table($name, $table); - } - } - catch (Exception $e) { - if (Database::getConnection()->supportsTransactionalDDL()) { - $transaction->rollback(); - } - else { - // Recreate tables. - $prior_schema = _field_sql_storage_schema($prior_field); - foreach ($prior_schema as $name => $table) { - if (!db_table_exists($name)) { - db_create_table($name, $table); - } - } - } - throw $e; - } - } - else { - // There is data, so there are no column changes. Drop all the - // prior indexes and create all the new ones, except for all the - // priors that exist unchanged. - $table = _field_sql_storage_tablename($prior_field); - $revision_table = _field_sql_storage_revision_tablename($prior_field); - - $schema = $field->getSchema(); - $prior_schema = $prior_field->getSchema(); - - foreach ($prior_schema['indexes'] as $name => $columns) { - if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) { - $real_name = _field_sql_storage_indexname($field['field_name'], $name); - db_drop_index($table, $real_name); - db_drop_index($revision_table, $real_name); - } - } - $table = _field_sql_storage_tablename($field); - $revision_table = _field_sql_storage_revision_tablename($field); - foreach ($schema['indexes'] as $name => $columns) { - if (!isset($prior_schema['indexes'][$name]) || $columns != $prior_schema['indexes'][$name]) { - $real_name = _field_sql_storage_indexname($field['field_name'], $name); - $real_columns = array(); - foreach ($columns as $column_name) { - // Indexes can be specified as either a column name or an array with - // column name and length. Allow for either case. - if (is_array($column_name)) { - $real_columns[] = array( - _field_sql_storage_columnname($field['field_name'], $column_name[0]), - $column_name[1], - ); - } - else { - $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name); - } - } - db_add_index($table, $real_name, $real_columns); - db_add_index($revision_table, $real_name, $real_columns); - } - } - } - drupal_get_schema(NULL, TRUE); -} - -/** - * Implements hook_field_storage_delete_field(). - */ -function field_sql_storage_field_storage_delete_field($field) { - // Mark all data associated with the field for deletion. - $field['deleted'] = FALSE; - $table = _field_sql_storage_tablename($field); - $revision_table = _field_sql_storage_revision_tablename($field); - db_update($table) - ->fields(array('deleted' => 1)) - ->execute(); - - // Move the table to a unique name while the table contents are being deleted. - $field['deleted'] = TRUE; - $new_table = _field_sql_storage_tablename($field); - $revision_new_table = _field_sql_storage_revision_tablename($field); - db_rename_table($table, $new_table); - db_rename_table($revision_table, $revision_new_table); - drupal_get_schema(NULL, TRUE); -} - -/** - * Implements hook_field_storage_load(). - */ -function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) { - $load_current = $age == FIELD_LOAD_CURRENT; - - foreach ($fields as $field_id => $ids) { - // By the time this hook runs, the relevant field definitions have been - // populated and cached in FieldInfo, so calling field_info_field_by_id() - // on each field individually is more efficient than loading all fields in - // memory upfront with field_info_field_by_ids(). - $field = field_info_field_by_id($field_id); - $field_name = $field['field_name']; - $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field); - - $query = db_select($table, 't') - ->fields('t') - ->condition('entity_type', $entity_type) - ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN') - ->condition('langcode', field_available_languages($entity_type, $field), 'IN') - ->orderBy('delta'); - - if (empty($options['deleted'])) { - $query->condition('deleted', 0); - } - - $results = $query->execute(); - - $delta_count = array(); - foreach ($results as $row) { - if (!isset($delta_count[$row->entity_id][$row->langcode])) { - $delta_count[$row->entity_id][$row->langcode] = 0; - } - - if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) { - $item = array(); - // For each column declared by the field, populate the item - // from the prefixed database column. - foreach ($field['columns'] as $column => $attributes) { - $column_name = _field_sql_storage_columnname($field_name, $column); - // Unserialize the value if specified in the column schema. - $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name; - } - - // Add the item to the field values for the entity. - $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item; - $delta_count[$row->entity_id][$row->langcode]++; - } - } - } -} - -/** - * Implements hook_field_storage_write(). - */ -function field_sql_storage_field_storage_write(EntityInterface $entity, $op, $fields) { - $vid = $entity->getRevisionId(); - $id = $entity->id(); - $bundle = $entity->bundle(); - $entity_type = $entity->entityType(); - if (!isset($vid)) { - $vid = $id; - } - - foreach ($fields as $field_id) { - $field = field_info_field_by_id($field_id); - $field_name = $field['field_name']; - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); - - $all_langcodes = field_available_languages($entity_type, $field); - $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name)); - - // Delete and insert, rather than update, in case a value was added. - if ($op == FIELD_STORAGE_UPDATE) { - // Delete language codes present in the incoming $entity->$field_name. - // Delete all language codes if $entity->$field_name is empty. - $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes; - if ($langcodes) { - // Only overwrite the field's base table if saving the default revision - // of an entity. - if ($entity->isDefaultRevision()) { - db_delete($table_name) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $id) - ->condition('langcode', $langcodes, 'IN') - ->execute(); - } - db_delete($revision_name) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $id) - ->condition('revision_id', $vid) - ->condition('langcode', $langcodes, 'IN') - ->execute(); - } - } - - // Prepare the multi-insert query. - $do_insert = FALSE; - $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'langcode'); - foreach ($field['columns'] as $column => $attributes) { - $columns[] = _field_sql_storage_columnname($field_name, $column); - } - $query = db_insert($table_name)->fields($columns); - $revision_query = db_insert($revision_name)->fields($columns); - - foreach ($field_langcodes as $langcode) { - $items = (array) $entity->{$field_name}[$langcode]; - $delta_count = 0; - foreach ($items as $delta => $item) { - // We now know we have someting to insert. - $do_insert = TRUE; - $record = array( - 'entity_type' => $entity_type, - 'entity_id' => $id, - 'revision_id' => $vid, - 'bundle' => $bundle, - 'delta' => $delta, - 'langcode' => $langcode, - ); - foreach ($field['columns'] as $column => $attributes) { - $column_name = _field_sql_storage_columnname($field_name, $column); - $value = isset($item[$column]) ? $item[$column] : NULL; - // Serialize the value if specified in the column schema. - $record[$column_name] = (!empty($attributes['serialize'])) ? serialize($value) : $value; - } - $query->values($record); - if (isset($vid)) { - $revision_query->values($record); - } - - if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) { - break; - } - } - } - - // Execute the query if we have values to insert. - if ($do_insert) { - // Only overwrite the field's base table if saving the default revision - // of an entity. - if ($entity->isDefaultRevision()) { - $query->execute(); - } - $revision_query->execute(); - } - } -} - -/** - * Implements hook_field_storage_delete(). - * - * This function deletes data for all fields for an entity from the database. - */ -function field_sql_storage_field_storage_delete(EntityInterface $entity, $fields) { - foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) { - if (isset($fields[$instance['field_id']])) { - $field = field_info_field_by_id($instance['field_id']); - field_sql_storage_field_storage_purge($entity, $field, $instance); - } - } -} - -/** - * Implements hook_field_storage_purge(). - * - * This function deletes data from the database for a single field on - * an entity. - */ -function field_sql_storage_field_storage_purge(EntityInterface $entity, $field, $instance) { - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); - db_delete($table_name) - ->condition('entity_type', $entity->entityType()) - ->condition('entity_id', $entity->id()) - ->execute(); - db_delete($revision_name) - ->condition('entity_type', $entity->entityType()) - ->condition('entity_id', $entity->id()) - ->execute(); -} - -/** - * Implements hook_field_storage_delete_revision(). - * - * This function actually deletes the data from the database. - */ -function field_sql_storage_field_storage_delete_revision(EntityInterface $entity, $fields) { - $vid = $entity->getRevisionId(); - if (isset($vid)) { - foreach ($fields as $field_id) { - $field = field_info_field_by_id($field_id); - $revision_name = _field_sql_storage_revision_tablename($field); - db_delete($revision_name) - ->condition('entity_type', $entity->entityType()) - ->condition('entity_id', $entity->id()) - ->condition('revision_id', $vid) - ->execute(); - } - } -} - -/** - * Implements hook_field_storage_delete_instance(). - * - * This function simply marks for deletion all data associated with the field. - */ -function field_sql_storage_field_storage_delete_instance($instance) { - $field = field_info_field($instance['field_name']); - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); - db_update($table_name) - ->fields(array('deleted' => 1)) - ->condition('entity_type', $instance['entity_type']) - ->condition('bundle', $instance['bundle']) - ->execute(); - db_update($revision_name) - ->fields(array('deleted' => 1)) - ->condition('entity_type', $instance['entity_type']) - ->condition('bundle', $instance['bundle']) - ->execute(); -} - -/** - * Implements hook_entity_bundle_rename(). - */ -function field_sql_storage_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) { - // We need to account for deleted or inactive fields and instances. - $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE)); - foreach ($instances as $instance) { - $field = field_info_field_by_id($instance['field_id']); - if ($field['storage']['type'] == 'field_sql_storage') { - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); - db_update($table_name) - ->fields(array('bundle' => $bundle_new)) - ->condition('entity_type', $entity_type) - ->condition('bundle', $bundle_old) - ->execute(); - db_update($revision_name) - ->fields(array('bundle' => $bundle_new)) - ->condition('entity_type', $entity_type) - ->condition('bundle', $bundle_old) - ->execute(); - } - } -} - -/** - * Implements hook_field_storage_purge_field(). - * - * All field data items and instances have already been purged, so all that is - * left is to delete the table. - */ -function field_sql_storage_field_storage_purge_field($field) { - $table_name = _field_sql_storage_tablename($field); - $revision_name = _field_sql_storage_revision_tablename($field); - db_drop_table($table_name); - db_drop_table($revision_name); -} - -/** - * Implements hook_field_storage_details(). - */ -function field_sql_storage_field_storage_details($field) { - $details = array(); - if (!empty($field['columns'])) { - // Add field columns. - foreach ($field['columns'] as $column_name => $attributes) { - $real_name = _field_sql_storage_columnname($field['field_name'], $column_name); - $columns[$column_name] = $real_name; - } - return array( - 'sql' => array( - FIELD_LOAD_CURRENT => array( - _field_sql_storage_tablename($field) => $columns, - ), - FIELD_LOAD_REVISION => array( - _field_sql_storage_revision_tablename($field) => $columns, - ), - ), - ); - } -} diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Tests/FieldSqlStorageTest.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Tests/FieldSqlStorageTest.php deleted file mode 100644 index 58d64b2..0000000 --- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Tests/FieldSqlStorageTest.php +++ /dev/null @@ -1,539 +0,0 @@ - 'Field SQL Storage tests', - 'description' => "Test Field SQL Storage module.", - 'group' => 'Field API' - ); - } - - function setUp() { - parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); - $entity_type = 'entity_test_rev'; - - $this->field_name = strtolower($this->randomName()); - $this->field = entity_create('field_entity', array( - 'field_name' => $this->field_name, - 'type' => 'test_field', - 'cardinality' => 4, - )); - $this->field->save(); - $this->instance = entity_create('field_instance', array( - 'field_name' => $this->field_name, - 'entity_type' => $entity_type, - 'bundle' => $entity_type - )); - $this->instance->save(); - $this->table = _field_sql_storage_tablename($this->field); - $this->revision_table = _field_sql_storage_revision_tablename($this->field); - } - - /** - * Uses the mysql tables and records to verify - * field_load_revision works correctly. - */ - function testFieldAttachLoad() { - $entity_type = 'entity_test_rev'; - $eid = 0; - $langcode = Language::LANGCODE_NOT_SPECIFIED; - - $columns = array('entity_type', 'entity_id', 'revision_id', 'delta', 'langcode', $this->field_name . '_value'); - - // Insert data for four revisions to the field revisions table - $query = db_insert($this->revision_table)->fields($columns); - for ($evid = 0; $evid < 4; ++$evid) { - $values[$evid] = array(); - // Note: we insert one extra value ('<=' instead of '<'). - for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) { - $value = mt_rand(1, 127); - $values[$evid][] = $value; - $query->values(array($entity_type, $eid, $evid, $delta, $langcode, $value)); - } - } - $query->execute(); - - // Insert data for the "most current revision" into the field table - $query = db_insert($this->table)->fields($columns); - foreach ($values[0] as $delta => $value) { - $query->values(array($entity_type, $eid, 0, $delta, $langcode, $value)); - } - $query->execute(); - - // Load the "most current revision" - $entity = entity_create($entity_type, array( - 'id' => 0, - 'revision_id' => 0, - )); - field_attach_load($entity_type, array($eid => $entity)); - foreach ($values[0] as $delta => $value) { - if ($delta < $this->field['cardinality']) { - $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value, "Value $delta is loaded correctly for current revision"); - } - else { - $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}), "No extraneous value gets loaded for current revision."); - } - } - - // Load every revision - for ($evid = 0; $evid < 4; ++$evid) { - $entity = entity_create($entity_type, array( - 'id' => $eid, - 'revision_id' => $evid, - )); - field_attach_load_revision($entity_type, array($eid => $entity)); - foreach ($values[$evid] as $delta => $value) { - if ($delta < $this->field['cardinality']) { - $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value, "Value $delta for revision $evid is loaded correctly"); - } - else { - $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}), "No extraneous value gets loaded for revision $evid."); - } - } - } - - // Add a translation in an unavailable language code and verify it is not - // loaded. - $eid = $evid = 1; - $unavailable_langcode = 'xx'; - $entity = entity_create($entity_type, array( - 'id' => $eid, - 'revision_id' => $evid, - )); - $values = array($entity_type, $eid, $evid, 0, $unavailable_langcode, mt_rand(1, 127)); - db_insert($this->table)->fields($columns)->values($values)->execute(); - db_insert($this->revision_table)->fields($columns)->values($values)->execute(); - field_attach_load($entity_type, array($eid => $entity)); - $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->field_name}), 'Field translation in an unavailable language ignored'); - } - - /** - * Reads mysql to verify correct data is - * written when using insert and update. - */ - function testFieldAttachInsertAndUpdate() { - $entity_type = 'entity_test_rev'; - $entity = entity_create($entity_type, array( - 'id' => 0, - 'revision_id' => 0, - )); - $langcode = Language::LANGCODE_NOT_SPECIFIED; - - // Test insert. - $values = array(); - // Note: we try to insert one extra value ('<=' instead of '<'). - // TODO : test empty values filtering and "compression" (store consecutive deltas). - for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) { - $values[$delta]['value'] = mt_rand(1, 127); - } - $entity->{$this->field_name} = $rev_values[0] = $values; - field_attach_insert($entity); - - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); - foreach ($values as $delta => $value) { - if ($delta < $this->field['cardinality']) { - $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is inserted correctly")); - } - else { - $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets inserted."); - } - } - - // Test update. - $entity = entity_create($entity_type, array( - 'id' => 0, - 'revision_id' => 1, - )); - $values = array(); - // Note: we try to update one extra value ('<=' instead of '<'). - for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) { - $values[$delta]['value'] = mt_rand(1, 127); - } - $rev_values[1] = $values; - $entity->{$this->field_name}->setValue($values); - field_attach_update($entity); - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); - foreach ($values as $delta => $value) { - if ($delta < $this->field['cardinality']) { - $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is updated correctly")); - } - else { - $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets updated."); - } - } - - // Check that data for both revisions are in the revision table. - // We make sure each value is stored correctly, then unset it. - // When an entire revision's values are unset (remembering that we - // put one extra value in $values per revision), unset the entire - // revision. Then, if $rev_values is empty at the end, all - // revision data was found. - $results = db_select($this->revision_table, 't')->fields('t')->execute(); - foreach ($results as $row) { - $this->assertEqual($row->{$this->field_name . '_value'}, $rev_values[$row->revision_id][$row->delta]['value'], "Value {$row->delta} for revision {$row->revision_id} stored correctly"); - unset($rev_values[$row->revision_id][$row->delta]); - if (count($rev_values[$row->revision_id]) == 1) { - unset($rev_values[$row->revision_id]); - } - } - $this->assertTrue(empty($rev_values), "All values for all revisions are stored in revision table {$this->revision_table}"); - - // Check that update leaves the field data untouched if - // $entity->{$field_name} is absent. - unset($entity->{$this->field_name}); - field_attach_update($entity); - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); - foreach ($values as $delta => $value) { - if ($delta < $this->field->cardinality) { - $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Update with no field_name entry leaves value $delta untouched")); - } - } - - // Check that update with an empty $entity->$field_name empties the field. - $entity->getBCEntity()->{$this->field_name} = NULL; - field_attach_update($entity); - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); - $this->assertEqual(count($rows), 0, t("Update with an empty field_name entry empties the field.")); - } - - /** - * Tests insert and update with empty and NULL fields. - */ - function testFieldAttachSaveMissingData() { - $entity_type = 'entity_test_rev'; - $entity = entity_create($entity_type, array( - 'id' => 0, - 'revision_id' => 0, - )); - - // Insert: Field is missing - field_attach_insert($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 0, 'Missing field results in no inserts'); - - // Insert: Field is NULL - $entity->{$this->field_name} = NULL; - field_attach_insert($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 0, 'NULL field results in no inserts'); - - // Add some real data - $entity->{$this->field_name}->value = 1; - field_attach_insert($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 1, 'Field data saved'); - - // Update: Field is NULL. Data should be wiped. - $entity->getBCEntity()->{$this->field_name} = NULL; - field_attach_update($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 0, 'NULL field leaves no data in table'); - - // Add a translation in an unavailable language. - $unavailable_langcode = 'xx'; - db_insert($this->table) - ->fields(array('entity_type', 'bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode')) - ->values(array($entity_type, $this->instance->bundle, 0, 0, 0, 0, $unavailable_langcode)) - ->execute(); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 1, 'Field translation in an unavailable language saved.'); - - // Again add some real data. - $entity->{$this->field_name}->value = 1; - field_attach_insert($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 2, 'Field data saved.'); - - // Update: Field translation is missing but field is not empty. Translation - // data should survive. - unset($entity->{$this->field_name}); - field_attach_update($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 2, 'Missing field translation leaves data in table.'); - - // Update: Field translation is NULL but field is not empty. Translation - // data should be wiped. - $entity->getBCEntity()->{$this->field_name}[Language::LANGCODE_NOT_SPECIFIED] = NULL; - field_attach_update($entity); - $count = db_select($this->table) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 1, 'NULL field translation is wiped.'); - } - - /** - * Test trying to update a field with data. - */ - function testUpdateFieldSchemaWithData() { - $entity_type = 'entity_test_rev'; - // Create a decimal 5.2 field and add some data. - $field = entity_create('field_entity', array( - 'field_name' => 'decimal52', - 'type' => 'number_decimal', - 'settings' => array('precision' => 5, 'scale' => 2), - )); - $field->save(); - $instance = entity_create('field_instance', array( - 'field_name' => 'decimal52', - 'entity_type' => $entity_type, - 'bundle' => $entity_type, - )); - $instance->save(); - $entity = entity_create($entity_type, array( - 'id' => 0, - 'revision_id' => 0, - )); - $entity->decimal52->value = '1.235'; - $entity->save(); - - // Attempt to update the field in a way that would work without data. - $field->settings['scale'] = 3; - try { - $field->save(); - $this->fail(t('Cannot update field schema with data.')); - } - catch (FieldException $e) { - $this->pass(t('Cannot update field schema with data.')); - } - } - - /** - * Test that failure to create fields is handled gracefully. - */ - function testFieldUpdateFailure() { - // Create a text field. - $field = entity_create('field_entity', array( - 'field_name' => 'test_text', - 'type' => 'text', - 'settings' => array('max_length' => 255), - )); - $field->save(); - - // Attempt to update the field in a way that would break the storage. - $prior_field = $field; - $field->settings['max_length'] = -1; - try { - $field->save(); - $this->fail(t('Update succeeded.')); - } - catch (\Exception $e) { - $this->pass(t('Update properly failed.')); - } - - // Ensure that the field tables are still there. - foreach (_field_sql_storage_schema($prior_field) as $table_name => $table_info) { - $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name))); - } - } - - /** - * Test adding and removing indexes while data is present. - */ - function testFieldUpdateIndexesWithData() { - // Create a decimal field. - $field_name = 'testfield'; - $entity_type = 'entity_test_rev'; - $field = entity_create('field_entity', array( - 'field_name' => $field_name, - 'type' => 'text')); - $field->save(); - $instance = entity_create('field_instance', array( - 'field_name' => $field_name, - 'entity_type' => $entity_type, - 'bundle' => $entity_type, - )); - $instance->save(); - $tables = array(_field_sql_storage_tablename($field), _field_sql_storage_revision_tablename($field)); - - // Verify the indexes we will create do not exist yet. - foreach ($tables as $table) { - $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in $table")); - $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in $table")); - } - - // Add data so the table cannot be dropped. - $entity = entity_create($entity_type, array( - 'id' => 1, - 'revision_id' => 1, - )); - $entity->$field_name->value = 'field data'; - $entity->enforceIsNew(); - $entity->save(); - - // Add an index. - $field->indexes = array('value' => array(array('value', 255))); - $field->save(); - foreach ($tables as $table) { - $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in $table")); - } - - // Add a different index, removing the existing custom one. - $field->indexes = array('value_format' => array(array('value', 127), array('format', 127))); - $field->save(); - foreach ($tables as $table) { - $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in $table")); - $this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in $table")); - } - - // Verify that the tables were not dropped. - $entity = entity_create($entity_type, array( - 'id' => 1, - 'revision_id' => 1, - )); - field_attach_load($entity_type, array(1 => $entity)); - $this->assertEqual($entity->$field_name->value, 'field data', t("Index changes performed without dropping the tables")); - } - - /** - * Test the storage details. - */ - function testFieldStorageDetails() { - $current = _field_sql_storage_tablename($this->field); - $revision = _field_sql_storage_revision_tablename($this->field); - - // Retrieve the field and instance with field_info so the storage details are attached. - $field = field_info_field($this->field_name); - $instance = field_info_instance($this->instance->entity_type, $field->id(), $this->instance->bundle); - - // The storage details are indexed by a storage engine type. - $storage_details = $field->getStorageDetails(); - $this->assertTrue(array_key_exists('sql', $storage_details), 'The storage type is SQL.'); - - // The SQL details are indexed by table name. - $details = $storage_details['sql']; - $this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), 'Table name is available in the instance array.'); - $this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), 'Revision table name is available in the instance array.'); - - // Test current and revision storage details together because the columns - // are the same. - $schema = $this->field->getSchema(); - foreach ($schema['columns'] as $column_name => $attributes) { - $storage_column_name = _field_sql_storage_columnname($this->field_name, $column_name); - $this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current))); - $this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision))); - } - } - - /** - * Test foreign key support. - */ - function testFieldSqlStorageForeignKeys() { - // Create a 'shape' field, with a configurable foreign key (see - // field_test_field_schema()). - $field_name = 'testfield'; - $foreign_key_name = 'shape'; - $field = entity_create('field_entity', array( - 'field_name' => $field_name, - 'type' => 'shape', - 'settings' => array('foreign_key_name' => $foreign_key_name), - )); - $field->save(); - // Get the field schema. - $schema = $field->getSchema(); - - // Retrieve the field definition and check that the foreign key is in place. - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD'); - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD'); - - // Update the field settings, it should update the foreign key definition too. - $foreign_key_name = 'color'; - $field->settings['foreign_key_name'] = $foreign_key_name; - $field->save(); - // Reload the field schema after the update. - $schema = $field->getSchema(); - - // Retrieve the field definition and check that the foreign key is in place. - $field = field_info_field($field_name); - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update'); - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update'); - - // Verify the SQL schema. - $schemas = _field_sql_storage_schema($field); - $schema = $schemas[_field_sql_storage_tablename($field)]; - $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema'); - $foreign_key = reset($schema['foreign keys']); - $foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_name); - $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema'); - $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema'); - } - -} diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index 7dff0b6..51d755e 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -217,7 +217,7 @@ function field_ui_instance_load($field_name, $entity_type, $bundle_name, $bundle $bundle_name = field_extract_bundle($entity_type, $bundle); } // Check whether the field exists at all. - if ($field = field_info_field($field_name)) { + if ($field = field_info_field($entity_type, $field_name)) { // Only return the field if a field instance exists for the given entity // type and bundle. if ($instance = field_info_instance($entity_type, $field_name, $bundle_name)) { diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php index 514e966..075ffe8 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php @@ -12,7 +12,6 @@ use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\Field\FieldTypePluginManager; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\field\FieldInfo; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -28,13 +27,6 @@ class FieldListController extends ConfigEntityListController { protected $fieldTypes; /** - * An array of field data. - * - * @var array - */ - protected $fieldInfo; - - /** * The entity manager. * * @var \Drupal\Core\Entity\EntityManager @@ -71,10 +63,9 @@ class FieldListController extends ConfigEntityListController { * @param \Drupal\Core\Entity\Field\FieldTypePluginManager $field_type_manager * The 'field type' plugin manager. */ - public function __construct($entity_type, array $entity_info, EntityManager $entity_manager, ModuleHandlerInterface $module_handler, FieldInfo $field_info, FieldTypePluginManager $field_type_manager) { + public function __construct($entity_type, array $entity_info, EntityManager $entity_manager, ModuleHandlerInterface $module_handler, FieldTypePluginManager $field_type_manager) { parent::__construct($entity_type, $entity_info, $entity_manager->getStorageController($entity_type), $module_handler); - $this->fieldInfo = $field_info->getFieldMap(); $this->entityManager = $entity_manager; $this->bundles = entity_get_bundles(); $this->fieldTypeManager = $field_type_manager; @@ -90,7 +81,6 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_info, $container->get('plugin.manager.entity'), $container->get('module_handler'), - $container->get('field.info'), $container->get('plugin.manager.entity.field.field_type') ); } @@ -111,24 +101,22 @@ public function buildHeader() { /** * {@inheritdoc} */ - public function buildRow(EntityInterface $entity) { - if ($entity->locked) { + public function buildRow(EntityInterface $field) { + if ($field->locked) { $row['class'] = array('menu-disabled'); - $row['data']['id'] = t('@field_name (Locked)', array('@field_name' => $entity->id())); + $row['data']['id'] = t('@field_name (Locked)', array('@field_name' => $field->name)); } else { - $row['data']['id'] = $entity->id(); + $row['data']['id'] = $field->name; } - $field_type = $this->fieldTypes[$entity->getFieldType()]; + $field_type = $this->fieldTypes[$field->type]; $row['data']['type'] = t('@type (module: @module)', array('@type' => $field_type['label'], '@module' => $field_type['provider'])); $usage = array(); - foreach($this->fieldInfo[$entity->id()]['bundles'] as $entity_type => $field_bundles) { - foreach($field_bundles as $bundle) { - $admin_path = $this->entityManager->getAdminPath($entity_type, $bundle); - $usage[] = $admin_path ? l($this->bundles[$entity_type][$bundle]['label'], $admin_path . '/fields') : $this->bundles[$entity_type][$bundle]['label']; - } + foreach ($field->getBundles() as $bundle) { + $admin_path = $this->entityManager->getAdminPath($field->entity_type, $bundle); + $usage[] = $admin_path ? l($this->bundles[$field->entity_type][$bundle]['label'], $admin_path . '/fields') : $this->bundles[$field->entity_type][$bundle]['label']; } $row['data']['usage'] = implode(', ', $usage); return $row; diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php index c900c04..b426759 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php @@ -118,7 +118,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, // Fields. foreach ($instances as $name => $instance) { - $field = field_info_field($instance['field_name']); + $field = $instance->getField(); $admin_field_path = $this->adminPath . '/fields/' . $instance->id(); $table[$name] = array( '#attributes' => array( @@ -202,7 +202,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), // Calculate characters depending on the length of the field prefix // setting. Maximum length is 32. - '#maxlength' => Field::ID_MAX_LENGTH - strlen($field_prefix), + '#maxlength' => Field::NAME_MAX_LENGTH - strlen($field_prefix), '#prefix' => '