diff --git a/core/includes/entity.inc b/core/includes/entity.inc index 8071a0d..3870a62 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -102,6 +102,8 @@ function entity_get_bundles($entity_type = NULL) { */ function entity_invoke_bundle_hook($hook, $entity_type, $bundle, $bundle_new = NULL) { entity_info_cache_clear(); + $method = 'handleBundle' . ucfirst($hook); + Drupal::entityManager()->getStorageController($entity_type)->$method($bundle, $bundle_new); 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..e6d8090 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); @@ -335,14 +348,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->fieldLoad($queried_entities, $load_revision ? FIELD_LOAD_REVISION : FIELD_LOAD_CURRENT); } // Call hook_entity_load(). @@ -490,7 +498,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,42 +536,673 @@ protected function saveRevision(EntityInterface $entity) { } /** - * Invokes a hook on behalf of the entity. + * {@inheritdoc} + */ + public function baseFieldDefinitions() { + // @todo: Define abstract once all entity types have been converted. + return array(); + } + + /** + * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::getQueryServiceName(). + */ + public function getQueryServiceName() { + return 'entity.query.sql'; + } + + /** + * {@inheritdoc} + */ + protected function doFieldLoad($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 doFieldInsert(EntityInterface $entity) { + $this->doFieldWrite($entity, FALSE); + } + + /** + * {@inheritdoc} + */ + protected function doFieldUpdate(EntityInterface $entity) { + $this->doFieldWrite($entity, TRUE); + } + + /** + * Performs the actual write on behalf of doFieldInsert / doFieldUpdate. * - * @param $hook - * One of 'presave', 'insert', 'update', 'predelete', 'delete', or - * 'revision_delete'. - * @param $entity - * The entity object. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being written. + * @param bool $update + * TRUE when running an update, FALSE when running an insert. */ - 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'; + protected function doFieldWrite(EntityInterface $entity, $update) { + $vid = $entity->getRevisionId(); + $id = $entity->id(); + $bundle = $entity->bundle(); + $entity_type = $entity->entityType(); + if (!isset($vid)) { + $vid = $id; } - if (!empty($this->entityInfo['fieldable']) && function_exists($function)) { - $function($entity); + + 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(); + } } - // 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() { - // @todo: Define abstract once all entity types have been converted. - return array(); + protected function doFieldDelete(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(); + } } /** - * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::getQueryServiceName(). + * {@inheritdoc} */ - public function getQueryServiceName() { - return 'entity.query.sql'; + protected function doFieldRevisionDelete(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 handleFieldCreate(FieldInterface $field) { + $schema = $this->_fieldSqlSchema($field); + foreach ($schema as $name => $table) { + $this->database->schema()->createTable($name, $table); + } } + + /** + * {@inheritdoc} + */ + public function handleFieldUpdate(FieldInterface $field, FieldInterface $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 handleFieldDelete(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 handleInstanceDelete(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 handleBundleRename($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 fieldLoadPurgedValues(EntityInterface $entity, FieldInstanceInterface $instance) { + $field = $instance->getField(); + $query = $this->database->select(static::_fieldTableName($field), '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 doFieldPurgeData(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 handleFieldPurged(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..0c325bd 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']; 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 ceb09de..b9e3da5 100644 --- a/core/lib/Drupal/Core/Entity/EntityFormController.php +++ b/core/lib/Drupal/Core/Entity/EntityFormController.php @@ -295,6 +295,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(); @@ -311,9 +312,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(); @@ -330,7 +331,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); @@ -459,9 +460,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 5321099..187ff34 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -88,6 +88,15 @@ class EntityManager extends PluginManagerBase { protected $fieldDefinitions; /** + * The root paths. + * + * @see \Drupal\Core\Entity\EntityManager::__construct(). + * + * @var \Traversable + */ + protected $namespaces; + + /** * Constructs a new Entity plugin manager. * * @param \Traversable $namespaces @@ -104,21 +113,45 @@ class EntityManager extends PluginManagerBase { */ public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_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->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); } /** @@ -153,6 +186,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..8c5ff5e --- /dev/null +++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php @@ -0,0 +1,322 @@ +entityInfo['fieldable']) && method_exists($this, $method)) { + $this->$method($entity); + } + parent::invokeHook($hook, $entity); + } + + /** + * 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. + * + * @param array $entities + * An array of entities for which to load fields, 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. + */ + protected function fieldLoad($entities, $age) { + if (empty($entities)) { + return; + } + + // Only the most current revision of non-deleted fields for cacheable entity + // types can be cached. + $load_current = $age == FIELD_LOAD_CURRENT; + $info = entity_get_info($this->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) { + // The invoke order is: + // - Entity storage controller's doFieldLoad() method + // - Field class's prepareCache() method. + // - hook_field_attach_load() + + // Let the storage controller actually load the values. + $this->doFieldLoad($queried_entities, $age); + + // Invoke the field type's prepareCache() method. + foreach ($queried_entities as $entity) { + \Drupal::entityManager() + ->getStorageController($this->entityType) + ->invokeFieldItemPrepareCache($entity); + } + + // Invoke hook_field_attach_load(): let other modules act on loading the + // entity. + \Drupal::moduleHandler()->invokeAll('field_attach_load', array($this->entityType, $queried_entities, $age)); + + // 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); + } + } + } + } + + /** + * Save field data for a new entity. + * + * The passed-in entity must already contain its id and (if applicable) + * revision id attributes. + * + * It should be enough to override doFieldInsert() instead of this method. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity with fields to save. + */ + protected function fieldInsert(EntityInterface $entity) { + // Ensure we are working with a BC mode entity. + $entity = $entity->getBCEntity(); + + $this->doFieldInsert($entity); + } + + /** + * Saves field data for an existing entity. + * + * It should be enough to override doFieldUpdate() instead of this method. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity with fields to save. + */ + protected function fieldUpdate(EntityInterface $entity) { + // Ensure we are working with a BC mode entity. + $entity = $entity->getBCEntity(); + + $this->doFieldUpdate($entity); + + $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. + * + * It should be enough to override doFieldDelete() instead of this method. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity whose field data to delete. + */ + protected function fieldDelete(EntityInterface $entity) { + // Ensure we are working with a BC mode entity. + $entity = $entity->getBCEntity(); + + $this->doFieldDelete($entity); + + $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. + * + * It should be enough to override doFieldRevisionDelete() instead of this + * method. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity with fields to save. + */ + protected function fieldRevisionDelete(EntityInterface $entity) { + $this->dofieldRevisionDelete($entity->getBCEntity()); + } + + /** + * Load configurable fields from storage. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + protected function doFieldLoad($queried_entities, $age) { } + + /** + * Insert configurable fields into storage. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + protected function doFieldInsert(EntityInterface $entity) { } + + /** + * Update configurable fields in storage. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + protected function doFieldUpdate(EntityInterface $entity) { } + + /** + * Delete configurable fields from storage. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + protected function doFieldDelete(EntityInterface $entity) { } + + /** + * Delete specific revision of configurable fields from storage. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + protected function doFieldRevisionDelete(EntityInterface $entity) { } + + /** + * {@inheritdoc} + */ + public function handleFieldCreate(FieldInterface $field) { } + + /** + * {@inheritdoc} + */ + public function handleFieldUpdate(FieldInterface $field, FieldInterface $original) { } + + /** + * {@inheritdoc} + */ + public function handleFieldDelete(FieldInterface $field) { } + + /** + * {@inheritdoc} + */ + public function handleInstanceCreate(FieldInstanceInterface $instance) { } + + /** + * {@inheritdoc} + */ + public function handleInstanceUpdate(FieldInstanceInterface $instance, FieldInstanceInterface $orignal) { } + + /** + * {@inheritdoc} + */ + public function handleInstanceDelete(FieldInstanceInterface $instance) { } + + /** + * {@inheritdoc} + */ + public function handleBundleCreate($bundle) { } + + /** + * {@inheritdoc} + */ + public function handleBundleRename($bundle, $bundle_new) { } + + /** + * {@inheritdoc} + */ + public function handleBundleDelete($bundle) { } + + /** + * {@inheritdoc} + */ + public function fieldPurgeData(EntityInterface $entity, FieldInstanceInterface $instance) { + if ($values = $this->fieldLoadPurgedValues($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->doFieldPurgeData($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 + * handleFieldDelete() or handleFieldInstanceDelete() 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 fieldLoadPurgedValues(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. + */ + protected function doFieldPurgeData(EntityInterface $entity, FieldInstanceInterface $instance) { } + + /** + * {@inheritdoc} + */ + public function handleFieldPurged(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..8b8de91 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php @@ -0,0 +1,116 @@ +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 { @@ -242,12 +243,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.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 b7869fb..7202909 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..6723759 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->assertTrue(empty($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 08cc693..56892de 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..a4d86eb 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 649f033..a7d38af 100644 --- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php +++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php @@ -19,7 +19,7 @@ class EditTestBase extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'text', 'edit'); + public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_test', 'number', 'text', 'edit'); /** * Sets the default field storage backend for fields created during tests. */ @@ -55,7 +55,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 52bb75f..31ace31 100644 --- a/core/modules/entity_reference/entity_reference.module +++ b/core/modules/entity_reference/entity_reference.module @@ -50,7 +50,7 @@ function entity_reference_field_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; } @@ -165,7 +165,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) { @@ -394,14 +394,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 de4729c..ab8886d 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 @@ -108,7 +108,7 @@ public static function settingsForm(&$field, &$instance) { $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table'])); foreach (field_info_instances($field['settings']['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 f5f74d0..6cccb4d 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/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..3acbfb4 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -400,31 +400,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 +487,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 +517,6 @@ function hook_field_info_max_weight($entity_type, $bundle, $context, $context_mo } /** - * @} End of "addtogroup field_storage". - */ - -/** * @addtogroup field_crud * @{ */ @@ -1300,63 +596,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 8c9238e..ff9df77 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; } @@ -828,6 +834,7 @@ function field_attach_form_validate(EntityInterface $entity, $form, &$form_state return; } + $entity_type = $entity->entityType(); $has_violations = FALSE; foreach ($entity as $field_name => $field) { $definition = $field->getDefinition(); @@ -837,7 +844,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_type, field_info_field($entity_type, $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); 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..1e7fb90 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,67 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc } /** + * Writes field data directly to SQL storage. + * + * @param string $entity_type + * @param string $bundle + * @param int $entity_id + * @param int $revision_id + * @param int $field_name + * @param array $data + * + * @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 +312,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 +347,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 +421,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 +465,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 efb3fb0..4feeadc 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -259,7 +259,7 @@ function field_populate_default_values(EntityInterface $entity, $langcode = NULL $entity_type = $entity->entityType(); $langcode = $langcode ?: $entity->language()->id; foreach (field_info_instances($entity_type, $entity->bundle()) as $field_name => $instance) { - $field = field_info_field($field_name); + $field = $instance->getField(); $field_langcode = field_is_translatable($entity_type, $field) ? $langcode : Language::LANGCODE_NOT_SPECIFIED; // We need to preserve existing values. if (empty($entity->{$field_name}) || !array_key_exists($field_langcode, $entity->{$field_name})) { @@ -310,7 +310,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, @@ -437,24 +437,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'])) { @@ -466,11 +450,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. @@ -482,8 +461,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(); } } @@ -703,7 +680,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); @@ -793,7 +770,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( @@ -938,10 +915,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'], @@ -956,8 +935,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..a7b71bd 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($instance->entity_type)->fieldPurgeData($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)->handleFieldPurged($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 cb2d4f0..a054817 100644 --- a/core/modules/field/lib/Drupal/field/Entity/Field.php +++ b/core/modules/field/lib/Drupal/field/Entity/Field.php @@ -37,24 +37,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. @@ -66,6 +77,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. @@ -118,16 +136,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 @@ -142,25 +150,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 @@ -201,13 +190,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 @@ -222,8 +204,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 @@ -236,22 +219,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); } @@ -259,18 +238,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', @@ -296,7 +282,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(); @@ -318,18 +304,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 + // Assign the ID. + $this->id = $this->id(); + + // Field name cannot be longer than Field::NAME_MAX_LENGTH characters. We // use drupal_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 (drupal_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, ) )); } @@ -337,17 +325,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))); } } @@ -363,23 +351,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)->handleFieldCreate($this); // Save the configuration. $result = parent::save(); @@ -401,7 +374,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; @@ -410,11 +384,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 @@ -426,11 +397,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)->handleFieldUpdate($this, $original); // Save the configuration. $result = parent::save(); @@ -444,16 +414,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 @@ -463,9 +430,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)->handleFieldDelete($this); // Delete the configuration of this field and save the field configuration // in the key_value table so we can use it later during @@ -528,30 +493,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(); @@ -561,7 +507,7 @@ public function getBundles() { * {@inheritdoc} */ public function getFieldName() { - return $this->id; + return $this->name; } /** @@ -664,7 +610,7 @@ public function &offsetGet($offset) { return $this->uuid; case 'field_name': - return $this->id; + return $this->name; case 'columns': $this->getSchema(); @@ -677,10 +623,6 @@ public function &offsetGet($offset) { case 'bundles': $bundles = $this->getBundles(); return $bundles; - - case 'storage_details': - $this->getStorageDetails(); - return $this->storageDetails; } return $this->{$offset}; @@ -735,19 +677,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) @@ -762,4 +704,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 3d8635b..e96cc8f 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; @@ -34,7 +35,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. * @@ -234,10 +235,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; } @@ -266,16 +267,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); } @@ -284,7 +285,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; } /** @@ -354,14 +355,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. @@ -409,6 +405,9 @@ protected function saveUpdated() { // Ensure default values are present. $this->prepareSave(); + // Notify the entity storage controller. + \Drupal::entityManager()->getStorageController($this->entity_type)->handleInstanceUpdate($this, $original); + // Save the configuration. $result = parent::save(); field_cache_clear(); @@ -436,7 +435,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 @@ -450,16 +448,15 @@ public function delete($field_cleanup = TRUE) { parent::delete(); + // Notify the entity storage controller. + \Drupal::entityManager()->getStorageController($this->entity_type)->handleInstanceDelete($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. @@ -469,7 +466,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. @@ -490,7 +487,7 @@ public function getField() { * {@inheritdoc} */ public function getFieldName() { - return $this->field->id; + return $this->field->name; } /** @@ -604,7 +601,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 2639c25..096841d 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; @@ -411,7 +413,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']; } } } @@ -431,39 +433,42 @@ public function getBundleInstances($entity_type, $bundle) { // Cache miss: collect from the definitions. $instances = array(); + $field_map = $this->getFieldMap(); // 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']; } } + + // 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; + } } } @@ -565,7 +570,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/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 fdfea10..a0e8f6e 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..8c66ab1 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. @@ -198,19 +199,25 @@ 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))); + $this->checkFieldTableContents($field); + } + + /** + * Test that the actual stored content didn't change during delete. + * + * @param FieldInterface $field + */ + protected function checkFieldTableContents(FieldInterface $field) { + $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); } } @@ -226,7 +233,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 +248,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 +292,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 +303,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 +322,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 +337,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..c327b43 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. @@ -370,53 +252,55 @@ function testFieldAttachSaveEmptyDataDefaultValue() { 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..7ba1a18 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,16 @@ function createFieldWithInstance($suffix = '', $entity_type = 'entity_test', $bu } /** + * Save and reload an entity. + */ + 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 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 8658c52..2002c0f 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 8a8674c..187bf46 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -216,7 +216,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 799f953..eeebcfe 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' => 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' => '
 
', '#machine_name' => array( 'source' => array('fields', $name, 'label'), @@ -382,12 +382,13 @@ public function submitForm(array &$form, array &$form_state) { $values = $form_values['_add_new_field']; $field = array( - 'field_name' => $values['field_name'], + 'name' => $values['field_name'], + 'entity_type' => $this->entity_type, 'type' => $values['type'], 'translatable' => $values['translatable'], ); $instance = array( - 'field_name' => $field['field_name'], + 'field_name' => $values['field_name'], 'entity_type' => $this->entity_type, 'bundle' => $this->bundle, 'label' => $values['label'], @@ -403,14 +404,14 @@ public function submitForm(array &$form, array &$form_state) { // default widget and settings). It stays hidden for other form modes // until it is explicitly configured. entity_get_form_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field['field_name']) + ->setComponent($values['field_name']) ->save(); // Make sure the field is displayed in the 'default' view mode (using // default formatter and settings). It stays hidden for other view // modes until it is explicitly configured. entity_get_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field['field_name']) + ->setComponent($values['field_name']) ->save(); // Always show the field settings step, as the cardinality needs to be @@ -419,7 +420,7 @@ public function submitForm(array &$form, array &$form_state) { $destinations[] = $this->adminPath . '/fields/' . $new_instance->id(); // Store new field information for any additional submit handlers. - $form_state['fields_added']['_add_new_field'] = $field['field_name']; + $form_state['fields_added']['_add_new_field'] = $values['field_name']; } catch (\Exception $e) { drupal_set_message(t('There was a problem creating field %label: !message', array('%label' => $instance['label'], '!message' => $e->getMessage())), 'error'); @@ -429,7 +430,7 @@ public function submitForm(array &$form, array &$form_state) { // Re-use existing field. if (!empty($form_values['_add_existing_field']['field_name'])) { $values = $form_values['_add_existing_field']; - $field = field_info_field($values['field_name']); + $field = field_info_field($this->entity_type, $values['field_name']); if (!empty($field['locked'])) { drupal_set_message(t('The field %label cannot be added because it is locked.', array('%label' => $values['label'])), 'error'); } @@ -490,37 +491,42 @@ public function submitForm(array &$form, array &$form_state) { * An array of existing fields keyed by field name. */ protected function getExistingFieldOptions() { - $info = array(); - $field_types = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions(); - - foreach (field_info_instances() as $existing_entity_type => $bundles) { - foreach ($bundles as $existing_bundle => $instances) { - // No need to look in the current bundle. - if (!($existing_bundle == $this->bundle && $existing_entity_type == $this->entity_type)) { - foreach ($instances as $instance) { - $field = field_info_field($instance['field_name']); - // Don't show - // - locked fields, - // - fields already in the current bundle, - // - fields that cannot be added to the entity type, - // - fields that should not be added via user interface. - - if (empty($field['locked']) - && !field_info_instance($this->entity_type, $field['field_name'], $this->bundle) - && (empty($field['entity_types']) || in_array($this->entity_type, $field['entity_types'])) - && empty($field_types[$field['type']]['no_ui'])) { - $info[$instance['field_name']] = array( - 'type' => $field['type'], - 'type_label' => $field_types[$field['type']]['label'], - 'field' => $field['field_name'], - 'label' => $instance['label'], - ); - } - } + $options = array(); + + // Collect candidate field instances: all instances of fields for this + // entity type that are not already present in the current bundle. + $field_map = field_info_field_map(); + $instance_ids = array(); + if (!empty($field_map[$this->entity_type])) { + foreach ($field_map[$this->entity_type] as $field_name => $data) { + if (!in_array($this->bundle, $data['bundles'])) { + $bundle = reset($data['bundles']); + $instance_ids[] = $this->entity_type . '.' . $bundle . '.' . $field_name; + } + } + } + + // Load the instances and build the list of options. + if ($instance_ids) { + $field_types = $this->fieldTypeManager->getDefinitions(); + $instances = $this->entityManager->getStorageController('field_instance')->loadMultiple($instance_ids); + foreach ($instances as $instance) { + $field = $instance->getField(); + // Do not show: + // - locked fields, + // - fields that should not be added via user interface. + if (empty($field['locked']) && empty($field_types[$field['type']]['no_ui'])) { + $options[$field->name] = array( + 'type' => $field->type, + 'type_label' => $field_types[$field->type]['label'], + 'field' => $field->name, + 'label' => $instance->label, + ); } } } - return $info; + + return $options; } /** @@ -538,7 +544,7 @@ public function fieldNameExists($value) { // We need to check inactive fields as well, so we can't use // field_info_fields(). - return (bool) field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE)); + return (bool) field_read_fields(array('entity_type' => $this->entity_type, 'name' => $field_name), array('include_inactive' => TRUE)); } } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php index f40e32a..f880cce 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php @@ -174,7 +174,7 @@ public function submitForm(array &$form, array &$form_state) { unset($field_values['container']); // Merge incoming form values into the existing field. - $field = Field::fieldInfo()->getField($field_values['field_name']); + $field = $this->instance->getField(); foreach ($field_values as $key => $value) { $field[$key] = $value; } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php index 046e012..743d779 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php @@ -198,7 +198,7 @@ public function validateForm(array &$form, array &$form_state) { // Extract the 'default value'. $items = $entity->getNGEntity()->{$field_name}; - $entity_form_display->getRenderer($this->instance->getField()->id)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); + $entity_form_display->getRenderer($this->instance->getField()->name)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); $violations = $items->validate(); // Report errors. @@ -209,7 +209,7 @@ public function validateForm(array &$form, array &$form_state) { field_form_set_state($element['#parents'], $field_name, Language::LANGCODE_NOT_SPECIFIED, $form_state, $field_state); // Assign reported errors to the correct form element. - $entity_form_display->getRenderer($this->instance->getField()->id)->flagErrors($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); + $entity_form_display->getRenderer($this->instance->getField()->name)->flagErrors($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); } } } @@ -228,7 +228,7 @@ public function submitForm(array &$form, array &$form_state) { // Extract field values. $items = $entity->getNGEntity()->{$field_name}; - $entity_form_display->getRenderer($this->instance->getField()->id)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); + $entity_form_display->getRenderer($this->instance->getField()->name)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); $this->instance['default_value'] = $items->getValue() ?: NULL; } @@ -296,11 +296,11 @@ protected function getDefaultValueWidget($field, array &$form, &$form_state) { // Insert the widget. Since we do not use the "official" instance definition, // the whole flow cannot use field_invoke_method(). - $items = $entity->getNGEntity()->{$this->instance->getField()->id}; + $items = $entity->getNGEntity()->{$this->instance->getField()->name}; if (!empty($this->instance['default_value'])) { $items->setValue((array) $this->instance['default_value']); } - $element += $entity_form_display->getRenderer($this->instance->getField()->id)->form($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); + $element += $entity_form_display->getRenderer($this->instance->getField()->name)->form($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state); return $element; } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php index 69be9ec..9d957c5 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php @@ -42,7 +42,8 @@ function setUp() { $vocabulary->save(); $field = array( - 'field_name' => 'field_' . $vocabulary->id(), + 'name' => 'field_' . $vocabulary->id(), + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', ); entity_create('field_entity', $field)->save(); @@ -116,13 +117,6 @@ function createField() { 'fields[_add_new_field][field_name]' => $this->field_name_input, ); $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->type, $edit); - - // Assert the field appears in the "re-use existing field" section for - // different entity types; e.g. if a field was added in a node entity, it - // should also appear in the 'taxonomy term' entity. - $vocabulary = entity_load('taxonomy_vocabulary', 'tags'); - $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/fields'); - $this->assertTrue($this->xpath('//select[@name="fields[_add_existing_field][field_name]"]//option[@value="' . $this->field_name . '"]'), 'Existing field was found in taxonomy term fields.'); } /** @@ -162,9 +156,8 @@ function addExistingField() { $this->drupalGet('admin/structure/types/manage/page/fields'); $this->assertRaw(t('Re-use existing field'), '"Re-use existing field" was found.'); - // Check that the list of options respects entity type restrictions on - // fields. The 'comment' field is restricted to the 'comment' entity type - // and should not appear in the list. + // Check that fields of other entity types (here, the 'comment_body' field) + // do not show up in the "Re-use existing field" list. $this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value="comment"]'), 'The list of options respects entity type restrictions.'); // Add a new field based on an existing field. @@ -231,7 +224,7 @@ function assertFieldSettings($bundle, $field_name, $string = 'dummy test string' // Reset the fields info. field_info_cache_clear(); // Assert field settings. - $field = field_info_field($field_name); + $field = field_info_field($entity_type, $field_name); $this->assertTrue($field['settings']['test_field_setting'] == $string, 'Field settings were found.'); // Assert instance settings. @@ -276,7 +269,8 @@ function testDefaultValue() { // Create a test field and instance. $field_name = 'test'; entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'node', 'type' => 'test_field' ))->save(); $instance = entity_create('field_instance', array( @@ -366,7 +360,7 @@ function testDeleteField() { // Check that the field instance was deleted. $this->assertNull(field_info_instance('node', $this->field_name, $this->type), 'Field instance was deleted.'); // Check that the field was not deleted - $this->assertNotNull(field_info_field($this->field_name), 'Field was not deleted.'); + $this->assertNotNull(field_info_field('node', $this->field_name), 'Field was not deleted.'); // Delete the second instance. $this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$this->field_name", $this->field_label, $type_name2); @@ -376,7 +370,7 @@ function testDeleteField() { // Check that the field instance was deleted. $this->assertNull(field_info_instance('node', $this->field_name, $type_name2), 'Field instance was deleted.'); // Check that the field was deleted too. - $this->assertNull(field_info_field($this->field_name), 'Field was deleted.'); + $this->assertNull(field_info_field('node', $this->field_name), 'Field was deleted.'); } /** @@ -386,7 +380,8 @@ function testLockedField() { // Create a locked field and attach it to a bundle. We need to do this // programatically as there's no way to create a locked field through UI. $field = entity_create('field_entity', array( - 'field_name' => strtolower($this->randomName(8)), + 'name' => strtolower($this->randomName(8)), + 'entity_type' => 'node', 'type' => 'test_field', 'cardinality' => 1, 'locked' => TRUE @@ -405,11 +400,11 @@ function testLockedField() { // Check that the links for edit and delete are not present. $this->drupalGet('admin/structure/types/manage/' . $this->type . '/fields'); - $locked = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->id())); + $locked = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->name)); $this->assertTrue(in_array('Locked', $locked), 'Field is marked as Locked in the UI'); - $edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->id())); + $edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->name)); $this->assertFalse(in_array('edit', $edit_link), 'Edit option for locked field is not present the UI'); - $delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->id())); + $delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->name)); $this->assertFalse(in_array('delete', $delete_link), 'Delete option for locked field is not present the UI'); } @@ -425,7 +420,11 @@ function testHiddenFields() { // Create a field and an instance programmatically. $field_name = 'hidden_test_field'; - entity_create('field_entity', array('field_name' => $field_name, 'type' => $field_name))->save(); + entity_create('field_entity', array( + 'name' => $field_name, + 'entity_type' => 'node', + 'type' => $field_name, + ))->save(); $instance = array( 'field_name' => $field_name, 'bundle' => $this->type, @@ -501,7 +500,7 @@ function testDeleteTaxonomyField() { // Check that the field instance was deleted. $this->assertNull(field_info_instance('taxonomy_term', $this->field_name, 'tags'), 'Field instance was deleted.'); // Check that the field was deleted too. - $this->assertNull(field_info_field($this->field_name), 'Field was deleted.'); + $this->assertNull(field_info_field('taxonomy_term', $this->field_name), 'Field was deleted.'); } /** @@ -510,7 +509,8 @@ function testDeleteTaxonomyField() { function testHelpDescriptions() { // Create an image field entity_create('field_entity', array( - 'field_name' => 'field_image', + 'name' => 'field_image', + 'entity_type' => 'node', 'type' => 'image', ))->save(); diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index 3e3806e..369b8c0 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -355,7 +355,7 @@ function file_field_widget_multiple_count_validate($element, &$form_state, $form array_pop($parents); $current = count(element_children(NestedArray::getValue($form, $parents))) - 1; - $field = field_info_field($element['#field_name']); + $field = field_info_field($element['#entity_type'], $element['#field_name']); $uploaded = count($values['fids']); $count = $uploaded + $current; if ($count > $field['cardinality']) { diff --git a/core/modules/file/file.install b/core/modules/file/file.install index 39d671c..ab17ad8 100644 --- a/core/modules/file/file.install +++ b/core/modules/file/file.install @@ -5,6 +5,7 @@ * Install, update and uninstall functions for File module. */ +use Drupal\Core\Entity\DatabaseStorageController; use Drupal\field\Entity\Field; /** @@ -247,10 +248,10 @@ function file_update_dependencies() { 'file' => 8001, ); - // Convert the 'fid' column of file fields to 'target_id' after fields and - // instances have been moved to the config system. + // Convert the 'fid' column of file fields to 'target_id' after the field + // tables have been reorganized. $dependencies['file'][8003] = array( - 'field' => 8003, + 'field' => 8006, ); return $dependencies; } @@ -305,21 +306,21 @@ function file_update_8003() { foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) { $field_config = Drupal::config($config_name); // Only update file fields that use the default SQL storage. - if (in_array($field_config->get('type'), array('file', 'image')) && $field_config->get('storage.type') == 'field_sql_storage') { + if (in_array($field_config->get('type'), array('file', 'image'))) { $field = new Field($field_config->get()); $tables = array( - _field_sql_storage_tablename($field), - _field_sql_storage_revision_tablename($field), + DatabaseStorageController::_fieldTableName($field), + DatabaseStorageController::_fieldRevisionTableName($field), ); foreach ($tables as $table_name) { // Skip fields which were created during the upgrade process. - if (!db_field_exists($table_name, $field->id() . '_fid')) { + if (!db_field_exists($table_name, $field->name . '_fid')) { continue 2; } - db_change_field($table_name, $field->id() . '_fid', $field->id() . '_target_id', array( + db_change_field($table_name, $field->name . '_fid', $field->name . '_target_id', array( 'description' => 'The ID of the target entity.', 'type' => 'int', 'unsigned' => TRUE, @@ -327,8 +328,8 @@ function file_update_8003() { )); // Change the index. - db_drop_index($table_name, $field->id() . '_fid'); - db_add_index($table_name, $field->id() . '_target_id', array($field->id() . '_target_id')); + db_drop_index($table_name, $field->name . '_fid'); + db_add_index($table_name, $field->name . '_target_id', array($field->name . '_target_id')); } // Update the indexes in field config as well. diff --git a/core/modules/file/file.module b/core/modules/file/file.module index e25a17b..0ccf22c 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -659,7 +659,7 @@ function file_file_download($uri, $field_type = 'file') { foreach ($references as $field_name => $field_references) { foreach ($field_references as $entity_type => $entities) { foreach ($entities as $entity) { - $field = field_info_field($field_name); + $field = field_info_field($entity_type, $field_name); // Check if access to this field is not disallowed. if (!field_access('view', $field, $entity_type, $entity)) { $denied = TRUE; @@ -1878,7 +1878,7 @@ function file_get_file_references(File $file, $field = NULL, $age = FIELD_LOAD_R // This contains the possible field names. $instances = field_info_instances($entity_type, $bundle); foreach ($instances as $field_name => $instance) { - $current_field = field_info_field($field_name); + $current_field = $instance->getField(); // If this is the first time this field type is seen, check // whether it references files. if (!isset($field_columns[$current_field['type']])) { @@ -1916,9 +1916,11 @@ function file_get_file_references(File $file, $field = NULL, $age = FIELD_LOAD_R // cache is very small so this will be very fast. if ($field || $field_type) { foreach ($return as $field_name => $data) { - $current_field = field_info_field($field_name); - if (($field_type && $current_field['type'] != $field_type) || ($field && $field['id'] != $current_field['id'])) { - unset($return[$field_name]); + foreach (array_keys($data) as $entity_type) { + $current_field = field_info_field($entity_type, $field_name); + if (($field_type && $current_field['type'] != $field_type) || ($field && $field['id'] != $current_field['id'])) { + unset($return[$field_name][$entity_type]); + } } } } diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc index db18c46..501ce7c 100644 --- a/core/modules/file/file.views.inc +++ b/core/modules/file/file.views.inc @@ -7,6 +7,9 @@ * @ingroup views_module_handlers */ +use Drupal\Core\Entity\DatabaseStorageController; +use Drupal\field\FieldInterface; + /** * Implements hook_views_data(). */ @@ -460,7 +463,7 @@ function file_views_data() { * * @see field_views_field_default_views_data() */ -function file_field_views_data($field) { +function file_field_views_data(FieldInterface $field) { $data = field_views_field_default_views_data($field); foreach ($data as $table_name => $table_data) { // Add the relationship only on the fid field. @@ -481,38 +484,31 @@ function file_field_views_data($field) { * * Views integration to provide reverse relationships on file fields. */ -function file_field_views_data_views_data_alter(&$data, $field) { - foreach ($field['bundles'] as $entity_type => $bundles) { - $entity_info = entity_get_info($entity_type); - $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; +function file_field_views_data_views_data_alter(array &$data, FieldInterface $field) { + $entity_type = $field['entity_type']; + $entity_info = entity_get_info($entity_type); + $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; - list($label, $all_labels) = field_views_field_label($field['field_name']); - $entity = $entity_info['label']; - if ($entity == t('Node')) { - $entity = t('Content'); - } + list($label,) = field_views_field_label($entity_type, $field['field_name']); + $entity_label = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label']; - $data['file_managed'][$pseudo_field_name]['relationship'] = array( - 'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)), - 'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity, '@field' => $label)), - 'id' => 'entity_reverse', - 'field_name' => $field['field_name'], - 'field table' => _field_sql_storage_tablename($field), - 'field field' => $field['field_name'] . '_target_id', - 'base' => $entity_info['base_table'], - 'base field' => $entity_info['entity_keys']['id'], - 'label' => t('!field_name', array('!field_name' => $field['field_name'])), - 'join_extra' => array( - 0 => array( - 'field' => 'entity_type', - 'value' => $entity_type, - ), - 1 => array( - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ), + $data['file_managed'][$pseudo_field_name]['relationship'] = array( + 'title' => t('@entity using @field', array('@entity' => $entity_label, '@field' => $label)), + 'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity_label, '@field' => $label)), + 'id' => 'entity_reverse', + 'field_name' => $field['field_name'], + 'entity_type' => $field['entity_type'], + 'field table' => DatabaseStorageController::_fieldTableName($field), + 'field field' => $field['field_name'] . '_target_id', + 'base' => $entity_info['base_table'], + 'base field' => $entity_info['entity_keys']['id'], + 'label' => t('!field_name', array('!field_name' => $field['field_name'])), + 'join_extra' => array( + 0 => array( + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, ), - ); - } + ), + ); } diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php index 4ef654d..0ed2f7f 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php @@ -36,7 +36,7 @@ function testNodeDisplay() { 'description_field' => '1', ); $widget_settings = array(); - $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings); + $this->createFileField($field_name, 'node', $type_name, $field_settings, $instance_settings, $widget_settings); // Create a new node *without* the file field set, and check that the field // is not shown for each node display. diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php index f2c45b6..3fa22c4 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php @@ -27,7 +27,7 @@ public static function getInfo() { function testUploadPath() { $field_name = strtolower($this->randomName()); $type_name = 'article'; - $this->createFileField($field_name, $type_name); + $this->createFileField($field_name, 'node', $type_name); $test_file = $this->getTestFile('text'); // Create a new node. diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php index d031ffe..e46f736 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php @@ -43,7 +43,7 @@ function testFileFieldRSSContent() { 'description_field' => '1', ); $widget_settings = array(); - $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings); + $this->createFileField($field_name, 'node', $type_name, $field_settings, $instance_settings, $widget_settings); // RSS display must be added manually. $this->drupalGet("admin/structure/types/manage/$type_name/display"); diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php index a6225b5..58fcafa 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php @@ -35,10 +35,9 @@ public static function getInfo() { function testRevisions() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name); - - // Attach the same fields to users. - $this->attachFileField($field_name, 'user', 'user'); + $this->createFileField($field_name, 'node', $type_name); + // Create the same fields for users. + $this->createFileField($field_name, 'user', 'user'); $test_file = $this->getTestFile('text'); diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php index ba51a17..9558047 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php @@ -57,8 +57,10 @@ function getLastFileId() { * * @param $name * The name of the new field (all lowercase), exclude the "field_" prefix. - * @param $type_name - * The node type that this field will be added to. + * @param $entity_type + * The entity type. + * @param $bundle + * The bundle that this field will be added to. * @param $field_settings * A list of field settings that will be added to the defaults. * @param $instance_settings @@ -66,9 +68,10 @@ function getLastFileId() { * @param $widget_settings * A list of widget settings that will be added to the widget defaults. */ - function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) { + function createFileField($name, $entity_type, $bundle, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) { $field_definition = array( - 'field_name' => $name, + 'entity_type' => $entity_type, + 'name' => $name, 'type' => 'file', 'settings' => array(), 'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1, @@ -77,7 +80,7 @@ function createFileField($name, $type_name, $field_settings = array(), $instance $field = entity_create('field_entity', $field_definition); $field->save(); - $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings); + $this->attachFileField($name, $entity_type, $bundle, $instance_settings, $widget_settings); return $field; } @@ -159,7 +162,7 @@ function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE, } // Attach a file to the node. - $field = field_info_field($field_name); + $field = field_info_field('node', $field_name); $name = 'files[' . $field_name . '_' . $langcode . '_0]'; if ($field['cardinality'] != 1) { $name .= '[]'; diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php index a7cf1e5..670ce91 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php @@ -30,8 +30,8 @@ public static function getInfo() { function testRequired() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $field = $this->createFileField($field_name, $type_name, array(), array('required' => '1')); - $instance = field_info_instance('node', $field_name, $type_name); + $field = $this->createFileField($field_name, 'node', $type_name, array(), array('required' => '1')); + $instance = field_info_instance($field_name, 'node', $type_name); $test_file = $this->getTestFile('text'); @@ -53,7 +53,7 @@ function testRequired() { // Try again with a multiple value field. $field->delete(); - $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1')); + $this->createFileField($field_name, 'node', $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1')); // Try to post a new node without uploading a file in the multivalue field. $edit = array('title' => $this->randomName()); @@ -74,7 +74,7 @@ function testRequired() { function testFileMaxSize() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name, array(), array('required' => '1')); + $this->createFileField($field_name, 'node', $type_name, array(), array('required' => '1')); $small_file = $this->getTestFile('text', 131072); // 128KB. $large_file = $this->getTestFile('text', 1310720); // 1.2MB @@ -120,7 +120,7 @@ function testFileMaxSize() { function testFileExtension() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name); + $this->createFileField($field_name, 'node', $type_name); $test_file = $this->getTestFile('image'); list(, $test_file_extension) = explode('.', $test_file->getFilename()); diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php index e481384..1f8e9d6 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php @@ -35,7 +35,7 @@ public static function getInfo() { function testSingleValuedWidget() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name); + $this->createFileField($field_name, 'node', $type_name); $test_file = $this->getTestFile('text'); @@ -96,8 +96,8 @@ function testMultiValuedWidget() { // names). $field_name = 'test_file_field_1'; $field_name2 = 'test_file_field_2'; - $this->createFileField($field_name, $type_name, array('cardinality' => 3)); - $this->createFileField($field_name2, $type_name, array('cardinality' => 3)); + $this->createFileField($field_name, 'node', $type_name, array('cardinality' => 3)); + $this->createFileField($field_name2, 'node', $type_name, array('cardinality' => 3)); $test_file = $this->getTestFile('text'); @@ -206,7 +206,7 @@ function testMultiValuedWidget() { function testPrivateFileSetting() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name); + $this->createFileField($field_name, 'node', $type_name); $instance = field_info_instance('node', $field_name, $type_name); $test_file = $this->getTestFile('text'); @@ -316,7 +316,7 @@ function testPrivateFileComment() { function testWidgetValidation() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name); + $this->createFileField($field_name, 'node', $type_name); $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt')); foreach (array('nojs', 'js') as $type) { @@ -326,7 +326,7 @@ function testWidgetValidation() { $this->drupalGet("node/$nid/edit"); $test_file_text = $this->getTestFile('text'); $test_file_image = $this->getTestFile('image'); - $field = field_info_field($field_name); + $field = field_info_field('ndde', $field_name); $name = 'files[' . $field_name . '_' . Language::LANGCODE_NOT_SPECIFIED . '_0]'; // Upload file with incorrect extension, check for validation error. diff --git a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php index 3aacead..39674e2 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php @@ -45,7 +45,8 @@ public function setUp() { $this->installSchema('file', 'file_usage'); entity_create('field_entity', array( - 'field_name' => 'file_test', + 'name' => 'file_test', + 'entity_type' => 'entity_test', 'type' => 'file', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, ))->save(); diff --git a/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php b/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php index 693f236..fd8a98d 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php @@ -34,7 +34,7 @@ function setUp() { $this->admin_user = $this->drupalCreateUser(array('access files overview', 'bypass node access')); $this->base_user = $this->drupalCreateUser(); - $this->createFileField('file', 'article', array(), array('file_extensions' => 'txt png')); + $this->createFileField('file', 'node', 'article', array(), array('file_extensions' => 'txt png')); } /** diff --git a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php index 1b8a82f..bf338e0 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php @@ -41,11 +41,11 @@ public function setUp() { function testPrivateFile() { $type_name = 'article'; $field_name = strtolower($this->randomName()); - $this->createFileField($field_name, $type_name, array('uri_scheme' => 'private')); + $this->createFileField($field_name, 'node', $type_name, array('uri_scheme' => 'private')); // Create a field with no view access - see field_test_field_access(). $no_access_field_name = 'field_no_view_access'; - $this->createFileField($no_access_field_name, $type_name, array('uri_scheme' => 'private')); + $this->createFileField($no_access_field_name, 'node', $type_name, array('uri_scheme' => 'private')); $test_file = $this->getTestFile('text'); $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE)); diff --git a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php index 36d323d..b9ba145 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php @@ -35,7 +35,7 @@ function testFileTokenReplacement() { // Create file field. $type_name = 'article'; $field_name = 'field_' . strtolower($this->randomName()); - $this->createFileField($field_name, $type_name); + $this->createFileField($field_name, 'node', $type_name); $test_file = $this->getTestFile('text'); // Coping a file to test uploads with non-latin filenames. diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 60efc30..adc8ddb 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -51,9 +51,10 @@ function forum_enable() { // Create the 'taxonomy_forums' field if it doesn't already exist. If forum // is being enabled at the same time as taxonomy after both modules have been // enabled, the field might exist but still be marked inactive. - if (!field_read_field('taxonomy_forums', array('include_inactive' => TRUE))) { + if (!field_read_field('node', 'taxonomy_forums', array('include_inactive' => TRUE))) { entity_create('field_entity', array( - 'field_name' => 'taxonomy_forums', + 'name' => 'taxonomy_forums', + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'settings' => array( 'allowed_values' => array( @@ -114,7 +115,7 @@ function forum_uninstall() { // Load the dependent Taxonomy module, in case it has been disabled. drupal_load('module', 'taxonomy'); - if ($field = field_info_field('taxonomy_forums')) { + if ($field = field_info_field('node', 'taxonomy_forums')) { $field->delete(); } diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 41d2604..8ff0994 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -171,8 +171,8 @@ function forum_menu_local_tasks(&$data, $router_item, $root_path) { if ($forum_term) { $links = array(); // Loop through all bundles for forum taxonomy vocabulary field. - $field = field_info_field('taxonomy_forums'); - foreach ($field['bundles']['node'] as $type_name) { + $field = field_info_field('node', 'taxonomy_forums'); + foreach ($field['bundles'] as $type_name) { if (($type = entity_load('node_type', $type_name)) && node_access('create', $type_name)) { $links[$type_name] = array( '#theme' => 'menu_local_action', @@ -377,6 +377,34 @@ function forum_node_update(EntityInterface $node) { )) ->execute(); } + + // If the node is published, update the forum index. + if ($node->isPublished()) { + db_delete('forum_index')->condition('nid', $node->id())->execute(); + $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp')); + foreach ($node->getTranslationLanguages() as $langcode => $language) { + $translation = $node->getTranslation($langcode); + foreach ($translation->taxonomy_forums as $item) { + $query->values(array( + 'nid' => $node->id(), + 'title' => $translation->label(), + 'tid' => $item->target_id, + 'sticky' => (int) $node->isSticky(), + 'created' => $node->getCreatedTime(), + 'comment_count' => 0, + 'last_comment_timestamp' => $node->getCreatedTime(), + )); + } + } + $query->execute(); + // The logic for determining last_comment_count is fairly complex, so + // call _forum_update_forum_index() too. + _forum_update_forum_index($node->id()); + } + // When a forum node is unpublished, remove it from the forum_index table. + else { + db_delete('forum_index')->condition('nid', $node->id())->execute(); + } } } @@ -394,6 +422,23 @@ function forum_node_insert(EntityInterface $node) { )) ->execute(); } + + if ($node->isPublished()) { + $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp')); + foreach ($node->getTranslationLanguages() as $langcode => $language) { + $translation = $node->getTranslation($langcode); + $query->values(array( + 'nid' => $node->id(), + 'title' => $translation->label(), + 'tid' => $translation->taxonomy_forums->target_id, + 'sticky' => (int) $node->isSticky(), + 'created' => $node->getCreatedTime(), + 'comment_count' => 0, + 'last_comment_timestamp' => $node->getCreatedTime(), + )); + } + $query->execute(); + } } } @@ -498,75 +543,6 @@ function forum_comment_delete($comment) { } /** - * Implements hook_field_storage_pre_insert(). - */ -function forum_field_storage_pre_insert(EntityInterface $entity, &$skip_fields) { - if ($entity->entityType() == 'node' && $entity->isPublished() && _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->getTranslationLanguages() as $langcode => $language) { - $translation = $entity->getTranslation($langcode); - $query->values(array( - 'nid' => $entity->id(), - 'title' => $translation->label(), - 'tid' => $translation->taxonomy_forums->target_id, - 'sticky' => (int) $entity->isSticky(), - 'created' => $entity->getCreatedTime(), - 'comment_count' => 0, - 'last_comment_timestamp' => $entity->getCreatedTime(), - )); - } - $query->execute(); - } -} - -/** - * Implements hook_field_storage_pre_update(). - */ -function forum_field_storage_pre_update(EntityInterface $entity, &$skip_fields) { - $first_call = &drupal_static(__FUNCTION__, array()); - - if ($entity->entityType() == 'node' && _forum_node_check_node_type($entity)) { - - // If the node is published, update the forum index. - if ($entity->isPublished()) { - - // We don't maintain data for old revisions, so clear all previous values - // from the table. Since this hook runs once per field, per object, 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(); - } - $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp')); - foreach ($entity->getTranslationLanguages() as $langcode => $language) { - $translation = $entity->getTranslation($langcode); - foreach ($translation->taxonomy_forums as $item) { - $query->values(array( - 'nid' => $entity->id(), - 'title' => $translation->label(), - 'tid' => $item->target_id, - 'sticky' => (int) $entity->isSticky(), - 'created' => $entity->getCreatedTime(), - 'comment_count' => 0, - 'last_comment_timestamp' => $entity->getCreatedTime(), - )); - } - } - $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()); - } - - // When a forum node is unpublished, remove it from the forum_index table. - else { - db_delete('forum_index')->condition('nid', $entity->id())->execute(); - } - - } -} - -/** * Implements hook_form_BASE_FORM_ID_alter(). */ function forum_form_taxonomy_vocabulary_form_alter(&$form, &$form_state, $form_id) { diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php index 0fe25e3..cce8db0 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php @@ -34,7 +34,7 @@ public static function getInfo() { */ function testForumUninstallWithField() { // Ensure that the field exists before uninstallation. - $field = field_info_field('taxonomy_forums'); + $field = field_info_field('node', 'taxonomy_forums'); $this->assertNotNull($field, 'The taxonomy_forums field exists.'); // Uninstall the forum module which should trigger field deletion. @@ -42,7 +42,7 @@ function testForumUninstallWithField() { $this->container->get('module_handler')->uninstall(array('forum')); // Check that the field is now deleted. - $field = field_info_field('taxonomy_forums'); + $field = field_info_field('node', 'taxonomy_forums'); $this->assertNull($field, 'The taxonomy_forums field has been deleted.'); } @@ -52,12 +52,12 @@ function testForumUninstallWithField() { */ function testForumUninstallWithoutField() { // Manually delete the taxonomy_forums field before module uninstallation. - $field = field_info_field('taxonomy_forums'); + $field = field_info_field('node', 'taxonomy_forums'); $this->assertNotNull($field, 'The taxonomy_forums field exists.'); $field->delete(); // Check that the field is now deleted. - $field = field_info_field('taxonomy_forums'); + $field = field_info_field('node', 'taxonomy_forums'); $this->assertNull($field, 'The taxonomy_forums field has been deleted.'); // Ensure that uninstallation succeeds even if the field has already been diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php index 0c1bcaa..794f41d 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php @@ -78,7 +78,8 @@ function setUp() { // Create the test text field. entity_create('field_entity', array( - 'field_name' => 'field_test_text', + 'name' => 'field_test_text', + 'entity_type' => 'entity_test', 'type' => 'text', 'translatable' => FALSE, ))->save(); @@ -90,7 +91,8 @@ function setUp() { // Create the test translatable field. entity_create('field_entity', array( - 'field_name' => 'field_test_translatable_text', + 'name' => 'field_test_translatable_text', + 'entity_type' => 'entity_test', 'type' => 'text', 'translatable' => TRUE, ))->save(); @@ -102,12 +104,13 @@ function setUp() { // Create the test entity reference field. entity_create('field_entity', array( + 'name' => 'field_test_entity_reference', + 'entity_type' => 'entity_test', + 'type' => 'entity_reference', 'translatable' => TRUE, 'settings' => array( 'target_type' => 'entity_test', ), - 'field_name' => 'field_test_entity_reference', - 'type' => 'entity_reference', ))->save(); entity_create('field_instance', array( 'entity_type' => 'entity_test', diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc index 2e70c08..356f140 100644 --- a/core/modules/image/image.views.inc +++ b/core/modules/image/image.views.inc @@ -7,6 +7,9 @@ * @ingroup views_module_handlers */ +use Drupal\Core\Entity\DatabaseStorageController; +use Drupal\field\FieldInterface; + /** * Implements hook_field_views_data(). * @@ -15,7 +18,7 @@ * * @see field_views_field_default_views_data() */ -function image_field_views_data($field) { +function image_field_views_data(FieldInterface $field) { $data = field_views_field_default_views_data($field); foreach ($data as $table_name => $table_data) { // Add the relationship only on the target_id field. @@ -35,38 +38,31 @@ function image_field_views_data($field) { * * Views integration to provide reverse relationships on image fields. */ -function image_field_views_data_views_data_alter(&$data, $field) { - foreach ($field['bundles'] as $entity_type => $bundles) { - $entity_info = entity_get_info($entity_type); - $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; +function image_field_views_data_views_data_alter(array &$data, FieldInterface $field) { + $entity_type = $field['entity_type']; + $entity_info = entity_get_info($entity_type); + $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; - list($label, $all_labels) = field_views_field_label($field['field_name']); - $entity = $entity_info['label']; - if ($entity == t('Node')) { - $entity = t('Content'); - } + list($label,) = field_views_field_label($entity_type, $field['field_name']); + $entity_label = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label']; - $data['file_managed'][$pseudo_field_name]['relationship'] = array( - 'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)), - 'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity, '@field' => $label)), - 'id' => 'entity_reverse', - 'field_name' => $field['field_name'], - 'field table' => _field_sql_storage_tablename($field), - 'field field' => $field['field_name'] . '_target_id', - 'base' => $entity_info['base_table'], - 'base field' => $entity_info['entity_keys']['id'], - 'label' => t('!field_name', array('!field_name' => $field['field_name'])), - 'join_extra' => array( - 0 => array( - 'field' => 'entity_type', - 'value' => $entity_type, - ), - 1 => array( - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ), + $data['file_managed'][$pseudo_field_name]['relationship'] = array( + 'title' => t('@entity using @field', array('@entity' => $entity_label, '@field' => $label)), + 'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity_label, '@field' => $label)), + 'id' => 'entity_reverse', + 'field_name' => $field['field_name'], + 'entity_type' => $field['entity_type'], + 'field table' => DatabaseStorageController::_fieldTableName($field), + 'field field' => $field['field_name'] . '_target_id', + 'base' => $entity_info['base_table'], + 'base field' => $entity_info['entity_keys']['id'], + 'label' => t('!field_name', array('!field_name' => $field['field_name'])), + 'join_extra' => array( + 0 => array( + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, ), - ); - } + ), + ); } diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php index f926abc..45b2eef 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php @@ -60,7 +60,7 @@ public function testDefaultImages() { $instance_field_settings = $instance->getFieldSettings(); $this->assertEqual($instance_field_settings['default_image'], $default_images['instance']->id()); - $field = entity_load('field_entity', $field_name); + $field = $instance->getField(); // The field default image id should be 1. $this->assertEqual($field->getFieldSetting('default_image'), $default_images['field']->id()); @@ -71,7 +71,7 @@ public function testDefaultImages() { // Add another instance with another default image to the page content type. $instance2 = entity_create('field_instance', array( - 'field_name' => $field->id(), + 'field_name' => $field->name, 'entity_type' => 'node', 'bundle' => 'page', 'label' => $instance->label(), @@ -82,12 +82,12 @@ public function testDefaultImages() { )); $instance2->save(); - $widget_settings = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')->getComponent($field['field_name']); + $widget_settings = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')->getComponent($field_name); entity_get_form_display('node', 'page', 'default') - ->setComponent($field->id(), $widget_settings) + ->setComponent($field_name, $widget_settings) ->save(); entity_get_display('node', 'page', 'default') - ->setComponent($field->id()) + ->setComponent($field_name) ->save(); // Confirm the defaults are present on the article field settings form. diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php index 27e67ef..ff276cd 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php @@ -231,7 +231,7 @@ function testImageFieldDefaultImage() { $this->drupalPost("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings')); // Clear field info cache so the new default image is detected. field_info_cache_clear(); - $field = field_info_field($field_name); + $field = field_info_field('node', $field_name); $image = file_load($field['settings']['default_image']); $this->assertTrue($image->isPermanent(), 'The default image status is permanent.'); $default_output = theme('image', array('uri' => $image->getFileUri())); @@ -259,7 +259,7 @@ function testImageFieldDefaultImage() { $this->drupalPost("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings')); // Clear field info cache so the new default image is detected. field_info_cache_clear(); - $field = field_info_field($field_name); + $field = field_info_field('node', $field_name); $this->assertFalse($field['settings']['default_image'], 'Default image removed from field.'); // Create an image field that uses the private:// scheme and test that the // default image works as expected. @@ -273,7 +273,7 @@ function testImageFieldDefaultImage() { // Clear field info cache so the new default image is detected. field_info_cache_clear(); - $private_field = field_info_field($private_field_name); + $private_field = field_info_field('node', $private_field_name); $image = file_load($private_field['settings']['default_image']); $this->assertEqual('private', file_uri_scheme($image->getFileUri()), 'Default image uses private:// scheme.'); $this->assertTrue($image->isPermanent(), 'The default image status is permanent.'); diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php index 7d0f054..5dc38f3 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php @@ -66,7 +66,8 @@ function setUp() { */ function createImageField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) { $field = array( - 'field_name' => $name, + 'name' => $name, + 'entity_type' => 'node', 'type' => 'image', 'settings' => array(), 'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1, @@ -75,9 +76,9 @@ function createImageField($name, $type_name, $field_settings = array(), $instanc entity_create('field_entity', $field)->save(); $instance = array( - 'field_name' => $field['field_name'], - 'entity_type' => 'node', + 'field_name' => $field['name'], 'label' => $name, + 'entity_type' => 'node', 'bundle' => $type_name, 'required' => !empty($instance_settings['required']), 'description' => !empty($instance_settings['description']) ? $instance_settings['description'] : '', @@ -88,14 +89,14 @@ function createImageField($name, $type_name, $field_settings = array(), $instanc $field_instance->save(); entity_get_form_display('node', $type_name, 'default') - ->setComponent($field['field_name'], array( + ->setComponent($field['name'], array( 'type' => 'image_image', 'settings' => $widget_settings, )) ->save(); entity_get_display('node', $type_name, 'default') - ->setComponent($field['field_name']) + ->setComponent($field['name']) ->save(); return $field_instance; diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php index 8a51694..cff56f4 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php @@ -49,7 +49,8 @@ public function setUp() { $this->installSchema('file', array('file_managed', 'file_usage')); entity_create('field_entity', array( - 'field_name' => 'image_test', + 'name' => 'image_test', + 'entity_type' => 'entity_test', 'type' => 'image', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, ))->save(); diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php index c3c62ef..8a973d0 100644 --- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php +++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php @@ -68,7 +68,8 @@ function testURLValidation() { $field_name = drupal_strtolower($this->randomName()); // Create a field with settings to validate. $this->field = entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'entity_test', 'type' => 'link', )); $this->field->save(); @@ -142,7 +143,8 @@ function testLinkTitle() { $field_name = drupal_strtolower($this->randomName()); // Create a field with settings to validate. $this->field = entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'entity_test', 'type' => 'link', )); $this->field->save(); @@ -261,7 +263,8 @@ function testLinkFormatter() { $field_name = drupal_strtolower($this->randomName()); // Create a field with settings to validate. $this->field = entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'entity_test', 'type' => 'link', 'cardinality' => 2, )); @@ -404,7 +407,8 @@ function testLinkSeparateFormatter() { $field_name = drupal_strtolower($this->randomName()); // Create a field with settings to validate. $this->field = entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'entity_test', 'type' => 'link', 'cardinality' => 2, )); diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php index 2cd88bc..a42d6a2 100644 --- a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php +++ b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php @@ -36,7 +36,8 @@ public function setUp() { // Create an link field and instance for validation. entity_create('field_entity', array( - 'field_name' => 'field_test', + 'name' => 'field_test', + 'entity_type' => 'entity_test', 'type' => 'link', ))->save(); entity_create('field_instance', array( diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php index 43d54ce..7242cbe 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Database\Connection; +use Drupal\field\FieldInfo; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Cmf\Component\Routing\RouteProviderInterface; @@ -52,11 +53,13 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men * 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. * @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider * The route provider service. */ - public function __construct($entity_type, array $entity_info, Connection $database, RouteProviderInterface $route_provider) { - parent::__construct($entity_type, $entity_info, $database); + public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, RouteProviderInterface $route_provider) { + parent::__construct($entity_type, $entity_info, $database, $field_info); $this->routeProvider = $route_provider; @@ -85,6 +88,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_type, $entity_info, $container->get('database'), + $container->get('field.info'), $container->get('router.route_provider') ); } diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php index 1a874fe..3318acb 100644 --- a/core/modules/node/lib/Drupal/node/NodeStorageController.php +++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\DatabaseStorageControllerNG; use Drupal\Core\Entity\EntityInterface; +use Symfony\Component\DependencyInjection\Container; /** * Controller class for nodes. @@ -46,10 +47,10 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) { } if ($load_revision) { - field_attach_load_revision($this->entityType, $queried_entities); + $this->fieldLoad($queried_entities, FIELD_LOAD_REVISION); } else { - field_attach_load($this->entityType, $queried_entities); + $this->fieldLoad($queried_entities, FIELD_LOAD_CURRENT); } // Besides the list of nodes, pass one additional argument to @@ -80,14 +81,9 @@ protected function invokeHook($hook, EntityInterface $node) { // Inline parent::invokeHook() to pass on BC-entities to node-specific // hooks. - $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($node); + $method = Container::camelize('field_' . $hook); + if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) { + $this->$method($node); } // Invoke the hook. diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php index acba6b6..2d55c6e 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php @@ -306,9 +306,10 @@ protected function buildFilters(&$form, &$form_state) { $form['displays']['show']['tagged_with'] = array( '#type' => 'textfield', '#title' => t('tagged with'), - '#autocomplete_path' => 'taxonomy/autocomplete/' . $tag_field_name, + '#autocomplete_path' => 'taxonomy/autocomplete/' . $this->entity_type . '/' . $tag_field_name, '#size' => 30, '#maxlength' => 1024, + '#entity_type' => $this->entity_type, '#field_name' => $tag_field_name, '#element_validate' => array('views_ui_taxonomy_autocomplete_validate'), ); diff --git a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php index 65e3aec..4fc3f7b 100644 --- a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php @@ -41,7 +41,8 @@ function testMultiStepNodeFormBasicOptions() { // Create an unlimited cardinality field. $this->field_name = drupal_strtolower($this->randomName()); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'text', 'cardinality' => -1, ))->save(); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php index 8b4bf7a..944aa4f 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php @@ -62,7 +62,8 @@ public function setUp() { // Add a custom field to the page content type. $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'text' ))->save(); entity_create('field_instance', array( diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php index 6df2698..6b451a0 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php @@ -59,7 +59,7 @@ function setUp() { $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.'); // Make node body translatable. - $field = field_info_field('body'); + $field = field_info_field('node', 'body'); $field->translatable = TRUE; $field->save(); } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php index dac3575..7c852d9 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php @@ -169,7 +169,7 @@ function testFieldTranslationForm() { $this->assertRaw('Not translated'); // Delete the only translatable field. - field_info_field('field_test_et_ui_test')->delete(); + field_info_field($this->entityType, 'field_test_et_ui_test')->delete(); // Visit translation page. $this->drupalGet('node/' . $article->id() . '/translations'); diff --git a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php index 0538e00..f9e3670 100644 --- a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php @@ -68,7 +68,8 @@ function setUp() { // Set up a field and instance. $this->field_name = drupal_strtolower($this->randomName()); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'settings' => array( 'allowed_values' => array( diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 2353be3..b096d53 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -395,13 +395,13 @@ function node_type_load($name) { */ function node_add_body_field(NodeTypeInterface $type, $label = 'Body') { // Add or remove the body field, as needed. - $field = field_info_field('body'); + $field = field_info_field('node', 'body'); $instance = field_info_instance('node', 'body', $type->id()); if (empty($field)) { $field = entity_create('field_entity', array( - 'field_name' => 'body', + 'name' => 'body', + 'entity_type' => 'node', 'type' => 'text_with_summary', - 'entity_types' => array('node'), )); $field->save(); } @@ -417,20 +417,20 @@ function node_add_body_field(NodeTypeInterface $type, $label = 'Body') { // Assign widget settings for the 'default' form mode. entity_get_form_display('node', $type->type, 'default') - ->setComponent($field->id(), array( + ->setComponent('body', array( 'type' => 'text_textarea_with_summary', )) ->save(); // Assign display settings for the 'default' and 'teaser' view modes. entity_get_display('node', $type->type, 'default') - ->setComponent($field->id(), array( + ->setComponent('body', array( 'label' => 'hidden', 'type' => 'text_default', )) ->save(); entity_get_display('node', $type->type, 'teaser') - ->setComponent($field->id(), array( + ->setComponent('body', array( 'label' => 'hidden', 'type' => 'text_summary_or_trimmed', )) diff --git a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module index 1baa48e..5b54e13 100644 --- a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module +++ b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module @@ -50,7 +50,8 @@ function node_access_test_language_node_access_records(EntityInterface $node) { */ function node_access_test_language_enable() { $field_private = entity_create('field_entity', array( - 'field_name' => 'field_private', + 'name' => 'field_private', + 'entity_type' => 'node', 'type' => 'list_boolean', 'cardinality' => 1, 'translatable' => TRUE, @@ -61,7 +62,7 @@ function node_access_test_language_enable() { $field_private->save(); entity_create('field_instance', array( - 'field_name' => $field_private->id(), + 'field_name' => $field_private->name, 'entity_type' => 'node', 'bundle' => 'page', 'widget' => array( diff --git a/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php b/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php index 1edfd9f..f777f36 100644 --- a/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php +++ b/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php @@ -64,7 +64,8 @@ function setUp() { function testNumberDecimalField() { // 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' => 'number_decimal', 'settings' => array( 'precision' => 8, 'scale' => 4, 'decimal_separator' => '.', @@ -72,13 +73,13 @@ function testNumberDecimalField() { )); $this->field->save(); entity_create('field_instance', array( - 'field_name' => $this->field->id(), + 'field_name' => $this->field->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', ))->save(); entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->field->id(), array( + ->setComponent($this->field->name, array( 'type' => 'number', 'settings' => array( 'placeholder' => '0.00' @@ -86,7 +87,7 @@ function testNumberDecimalField() { )) ->save(); entity_get_display('entity_test', 'entity_test', 'default') - ->setComponent($this->field->id(), array( + ->setComponent($this->field->name, array( 'type' => 'number_decimal', )) ->save(); diff --git a/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php b/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php index a0cc904..636c55d 100644 --- a/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php +++ b/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php @@ -37,7 +37,8 @@ public function setUp() { // Create number fields and instances for validation. foreach (array('integer', 'float', 'decimal') as $type) { entity_create('field_entity', array( - 'field_name' => 'field_' . $type, + 'name' => 'field_' . $type, + 'entity_type' => 'entity_test', 'type' => 'number_' . $type, ))->save(); entity_create('field_instance', array( diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php index c270e4a..6607332 100644 --- a/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php +++ b/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php @@ -33,7 +33,8 @@ function setUp() { $this->field_name = 'test_options'; entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'entity_test_rev', 'type' => 'list_text', 'cardinality' => 1, 'settings' => array( diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php index 2b45acd..34f0a87 100644 --- a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php +++ b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php @@ -231,7 +231,7 @@ function testOptionsAllowedValuesBoolean() { $this->drupalGet($this->admin_path); $this->assertFieldByName('on', $on, t("The 'On' value is stored correctly.")); $this->assertFieldByName('off', $off, t("The 'Off' value is stored correctly.")); - $field = field_info_field($this->field_name); + $field = field_info_field('node', $this->field_name); $this->assertEqual($field['settings']['allowed_values'], $allowed_values, 'The allowed value is correct'); $this->assertFalse(isset($field['settings']['on']), 'The on value is not saved into settings'); $this->assertFalse(isset($field['settings']['off']), 'The off value is not saved into settings'); @@ -259,7 +259,8 @@ function testOptionsTrimmedValuesText() { protected function createOptionsField($type) { // Create a test field and instance. entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => $type, ))->save(); entity_create('field_instance', array( @@ -294,7 +295,7 @@ function assertAllowedValuesInput($input_string, $result, $message) { } else { field_info_cache_clear(); - $field = field_info_field($this->field_name); + $field = field_info_field('node', $this->field_name); $this->assertIdentical($field['settings']['allowed_values'], $result, $message); } } diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php index eda24da..893030e 100644 --- a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php +++ b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php @@ -58,7 +58,8 @@ public function setUp() { $this->installSchema('system', 'menu_router'); $this->fieldDefinition = array( - 'field_name' => $this->fieldName, + 'name' => $this->fieldName, + 'entity_type' => 'entity_test', 'type' => 'list_integer', 'cardinality' => 1, 'settings' => array( diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php index db77e61..bf76dc7 100644 --- a/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php +++ b/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php @@ -64,7 +64,8 @@ function setUp() { // Field with cardinality 1. $this->card_1 = entity_create('field_entity', array( - 'field_name' => 'card_1', + 'name' => 'card_1', + 'entity_type' => 'entity_test', 'type' => 'list_integer', 'cardinality' => 1, 'settings' => array( @@ -76,7 +77,8 @@ function setUp() { // Field with cardinality 2. $this->card_2 = entity_create('field_entity', array( - 'field_name' => 'card_2', + 'name' => 'card_2', + 'entity_type' => 'entity_test', 'type' => 'list_integer', 'cardinality' => 2, 'settings' => array( @@ -88,7 +90,8 @@ function setUp() { // Boolean field. $this->bool = entity_create('field_entity', array( - 'field_name' => 'bool', + 'name' => 'bool', + 'entity_type' => 'entity_test', 'type' => 'list_boolean', 'cardinality' => 1, 'settings' => array( @@ -108,13 +111,13 @@ function setUp() { function testRadioButtons() { // Create an instance of the 'single value' field. $instance = entity_create('field_instance', array( - 'field_name' => $this->card_1->id(), + 'field_name' => $this->card_1->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', )); $instance->save(); entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->card_1->id(), array( + ->setComponent($this->card_1->name, array( 'type' => 'options_buttons', )) ->save(); @@ -167,13 +170,13 @@ function testRadioButtons() { function testCheckBoxes() { // Create an instance of the 'multiple values' field. $instance = entity_create('field_instance', array( - 'field_name' => $this->card_2->id(), + 'field_name' => $this->card_2->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', )); $instance->save(); entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->card_2->id(), array( + ->setComponent($this->card_2->name, array( 'type' => 'options_buttons', )) ->save(); @@ -259,14 +262,14 @@ function testCheckBoxes() { function testSelectListSingle() { // Create an instance of the 'single value' field. $instance = entity_create('field_instance', array( - 'field_name' => $this->card_1->id(), + 'field_name' => $this->card_1->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', 'required' => TRUE, )); $instance->save(); entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->card_1->id(), array( + ->setComponent($this->card_1->name, array( 'type' => 'options_select', )) ->save(); @@ -361,13 +364,13 @@ function testSelectListSingle() { function testSelectListMultiple() { // Create an instance of the 'multiple values' field. $instance = entity_create('field_instance', array( - 'field_name' => $this->card_2->id(), + 'field_name' => $this->card_2->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', )); $instance->save(); entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->card_2->id(), array( + ->setComponent($this->card_2->name, array( 'type' => 'options_select', )) ->save(); @@ -483,12 +486,12 @@ function testSelectListMultiple() { function testOnOffCheckbox() { // Create an instance of the 'boolean' field. entity_create('field_instance', array( - 'field_name' => $this->bool->id(), + 'field_name' => $this->bool->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', ))->save(); entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->bool->id(), array( + ->setComponent($this->bool->name, array( 'type' => 'options_onoff', )) ->save(); @@ -525,7 +528,12 @@ function testOnOffCheckbox() { // Display form: with 'off' value, option is unchecked. $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit'); $this->assertNoFieldChecked("edit-bool-$langcode"); + } + /** + * Tests that the 'options_onoff' widget has a 'display_label' setting. + */ + function testOnOffCheckboxLabelSetting() { // Create Basic page node type. $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); @@ -534,11 +542,17 @@ function testOnOffCheckbox() { $this->drupalLogin($admin_user); // Create a test field instance. - $fieldUpdate = $this->bool; - $fieldUpdate['settings']['allowed_values'] = array(0 => 0, 1 => 'MyOnValue'); - $fieldUpdate->save(); + entity_create('field_entity', array( + 'name' => 'bool', + 'entity_type' => 'node', + 'type' => 'list_boolean', + 'cardinality' => 1, + 'settings' => array( + 'allowed_values' => array(0 => 'Zero', 1 => 'Some & unescaped markup'), + ), + ))->save(); entity_create('field_instance', array( - 'field_name' => $this->bool['field_name'], + 'field_name' => 'bool', 'entity_type' => 'node', 'bundle' => 'page', ))->save(); @@ -584,4 +598,5 @@ function testOnOffCheckbox() { t('Display label changes label of the checkbox') ); } + } diff --git a/core/modules/options/options.module b/core/modules/options/options.module index e6ebdbe..6dedc11 100644 --- a/core/modules/options/options.module +++ b/core/modules/options/options.module @@ -384,16 +384,14 @@ function _options_values_in_use($field, $values) { if ($values) { $field = field_info_field_by_id($field['uuid']); $factory = Drupal::service('entity.query'); - foreach ($field['bundles'] as $entity_type => $bundle) { - $result = $factory->get($entity_type) - ->condition($field['field_name'] . '.value', $values) - ->count() - ->accessCheck(FALSE) - ->range(0, 1) - ->execute(); - if ($result) { - return TRUE; - } + $result = $factory->get($field->entity_type) + ->condition($field['field_name'] . '.value', $values) + ->count() + ->accessCheck(FALSE) + ->range(0, 1) + ->execute(); + if ($result) { + return TRUE; } } diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php index 11176dc..46119d5 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php @@ -56,7 +56,7 @@ public function setUp() { $this->fieldName = strtolower($this->randomName()); $type_name = 'article'; - $this->createFileField($this->fieldName, $type_name); + $this->createFileField($this->fieldName, 'node', $type_name); // Set the teaser display to show this field. entity_get_display('node', 'article', 'teaser') diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php index 5d298d9..5d94340 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php @@ -154,7 +154,8 @@ function testNodeTeaser() { */ protected function createTaxonomyTermReferenceField($field_name, $vocabulary) { entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php index 0e3fd91..9c7815c 100644 --- a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php +++ b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php @@ -74,16 +74,14 @@ public function getRelations() { protected function writeCache() { $data = array(); - foreach (field_info_fields() as $field_info) { - foreach ($field_info['bundles'] as $entity_type => $bundles) { - foreach ($bundles as $bundle) { - $relation_uri = $this->getRelationUri($entity_type, $bundle, $field_info['field_name']); - $data[$relation_uri] = array( - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'field_name' => $field_info['field_name'], - ); - } + foreach (field_info_fields() as $field) { + foreach ($field['bundles'] as $bundle) { + $relation_uri = $this->getRelationUri($field->entity_type, $bundle, $field->name); + $data[$relation_uri] = array( + 'entity_type' => $field->entity_type, + 'bundle' => $bundle, + 'field_name' => $field->name, + ); } } // These URIs only change when field info changes, so cache it permanently diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php index e1021b4..dcb5bb2 100644 --- a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php +++ b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php @@ -48,7 +48,7 @@ function setUp() { // Make the body field translatable. // The parent class has already created the article and page content types. - $field = field_info_field('body'); + $field = field_info_field('node', 'body'); $field->translatable = TRUE; $field->save(); diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php b/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php index fa20263..73c515c 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php @@ -38,7 +38,8 @@ protected function setUp() { 'settings' => array( 'target_type' => 'entity_test_mulrev', ), - 'field_name' => 'field_test_entity_reference', + 'name' => 'field_test_entity_reference', + 'entity_type' => 'entity_test_mulrev', 'type' => 'entity_reference', ))->save(); diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php index 74fa8d9..8bb36f2 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php @@ -16,7 +16,7 @@ * * @var array */ - public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage'); + public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text'); protected function setUp() { parent::setUp(); @@ -27,7 +27,8 @@ protected function setUp() { // Auto-create a field for testing. entity_create('field_entity', array( - 'field_name' => 'field_test_text', + 'name' => 'field_test_text', + 'entity_type' => 'entity_test_mulrev', 'type' => 'text', 'cardinality' => 1, 'translatable' => FALSE, diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php index a2788b1..505ff33 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php @@ -232,12 +232,13 @@ function testEnableModulesFixedList() { 'mode' => 'default', )); $field = entity_create('field_entity', array( - 'field_name' => 'test_field', + 'name' => 'test_field', + 'entity_type' => 'entity_test', 'type' => 'test_field' )); $field->save(); entity_create('field_instance', array( - 'field_name' => $field->id(), + 'field_name' => $field->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', ))->save(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php index f15f382..5051384 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php @@ -35,7 +35,8 @@ function setUp() { // Create a multi-valued field for 'page' nodes to use for Ajax testing. $field_name = 'field_ajax_test'; entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'node', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, ))->save(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php index 9db6310..b431593 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php @@ -62,7 +62,8 @@ public function setUp() { for ($i = 1; $i <= 2; $i++) { $field_name = 'field_test_' . $i; entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'entity_test', 'type' => 'number_integer', 'cardinality' => 2, ))->save(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php index ae99e9d..ee498c4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php @@ -81,7 +81,8 @@ public function setUp() { // Second, create the field. $this->fieldName = strtolower($this->randomName()); $field = array( - 'field_name' => $this->fieldName, + 'name' => $this->fieldName, + 'entity_type' => 'entity_test', 'type' => 'taxonomy_term_reference', ); $field['settings']['allowed_values']['vocabulary'] = $vocabulary->id(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php index 284526f..e1b0ea7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php @@ -63,7 +63,8 @@ function setUp() { $greetings = drupal_strtolower($this->randomName()); foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) { $field = entity_create('field_entity', array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'entity_test_mulrev', 'type' => $field_type, 'cardinality' => 2, 'translatable' => TRUE, @@ -81,7 +82,7 @@ function setUp() { entity_test_create_bundle($bundle); foreach ($fields as $field) { entity_create('field_instance', array( - 'field_name' => $field->id(), + 'field_name' => $field->name, 'entity_type' => 'entity_test_mulrev', 'bundle' => $bundle, ))->save(); @@ -404,12 +405,19 @@ protected function testTableSort() { } /** - * Test entity count query. + * Test that count queries are separated across entity types. */ protected function testCount() { - // Attach the existing 'figures' field to a second entity type so that we - // can test whether cross entity type fields produce the correct query. + // Create a field with the same name in a different entity type. $field_name = $this->figures; + $field = entity_create('field_entity', array( + 'name' => $field_name, + 'entity_type' => 'entity_test', + 'type' => 'shape', + 'cardinality' => 2, + 'translatable' => TRUE, + )); + $field->save(); $bundle = $this->randomName(); entity_create('field_instance', array( 'field_name' => $field_name, @@ -424,6 +432,7 @@ protected function testCount() { $entity->enforceIsNew(); $entity->setNewRevision(); $entity->save(); + // As the single entity of this type we just saved does not have a value // in the color field, the result should be 0. $count = $this->factory->get('entity_test') diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php index 4404ca5..68206a7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php @@ -104,10 +104,10 @@ function testEntityFormLanguage() { $this->assertTrue($node, 'Node found in database.'); // Make body translatable. - $field = field_info_field('body'); + $field = field_info_field('node', 'body'); $field->translatable = TRUE; $field->save(); - $field = field_info_field('body'); + $field = field_info_field('node', 'body'); $this->assertTrue($field['translatable'], 'Field body is translatable.'); // Create a body translation and check the form language. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index 40a7e44..7adfda3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -51,16 +51,16 @@ function setUp() { // Create a translatable test field. $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); - entity_create('field_entity', array( - 'field_name' => $this->field_name, - 'type' => 'text', - 'cardinality' => 4, - 'translatable' => TRUE, - ))->save(); - $this->field = field_read_field($this->field_name); // Create instance in all entity variations. foreach (entity_test_entity_types() as $entity_type) { + entity_create('field_entity', array( + 'name' => $this->field_name, + 'entity_type' => $entity_type, + 'type' => 'text', + 'cardinality' => 4, + 'translatable' => TRUE, + ))->save(); entity_create('field_instance', array( 'field_name' => $this->field_name, 'entity_type' => $entity_type, diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php index b3b25bb..e9fa907 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -20,7 +20,7 @@ * * @var array */ - public static $modules = array('entity', 'user', 'system', 'field', 'text', 'field_sql_storage', 'entity_test'); + public static $modules = array('entity', 'user', 'system', 'field', 'text', 'entity_test'); /** * The entity manager service. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php new file mode 100644 index 0000000..9189827 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php @@ -0,0 +1,462 @@ + 'Field SQL storage tests', + 'description' => "Test Field SQL Storage .", + 'group' => 'Entity 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( + 'name' => $this->field_name, + 'entity_type' => $entity_type, + '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 = DatabaseStorageController::_fieldTableName($this->field); + $this->revision_table = DatabaseStorageController::_fieldRevisionTableName($this->field); + } + + /** + * Tests field loading works correctly by inserting directly in the tables. + */ + function testFieldLoad() { + $entity_type = $bundle = 'entity_test_rev'; + $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController($entity_type); + + $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', DatabaseStorageController::_fieldColumnName($this->field, 'value')); + + // Create an entity with four revisions. + $revision_ids = array(); + $entity = entity_create($entity_type, array()); + $entity->save(); + $revision_ids[] = $entity->getRevisionId(); + for ($i = 0; $i < 4; $i++) { + $entity->setNewRevision(); + $entity->save(); + $revision_ids[] = $entity->getRevisionId(); + } + + // Generate values and insert them directly in the storage tables. + $values = array(); + $query = db_insert($this->revision_table)->fields($columns); + foreach ($revision_ids as $revision_id) { + // Put one value too many. + for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) { + $value = mt_rand(1, 127); + $values[$revision_id][] = $value; + $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->id, $value)); + } + $query->execute(); + } + $query = db_insert($this->table)->fields($columns); + foreach ($values[$revision_id] as $delta => $value) { + $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->id, $value)); + } + $query->execute(); + + // Load every revision and check the values. + foreach ($revision_ids as $revision_id) { + $entity = $storage_controller->loadRevision($revision_id); + foreach ($values[$revision_id] as $delta => $value) { + if ($delta < $this->field['cardinality']) { + $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value); + } + else { + $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name})); + } + } + } + + // Load the "current revision" and check the values. + $entity = $storage_controller->load($entity->id()); + foreach ($values[$revision_id] as $delta => $value) { + if ($delta < $this->field['cardinality']) { + $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value); + } + else { + $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name})); + } + } + + // Add a translation in an unavailable language code and verify it is not + // loaded. + $unavailable_langcode = 'xx'; + $values = array($bundle, 0, $entity->id(), $entity->getRevisionId(), 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(); + $entity = $storage_controller->load($entity->id()); + $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->field_name})); + } + + /** + * Tests field saving works correctly by reading directly from the tables. + */ + function testFieldWrite() { + $entity_type = $bundle = 'entity_test_rev'; + $entity = entity_create($entity_type, array()); + + $revision_values = array(); + + // Check insert. Add one value too many. + $values = array(); + for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) { + $values[$delta]['value'] = mt_rand(1, 127); + } + $entity->{$this->field_name} = $values; + $entity->save(); + + // Read the tables and check the correct values have been stored. + $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), $this->field['cardinality']); + foreach ($rows as $delta => $row) { + $expected = array( + 'bundle' => $bundle, + 'deleted' => 0, + 'entity_id' => $entity->id(), + 'revision_id' => $entity->getRevisionId(), + 'langcode' => $entity->language()->id, + 'delta' => $delta, + $this->field_name . '_value' => $values[$delta]['value'], + ); + $this->assertEqual($row, $expected, "Row $delta was stored as expected."); + } + + // Test update. Add less values and check that the previous values did not + // persist. + $values = array(); + for ($delta = 0; $delta <= $this->field['cardinality'] - 2; $delta++) { + $values[$delta]['value'] = mt_rand(1, 127); + } + $entity->{$this->field_name} = $values; + $entity->save(); + $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), count($values)); + foreach ($rows as $delta => $row) { + $expected = array( + 'bundle' => $bundle, + 'deleted' => 0, + 'entity_id' => $entity->id(), + 'revision_id' => $entity->getRevisionId(), + 'langcode' => $entity->language()->id, + 'delta' => $delta, + $this->field_name . '_value' => $values[$delta]['value'], + ); + $this->assertEqual($row, $expected, "Row $delta was stored as expected."); + } + + // Create a new revision. + $revision_values[$entity->getRevisionId()] = $values; + $values = array(); + for ($delta = 0; $delta < $this->field['cardinality']; $delta++) { + $values[$delta]['value'] = mt_rand(1, 127); + } + $entity->{$this->field_name} = $values; + $entity->setNewRevision(); + $entity->save(); + $revision_values[$entity->getRevisionId()] = $values; + + // Check that data for both revisions are in the revision table. + foreach ($revision_values as $revision_id => $values) { + $rows = db_select($this->revision_table, 't')->fields('t')->condition('revision_id', $revision_id)->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), min(count($values), $this->field['cardinality'])); + foreach ($rows as $delta => $row) { + $expected = array( + 'bundle' => $bundle, + 'deleted' => 0, + 'entity_id' => $entity->id(), + 'revision_id' => $revision_id, + 'langcode' => $entity->language()->id, + 'delta' => $delta, + $this->field_name . '_value' => $values[$delta]['value'], + ); + $this->assertEqual($row, $expected, "Row $delta was stored as expected."); + } + } + + // Test emptying the field. + $entity->{$this->field_name} = NULL; + $entity->save(); + $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), 0); + } + + /** + * Tests that long entity type and field names do not break. + */ + function testLongNames() { + // Use one of the longest entity_type names in core. + $entity_type = $bundle = 'entity_test_label_callback'; + $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController($entity_type); + + // Create two fields with instances, and generate randome values. + $name_base = drupal_strtolower($this->randomName(Field::NAME_MAX_LENGTH - 1)); + $field_names = array(); + $values = array(); + for ($i = 0; $i < 2; $i++) { + $field_names[$i] = $name_base . $i; + entity_create('field_entity', array( + 'name' => $field_names[$i], + 'entity_type' => $entity_type, + 'type' => 'test_field', + ))->save(); + entity_create('field_instance', array( + 'field_name' => $field_names[$i], + 'entity_type' => $entity_type, + 'bundle' => $bundle, + ))->save(); + $values[$field_names[$i]] = mt_rand(1, 127); + } + + // Save an entity with values. + $entity = entity_create($entity_type, $values); + $entity->save(); + + // Load the entity back and check the values. + $entity = $storage_controller->load($entity->id()); + foreach ($field_names as $field_name) { + $this->assertEqual($entity->get($field_name)->value, $values[$field_name]); + } + } + + /** + * 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( + 'name' => 'decimal52', + 'entity_type' => $entity_type, + '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( + 'name' => 'test_text', + 'entity_type' => 'entity_test', + '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 (DatabaseStorageController::_fieldSqlSchema($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( + 'name' => $field_name, + 'entity_type' => $entity_type, + '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(DatabaseStorageController::_fieldTableName($field), DatabaseStorageController::_fieldRevisionTableName($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 in the process. + field_cache_clear(); + $entity = $this->container->get('plugin.manager.entity')->getStorageController($entity_type)->load(1); + $this->assertEqual($entity->$field_name->value, 'field data', t("Index changes performed without dropping the tables")); + } + + /** + * 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( + 'name' => $field_name, + 'entity_type' => 'entity_test', + '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('entity_test', $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 = 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 = DatabaseStorageController::_fieldColumnName($field, $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/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php index cc6546a..193c2d2 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php @@ -34,7 +34,8 @@ function setUp() { // Auto-create a field for testing. entity_create('field_entity', array( - 'field_name' => 'test_multiple', + 'name' => 'test_multiple', + 'entity_type' => 'user', 'type' => 'text', 'cardinality' => -1, 'translatable' => FALSE, diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php index c580e88..460e0ca 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php @@ -72,7 +72,8 @@ function testPreserveFormActionAfterAJAX() { // Create a multi-valued field for 'page' nodes to use for Ajax testing. $field_name = 'field_ajax_test'; $field = array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'node', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, ); diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php index 01794f0..982a70d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php @@ -14,7 +14,7 @@ */ class TreeOutputTest extends DrupalUnitTestBase { - public static $modules = array('system', 'menu_link'); + public static $modules = array('system', 'menu_link', 'field'); /** * Dummy link structure acceptable for menu_tree_output(). diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php index b04e2d8..262537b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php @@ -17,7 +17,7 @@ */ class ContextPluginTest extends DrupalUnitTestBase { - public static $modules = array('system', 'user', 'node'); + public static $modules = array('system', 'user', 'node', 'field'); public static function getInfo() { return array( diff --git a/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php b/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php index 7b61c15..53ff2fc 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php @@ -20,7 +20,7 @@ class PasswordHashingTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('user'); + public static $modules = array('field', 'user'); public static function getInfo() { return array( diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php index 66a95c0..59aef23 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php @@ -6,7 +6,9 @@ */ namespace Drupal\system\Tests\Upgrade; -use Drupal\Core\Language\Language; + +use Drupal\Core\Entity\DatabaseStorageController; +use Drupal\field\Entity\Field; /** * Tests upgrade of system variables. @@ -121,27 +123,24 @@ function testFieldUpgradeToConfig() { $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); // Check that the configuration for the 'body' field is correct. - $config = \Drupal::config('field.field.body')->get(); + $config = \Drupal::config('field.field.node.body')->get(); + // This will be necessary to retrieve the table name. + $field_entity = new Field($config); // We cannot predict the value of the UUID, we just check it's present. $this->assertFalse(empty($config['uuid'])); $field_uuid = $config['uuid']; unset($config['uuid']); $this->assertEqual($config, array( - 'id' => 'body', + 'id' => 'node.body', + 'name' => 'body', 'type' => 'text_with_summary', 'module' => 'text', 'active' => '1', + 'entity_type' => 'node', 'settings' => array(), - 'storage' => array( - 'type' => 'field_sql_storage', - 'module' => 'field_sql_storage', - 'active' => '1', - 'settings' => array(), - ), 'locked' => 0, 'cardinality' => 1, 'translatable' => 0, - 'entity_types' => array('node'), 'indexes' => array( 'format' => array('format') ), @@ -195,7 +194,7 @@ function testFieldUpgradeToConfig() { $uuid_key = key($deleted_fields); $deleted_field = $deleted_fields[$uuid_key]; $this->assertEqual($deleted_field['uuid'], $uuid_key); - $this->assertEqual($deleted_field['id'], 'test_deleted_field'); + $this->assertEqual($deleted_field['id'], 'node.test_deleted_field'); // Check that the definition of a deleted instance is stored in state rather // than config. @@ -209,16 +208,11 @@ function testFieldUpgradeToConfig() { // The deleted field uuid and deleted instance field_uuid must match. $this->assertEqual($deleted_field['uuid'], $deleted_instance['field_uuid']); - // Check that pre-existing deleted field values are read correctly. - $entity = _field_create_entity_from_ids((object) array( - 'entity_type' => 'node', - 'bundle' => 'article', - 'entity_id' => 2, - 'revision_id' => 2, - )); - field_attach_load('node', array(2 => $entity), FIELD_LOAD_CURRENT, array('instance' => entity_create('field_instance', $deleted_instance))); - $deleted_value = $entity->get('test_deleted_field'); - $this->assertEqual($deleted_value[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Some deleted value'); + // Check that pre-existing deleted field table is renamed correctly. + $field_entity = new Field($deleted_field); + $table_name = DatabaseStorageController::_fieldTableName($field_entity); + $this->assertEqual("field_deleted_data_" . substr(hash('sha256', $deleted_field['uuid']), 0, 10), $table_name); + $this->assertTrue(db_table_exists($table_name)); // Check that creation of a new node works as expected. $value = $this->randomName(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php index 2503aa5..c8825af 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php @@ -52,7 +52,7 @@ public function testUserPictureUpgrade() { // Check file usage for the default image. $usage = file_usage()->listUsage($file); - $field = field_info_field('user_picture'); + $field = field_info_field('user', 'user_picture'); $this->assertTrue(isset($usage['image']['default_image'][$field['uuid']])); $this->assertEqual($instance['settings']['max_resolution'], '800x800', 'User picture maximum resolution has been migrated.'); diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install index d6f1b34..346eccb 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.install +++ b/core/modules/system/tests/modules/entity_test/entity_test.install @@ -9,14 +9,6 @@ * Implements hook_install(). */ function entity_test_install() { - // Auto-create a field for testing. - entity_create('field_entity', array( - 'field_name' => 'field_test_text', - 'type' => 'text', - 'cardinality' => 1, - 'translatable' => FALSE, - ))->save(); - $entity_types = array( 'entity_test', 'entity_test_rev', @@ -24,6 +16,14 @@ function entity_test_install() { 'entity_test_mulrev', ); foreach ($entity_types as $entity_type) { + // Auto-create fields for testing. + entity_create('field_entity', array( + 'name' => 'field_test_text', + 'entity_type' => $entity_type, + 'type' => 'text', + 'cardinality' => 1, + 'translatable' => FALSE, + ))->save(); entity_create('field_instance', array( 'entity_type' => $entity_type, 'field_name' => 'field_test_text', diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php index d2ad106..0ce4fdd 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php @@ -73,7 +73,7 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang $element += array( '#type' => 'textfield', '#default_value' => taxonomy_implode_tags($tags), - '#autocomplete_path' => $this->getSetting('autocomplete_path') . '/' . $this->fieldDefinition->getFieldName(), + '#autocomplete_path' => $this->getSetting('autocomplete_path') . '/' . $items->getParent()->entityType() . '/' . $this->fieldDefinition->getFieldName(), '#size' => $this->getSetting('size'), '#placeholder' => $this->getSetting('placeholder'), '#maxlength' => 1024, diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php index 2ceb843..7112de1 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php @@ -135,12 +135,12 @@ public function getArgument() { // Just check, if a node could be detected. if ($node) { $taxonomy = array(); - $fields = field_info_instances('node', $node->getType()); - foreach ($fields as $name => $info) { - $field_info = field_info_field($name); - if ($field_info['type'] == 'taxonomy_term_reference') { - foreach ($node->get($name) as $item) { - $taxonomy[$item->target_id] = $field_info['settings']['allowed_values'][0]['vocabulary']; + $instances = field_info_instances('node', $node->getType()); + foreach ($instances as $instance) { + $field = $instance->getField(); + if ($field->type == 'taxonomy_term_reference') { + foreach ($node->get($field->name) as $item) { + $taxonomy[$item->target_id] = $field->settings['allowed_values'][0]['vocabulary']; } } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php index c9b8078..905cc14 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php @@ -38,7 +38,8 @@ function setUp() { $this->field_name = 'taxonomy_' . $this->vocabulary->id(); $this->field = entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php index 2308f63..c5e96f7 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php @@ -45,7 +45,8 @@ public function setUp() { $vocabulary->save(); entity_create('field_entity', array( - 'field_name' => 'field_test_taxonomy', + 'name' => 'field_test_taxonomy', + 'entity_type' => 'entity_test', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php index c19ce9d..4cfb173 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php @@ -43,7 +43,8 @@ function setUp() { // Set up a field and instance. $this->field_name = drupal_strtolower($this->randomName()); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'entity_test', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( @@ -125,7 +126,7 @@ function testTaxonomyTermFieldMultipleVocabularies() { $this->assertNoText($term2->label(), 'Term 2 name is not displayed.'); // Verify that field and instance settings are correct. - $field_info = field_info_field($this->field_name); + $field_info = field_info_field('entity_test', $this->field_name); $this->assertEqual(count($field_info['settings']['allowed_values']), 1, 'Only one vocabulary is allowed for the field.'); // The widget should still be displayed. diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php index e893001..1fee5c3 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php @@ -46,7 +46,8 @@ function setUp() { // Setup a field and instance. $this->field_name = drupal_strtolower($this->randomName()); $this->field = entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'entity_test', 'type' => 'taxonomy_term_reference', 'settings' => array( 'allowed_values' => array( @@ -159,7 +160,7 @@ function testTaxonomyTermFieldChangeMachineName() { $this->vocabulary->save(); // Check that the field instance is still attached to the vocabulary. - $field = field_info_field($this->field_name); + $field = field_info_field('entity_test', $this->field_name); $allowed_values = $field['settings']['allowed_values']; $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, 'Index 0: Machine name was updated correctly.'); $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, 'Index 1: Machine name was updated correctly.'); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php index 991f274..0697de2 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php @@ -34,7 +34,8 @@ function setUp() { $this->field_name_1 = drupal_strtolower($this->randomName()); entity_create('field_entity', array( - 'field_name' => $this->field_name_1, + 'name' => $this->field_name_1, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( @@ -64,7 +65,8 @@ function setUp() { $this->field_name_2 = drupal_strtolower($this->randomName()); entity_create('field_entity', array( - 'field_name' => $this->field_name_2, + 'name' => $this->field_name_2, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index 99ab62e..5896c09 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -29,7 +29,8 @@ function setUp() { $this->vocabulary = $this->createVocabulary(); $field = array( - 'field_name' => 'taxonomy_' . $this->vocabulary->id(), + 'name' => 'taxonomy_' . $this->vocabulary->id(), + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( @@ -219,21 +220,21 @@ function testNodeTermCreationAndDeletion() { // Test autocomplete on term 3, which contains a comma. // The term will be quoted, and the " will be encoded in unicode (\u0022). $input = substr($term_objects['term3']->label(), 0, 3); - $json = $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input))); + $json = $this->drupalGet('taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input))); $this->assertEqual($json, '{"\u0022' . $term_objects['term3']->label() . '\u0022":"' . $term_objects['term3']->label() . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->label()))); // Test autocomplete on term 4 - it is alphanumeric only, so no extra // quoting. $input = substr($term_objects['term4']->label(), 0, 3); - $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input))); + $this->drupalGet('taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input))); $this->assertRaw('{"' . $term_objects['term4']->label() . '":"' . $term_objects['term4']->label() . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term4']->label()))); // Test taxonomy autocomplete with a nonexistent field. $field_name = $this->randomName(); $tag = $this->randomName(); $message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name)); - $this->assertFalse(field_info_field($field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name))); - $this->drupalGet('taxonomy/autocomplete/' . $field_name, array('query' => array('q' => $tag))); + $this->assertFalse(field_info_field('node', $field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name))); + $this->drupalGet('taxonomy/autocomplete/node/' . $field_name, array('query' => array('q' => $tag))); $this->assertRaw($message, 'Autocomplete returns correct error message when the taxonomy field does not exist.'); } @@ -257,7 +258,7 @@ function testTermAutocompletion() { // Try to autocomplete a term name that matches both terms. // We should get both term in a json encoded string. $input = '10/'; - $path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(); + $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(); // The result order is not guaranteed, so check each term separately. $result = $this->drupalGet($path, array('query' => array('q' => $input))); $data = drupal_json_decode($result); @@ -267,14 +268,14 @@ function testTermAutocompletion() { // Try to autocomplete a term name that matches first term. // We should only get the first term in a json encoded string. $input = '10/16'; - $path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(); + $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(); $this->drupalGet($path, array('query' => array('q' => $input))); $target = array($first_term->label() => check_plain($first_term->label())); $this->assertRaw(drupal_json_encode($target), 'Autocomplete returns only the expected matching term.'); // Try to autocomplete a term name with both a comma and a slash. $input = '"term with, comma and / a'; - $path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(); + $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(); $this->drupalGet($path, array('query' => array('q' => $input))); $n = $third_term->label(); // Term names containing commas or quotes must be wrapped in quotes. diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php index 35f4555..d785f2f 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php @@ -30,7 +30,8 @@ function setUp() { $this->langcode = Language::LANGCODE_NOT_SPECIFIED; $this->field_name = 'taxonomy_' . $this->vocabulary->id(); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php index e7c7031..6e5e7ff 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php @@ -78,7 +78,8 @@ protected function mockStandardInstall() { $this->vocabulary->save(); $this->field_name = 'field_' . $this->vocabulary->id(); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', // Set cardinality to unlimited for tagging. 'cardinality' => FIELD_CARDINALITY_UNLIMITED, diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php index 8901db1..13d4762 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php @@ -155,7 +155,8 @@ function testTaxonomyVocabularyLoadMultiple() { function testTaxonomyVocabularyChangeMachineName() { // Add a field instance to the vocabulary. entity_create('field_entity', array( - 'field_name' => 'field_test', + 'name' => 'field_test', + 'entity_type' => 'taxonomy_term', 'type' => 'test_field', ))->save(); entity_create('field_instance', array( @@ -187,7 +188,8 @@ function testUninstallReinstall() { // removed when the module is uninstalled. $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); $this->field_definition = array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'taxonomy_term', 'type' => 'text', 'cardinality' => 4 ); diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install index 4d4c952..39835d6 100644 --- a/core/modules/taxonomy/taxonomy.install +++ b/core/modules/taxonomy/taxonomy.install @@ -6,12 +6,17 @@ */ use Drupal\Component\Uuid\Uuid; +use Drupal\Core\Entity\DatabaseStorageController; use Drupal\field\Entity\Field; /** * Implements hook_uninstall(). */ function taxonomy_uninstall() { + Drupal::entityManager()->addNamespaces(new ArrayIterator(array( + 'Drupal\taxonomy' => DRUPAL_ROOT . '/core/modules/taxonomy/lib', + ))); + drupal_classloader_register('taxonomy', 'core/modules/taxonomy'); // Remove taxonomy_term bundles. $config_names = config_get_storage_names_with_prefix('taxonomy.vocabulary.'); foreach ($config_names as $config_name) { @@ -199,9 +204,9 @@ function taxonomy_field_schema($field) { */ function taxonomy_update_dependencies() { // Convert the 'tid' column of the taxonomy reference field to 'target_id' - // after fields and instances have been moved to the config system. + // after the field tables have been reorganized. $dependencies['taxonomy'][8007] = array( - 'field' => 8003, + 'field' => 8006, ); return $dependencies; } @@ -356,16 +361,16 @@ function taxonomy_update_8007() { foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) { $field_config = Drupal::config($config_name); // Only update taxonomy reference fields that use the default SQL storage. - if ($field_config->get('type') == 'taxonomy_term_reference' && $field_config->get('storage.type') == 'field_sql_storage') { + if ($field_config->get('type') == 'taxonomy_term_reference') { $field = new Field($field_config->get()); $tables = array( - _field_sql_storage_tablename($field), - _field_sql_storage_revision_tablename($field), + DatabaseStorageController::_fieldTableName($field), + DatabaseStorageController::_fieldRevisionTableName($field), ); foreach ($tables as $table_name) { - db_change_field($table_name, $field->id() . '_tid', $field->id() . '_target_id', array( + db_change_field($table_name, $field->name . '_tid', $field->name . '_target_id', array( 'description' => 'The ID of the target entity.', 'type' => 'int', 'unsigned' => TRUE, @@ -373,8 +378,8 @@ function taxonomy_update_8007() { )); // Change the index. - db_drop_index($table_name, $field->id() . '_tid'); - db_add_index($table_name, $field->id() . '_target_id', array($field->id() . '_target_id')); + db_drop_index($table_name, $field->name . '_tid'); + db_add_index($table_name, $field->name . '_target_id', array($field->name . '_target_id')); } // Update the indexes in field config as well. diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 0bfa84d..6939d3d 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -5,12 +5,13 @@ * Enables the organization of content into categories. */ -use Drupal\taxonomy\VocabularyInterface; +use Drupal\Core\Entity\DatabaseStorageController; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; use Drupal\node\Entity\Node; use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Vocabulary; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\Field\FieldDefinitionInterface; +use Drupal\taxonomy\VocabularyInterface; /** * Denotes that no term in the vocabulary has a parent. @@ -292,10 +293,10 @@ function taxonomy_menu() { 'type' => MENU_CALLBACK, 'file' => 'taxonomy.pages.inc', ); - $items['taxonomy/autocomplete/%'] = array( + $items['taxonomy/autocomplete/%/%'] = array( 'title' => 'Autocomplete taxonomy', 'page callback' => 'taxonomy_autocomplete', - 'page arguments' => array(2), + 'page arguments' => array(2, 3), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, 'file' => 'taxonomy.pages.inc', @@ -1128,19 +1129,20 @@ function taxonomy_node_insert(EntityInterface $node) { function taxonomy_build_node_index($node) { // We maintain a denormalized table of term/node relationships, containing // only data for current, published nodes. - $status = NULL; - if (Drupal::config('taxonomy.settings')->get('maintain_index_table')) { - $status = $node->isPublished(); - $sticky = (int) $node->isSticky(); + if (!Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(Drupal::entityManager()->getStorageController('node') instanceof DatabaseStorageController)) { + return; } + + $status = $node->isPublished(); + $sticky = (int) $node->isSticky(); // We only maintain the taxonomy index for published nodes. if ($status && $node->isDefaultRevision()) { // Collect a unique list of all the term IDs from all node fields. $tid_all = array(); foreach (field_info_instances('node', $node->getType()) as $instance) { $field_name = $instance['field_name']; - $field = field_info_field($field_name); - if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') { + $field = $instance->getField(); + if ($field['module'] == 'taxonomy') { // If a field value is not set in the node object when $node->save() is // called, the old value from $node->original is used. if (isset($node->{$field_name})) { diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc index 93f628d..fe61785 100644 --- a/core/modules/taxonomy/taxonomy.pages.inc +++ b/core/modules/taxonomy/taxonomy.pages.inc @@ -98,19 +98,21 @@ function taxonomy_term_feed(Term $term) { * }; * @endcode * - * @param $field_name + * @param string $entity_type + * The entity_type. + * @param string $field_name * The name of the term reference field. * * @see taxonomy_menu() * @see taxonomy_field_widget_info() */ -function taxonomy_autocomplete($field_name) { +function taxonomy_autocomplete($entity_type, $field_name) { // A comma-separated list of term names entered in the autocomplete form // element. Only the last term is used for autocompletion. $tags_typed = Drupal::request()->query->get('q'); // Make sure the field exists and is a taxonomy field. - if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') { + if (!($field = field_info_field($entity_type, $field_name)) || $field['type'] !== 'taxonomy_term_reference') { // Error string. The JavaScript handler will realize this is not JSON and // will display it as debugging information. print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name)); diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc index 5646268..9b37e9e 100644 --- a/core/modules/taxonomy/taxonomy.views.inc +++ b/core/modules/taxonomy/taxonomy.views.inc @@ -7,6 +7,9 @@ * @ingroup views_module_handlers */ +use Drupal\Core\Entity\DatabaseStorageController; +use Drupal\field\FieldInterface; + /** * Implements hook_views_data(). */ @@ -335,7 +338,7 @@ function taxonomy_views_data_alter(&$data) { * * @see field_views_field_default_views_data() */ -function taxonomy_field_views_data($field) { +function taxonomy_field_views_data(FieldInterface $field) { $data = field_views_field_default_views_data($field); foreach ($data as $table_name => $table_data) { foreach ($table_data as $field_name => $field_data) { @@ -363,40 +366,33 @@ function taxonomy_field_views_data($field) { * * Views integration to provide reverse relationships on term references. */ -function taxonomy_field_views_data_views_data_alter(&$data, $field) { - foreach ($field['bundles'] as $entity_type => $bundles) { - $entity_info = entity_get_info($entity_type); - $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; - - list($label, $all_labels) = field_views_field_label($field['field_name']); - $entity = $entity_info['label']; - if ($entity == t('Node')) { - $entity = t('Content'); - } - - $data['taxonomy_term_data'][$pseudo_field_name]['relationship'] = array( - 'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)), - 'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity, '@field' => $label)), - 'id' => 'entity_reverse', - 'field_name' => $field['field_name'], - 'field table' => _field_sql_storage_tablename($field), - 'field field' => $field['field_name'] . '_target_id', - 'base' => $entity_info['base_table'], - 'base field' => $entity_info['entity_keys']['id'], - 'label' => t('!field_name', array('!field_name' => $field['field_name'])), - 'join_extra' => array( - 0 => array( - 'field' => 'entity_type', - 'value' => $entity_type, - ), - 1 => array( - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ), +function taxonomy_field_views_data_views_data_alter(array &$data, FieldInterface $field) { + $entity_type = $field['entity_type']; + $entity_info = entity_get_info($entity_type); + $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; + + list($label,) = field_views_field_label($entity_type, $field['field_name']); + $entity_label = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label']; + + $data['taxonomy_term_data'][$pseudo_field_name]['relationship'] = array( + 'title' => t('@entity using @field', array('@entity' => $entity_label, '@field' => $label)), + 'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity_label, '@field' => $label)), + 'id' => 'entity_reverse', + 'field_name' => $field['field_name'], + 'entity_type' => $field['entity_type'], + 'field table' => DatabaseStorageController::_fieldTableName($field), + 'field field' => $field['field_name'] . '_target_id', + 'base' => $entity_info['base_table'], + 'base field' => $entity_info['entity_keys']['id'], + 'label' => t('!field_name', array('!field_name' => $field['field_name'])), + 'join_extra' => array( + 0 => array( + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, ), - ); - } + ), + ); } /** diff --git a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php index 6158f07..8e290ba 100644 --- a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php +++ b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php @@ -21,7 +21,6 @@ class TelephoneFieldTest extends WebTestBase { */ public static $modules = array( 'field', - 'field_sql_storage', 'node', 'telephone' ); @@ -54,7 +53,8 @@ function testTelephoneField() { // Add the telepone field to the article content type. entity_create('field_entity', array( - 'field_name' => 'field_telephone', + 'name' => 'field_telephone', + 'entity_type' => 'node', 'type' => 'telephone', ))->save(); entity_create('field_instance', array( diff --git a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php index bd3633b..6049260 100644 --- a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php +++ b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php @@ -36,7 +36,8 @@ public function setUp() { // Create a telephone field and instance for validation. entity_create('field_entity', array( - 'field_name' => 'field_test', + 'name' => 'field_test', + 'entity_type' => 'entity_test', 'type' => 'telephone', ))->save(); entity_create('field_instance', array( diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php index 3b19cb2..5a0b89e 100644 --- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php @@ -26,7 +26,7 @@ class TextPlainUnitTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('entity', 'field', 'field_sql_storage', 'text', 'entity_test', 'system'); + public static $modules = array('entity', 'field', 'text', 'entity_test', 'system'); /** * Contains rendered content. @@ -68,7 +68,8 @@ function setUp() { $this->formatter_settings = array(); $this->field = entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => $this->entity_type, 'type' => $this->field_type, 'settings' => $this->field_settings, )); diff --git a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php index 4627fb5..fb9e901 100644 --- a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php @@ -50,7 +50,8 @@ function testTextFieldValidation() { // Create a field with settings to validate. $max_length = 3; $this->field = entity_create('field_entity', array( - 'field_name' => drupal_strtolower($this->randomName()), + 'name' => drupal_strtolower($this->randomName()), + 'entity_type' => 'entity_test', 'type' => 'text', 'settings' => array( 'max_length' => $max_length, @@ -58,7 +59,7 @@ function testTextFieldValidation() { )); $this->field->save(); entity_create('field_instance', array( - 'field_name' => $this->field->id(), + 'field_name' => $this->field->name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', ))->save(); @@ -66,8 +67,8 @@ function testTextFieldValidation() { // Test validation with valid and invalid values. $entity = entity_create('entity_test', array()); for ($i = 0; $i <= $max_length + 2; $i++) { - $entity->{$this->field->id()}->value = str_repeat('x', $i); - $violations = $entity->{$this->field->id()}->validate(); + $entity->{$this->field->name}->value = str_repeat('x', $i); + $violations = $entity->{$this->field->name}->validate(); if ($i <= $max_length) { $this->assertEqual(count($violations), 0, "Length $i does not cause validation error when max_length is $max_length"); } @@ -90,10 +91,10 @@ function testTextfieldWidgets() { */ function _testTextfieldWidgets($field_type, $widget_type) { // Setup a field and instance - $entity_type = 'entity_test'; $this->field_name = drupal_strtolower($this->randomName()); $this->field = entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'entity_test', 'type' => $field_type )); $this->field->save(); @@ -161,7 +162,8 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { // Setup a field and instance $this->field_name = drupal_strtolower($this->randomName()); $this->field = entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'entity_test', 'type' => $field_type )); $this->field->save(); diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php index eaaf350..8458886 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php @@ -40,7 +40,8 @@ protected function testUserAdd() { // Create a field and an instance. $field_name = 'test_field'; $field = array( - 'field_name' => $field_name, + 'name' => $field_name, + 'entity_type' => 'user', 'module' => 'image', 'type' => 'image', 'cardinality' => 1, diff --git a/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php b/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php index 78e386d..2c64d7a 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php @@ -23,7 +23,7 @@ class UserEntityTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('system', 'user'); + public static $modules = array('system', 'user', 'field'); public static function getInfo() { return array( diff --git a/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php b/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php index ec86be2..fe53305 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php @@ -196,8 +196,9 @@ function testRegistrationDefaultValues() { function testRegistrationWithUserFields() { // Create a field, and an instance on 'user' entity type. $field = entity_create('field_entity', array( + 'name' => 'test_user_field', + 'entity_type' => 'user', 'type' => 'test_field', - 'field_name' => 'test_user_field', 'cardinality' => 1, )); $field->save(); diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php index a441023..59f0086 100644 --- a/core/modules/user/lib/Drupal/user/UserStorageController.php +++ b/core/modules/user/lib/Drupal/user/UserStorageController.php @@ -11,7 +11,9 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Password\PasswordInterface; use Drupal\Core\Database\Connection; +use Drupal\field\FieldInfo; use Drupal\user\UserDataInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Entity\DatabaseStorageControllerNG; @@ -46,13 +48,15 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS * 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. * @param \Drupal\Core\Password\PasswordInterface $password * The password hashing service. * @param \Drupal\user\UserDataInterface $user_data * The user data service. */ - public function __construct($entity_type, $entity_info, Connection $database, PasswordInterface $password, UserDataInterface $user_data) { - parent::__construct($entity_type, $entity_info, $database); + public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, PasswordInterface $password, UserDataInterface $user_data) { + parent::__construct($entity_type, $entity_info, $database, $field_info); $this->password = $password; $this->userData = $user_data; @@ -66,6 +70,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_type, $entity_info, $container->get('database'), + $container->get('field.info'), $container->get('password'), $container->get('user.data') ); @@ -143,14 +148,9 @@ public function deleteUserRoles(array $uids) { * {@inheritdoc} */ 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); + $method = Container::camelize('field_' . $hook); + if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) { + $this->$method($entity); } // Invoke the hook. diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 4a48efb..6d15166 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -268,7 +268,8 @@ function user_install() { */ function user_install_picture_field() { $field = array( - 'field_name' => 'user_picture', + 'name' => 'user_picture', + 'entity_type' => 'user', 'module' => 'image', 'type' => 'image', 'cardinality' => 1, @@ -343,9 +344,9 @@ function user_install_picture_field() { */ function user_update_dependencies() { // Convert user picture to field after the fields and instances are converted - // to ConfigEntities. + // to ConfigEntities and the tables are split. $dependencies['user'][8011] = array( - 'field' => 8003, + 'field' => 8006, ); return $dependencies; } @@ -655,7 +656,8 @@ function user_update_8011() { // Create the field and instance. $field = array( - 'id' => 'user_picture', + 'name' => 'user_picture', + 'entity_type' => 'user', 'module' => 'image', 'type' => 'image', 'cardinality' => 1, @@ -710,10 +712,9 @@ function user_update_8011() { _update_8003_field_create_field($field); $instance = array( - 'id' => 'user.user.user_picture', 'entity_type' => 'user', - 'label' => 'Picture', 'bundle' => 'user', + 'label' => 'Picture', 'description' => update_variable_get('user_picture_guidelines', ''), 'required' => FALSE, 'settings' => array( @@ -816,9 +817,8 @@ function user_update_8012(&$sandbox) { $rows = db_query_range('SELECT uid, picture FROM {users} WHERE picture > 0', 0, 20)->fetchAllKeyed(); foreach ($rows as $uid => $fid) { // Add a row to the field data and revision tables. - db_insert('field_data_user_picture') + db_insert('user__user_picture') ->fields(array( - 'entity_type' => 'user', 'bundle' => 'user', 'entity_id' => $uid, 'revision_id' => $uid, @@ -827,9 +827,8 @@ function user_update_8012(&$sandbox) { 'user_picture_target_id' => $fid, )) ->execute(); - db_insert('field_revision_user_picture') + db_insert('user_revision__user_picture') ->fields(array( - 'entity_type' => 'user', 'bundle' => 'user', 'entity_id' => $uid, 'revision_id' => $uid, diff --git a/core/modules/views/config/views.view.comments_recent.yml b/core/modules/views/config/views.view.comments_recent.yml index 378170d..9b9db7d 100644 --- a/core/modules/views/config/views.view.comments_recent.yml +++ b/core/modules/views/config/views.view.comments_recent.yml @@ -378,7 +378,7 @@ display: provider: comment comment: id: comment - table: field_data_comment_body + table: comment__comment_body field: comment_body label: '' plugin_id: field diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php index 066f518..f3321f8 100644 --- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php @@ -62,7 +62,8 @@ protected function setUp() { // Setup a field and instance. $this->field_name = drupal_strtolower($this->randomName()); entity_create('field_entity', array( - 'field_name' => $this->field_name, + 'name' => $this->field_name, + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'settings' => array( 'allowed_values' => array( diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php index 2f870d8..c7ec4e0 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php @@ -56,7 +56,8 @@ function setUp() { // Create the tag field itself. $this->tag_field = entity_create('field_entity', array( - 'field_name' => 'field_views_testing_tags', + 'name' => 'field_views_testing_tags', + 'entity_type' => 'node', 'type' => 'taxonomy_term_reference', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, 'settings' => array( @@ -108,7 +109,7 @@ function testTaggedWith() { $node_add_path = 'node/add/' . $this->node_type_with_tags->type; // Create three nodes, with different tags. - $tag_field = $this->tag_field->id() . '[' . Language::LANGCODE_NOT_SPECIFIED . ']'; + $tag_field = $this->tag_field->name . '[' . Language::LANGCODE_NOT_SPECIFIED . ']'; $edit = array(); $edit['title'] = $node_tag1_title = $this->randomName(); $edit[$tag_field] = 'tag1'; diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml index 910d770..1c993fb 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml @@ -26,7 +26,7 @@ display: provider: views body_value: id: body_value - table: field_data_body + table: node__body field: body_value relationship: none group_type: group diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc index d05888f..aeea6b2 100644 --- a/core/modules/views_ui/admin.inc +++ b/core/modules/views_ui/admin.inc @@ -224,7 +224,7 @@ function views_ui_taxonomy_autocomplete_validate($element, &$form_state) { if ($tags = $element['#value']) { // Get the machine names of the vocabularies we will search, keyed by the // vocabulary IDs. - $field = field_info_field($element['#field_name']); + $field = field_info_field($element['#entity_type'], $element['#field_name']); $vocabularies = array(); if (!empty($field['settings']['allowed_values'])) { foreach ($field['settings']['allowed_values'] as $tree) { diff --git a/core/profiles/standard/config/field.field.field_image.yml b/core/profiles/standard/config/field.field.node.field_image.yml similarity index 76% rename from core/profiles/standard/config/field.field.field_image.yml rename to core/profiles/standard/config/field.field.node.field_image.yml index 0fc032a..9611082 100644 --- a/core/profiles/standard/config/field.field.field_image.yml +++ b/core/profiles/standard/config/field.field.node.field_image.yml @@ -1,4 +1,6 @@ -id: field_image +id: node.field_image +name: field_image +entity_type: node type: image module: image active: '1' @@ -18,15 +20,9 @@ settings: title: label: Title translatable: true -storage: - type: field_sql_storage - module: field_sql_storage - active: '1' - settings: { } locked: '0' cardinality: '1' translatable: '0' -entity_types: { } indexes: target_id: - target_id diff --git a/core/profiles/standard/config/field.field.field_tags.yml b/core/profiles/standard/config/field.field.node.field_tags.yml similarity index 65% rename from core/profiles/standard/config/field.field.field_tags.yml rename to core/profiles/standard/config/field.field.node.field_tags.yml index e0f6952..5cab894 100644 --- a/core/profiles/standard/config/field.field.field_tags.yml +++ b/core/profiles/standard/config/field.field.node.field_tags.yml @@ -1,4 +1,6 @@ -id: field_tags +id: node.field_tags +name: field_tags +entity_type: node type: taxonomy_term_reference module: taxonomy active: '1' @@ -7,15 +9,9 @@ settings: - vocabulary: tags parent: 0 -storage: - type: field_sql_storage - module: field_sql_storage - active: '1' - settings: { } locked: '0' cardinality: '-1' translatable: '0' -entity_types: { } indexes: target_id: - target_id