Issue #1497374 by yched, chx, amateescu, plach, Damien Tournoud, fago, swentel: Switch from Field-based storage to Entity-based storage.
parent
df5b3bc501
commit
c7994001cc
|
|
@ -102,6 +102,12 @@ function entity_get_bundles($entity_type = NULL) {
|
|||
*/
|
||||
function entity_invoke_bundle_hook($hook, $entity_type, $bundle, $bundle_new = NULL) {
|
||||
entity_info_cache_clear();
|
||||
|
||||
// Notify the entity storage controller.
|
||||
$method = 'onBundle' . ucfirst($hook);
|
||||
Drupal::entityManager()->getStorageController($entity_type)->$method($bundle, $bundle_new);
|
||||
|
||||
// Invoke hook_entity_bundle_*() hooks.
|
||||
Drupal::moduleHandler()->invokeAll('entity_bundle_' . $hook, array($entity_type, $bundle, $bundle_new));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,4 +75,14 @@ interface ConfigEntityInterface extends EntityInterface {
|
|||
*/
|
||||
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();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@
|
|||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use PDO;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Entity\Query\QueryInterface;
|
||||
use Drupal\Component\Uuid\Uuid;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\Query\QueryInterface;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
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 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* 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.
|
||||
|
|
@ -58,6 +60,13 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The field info object.
|
||||
*
|
||||
* @var \Drupal\field\FieldInfo
|
||||
*/
|
||||
protected $fieldInfo;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -65,7 +74,8 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
return new static(
|
||||
$entity_type,
|
||||
$entity_info,
|
||||
$container->get('database')
|
||||
$container->get('database'),
|
||||
$container->get('field.info')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -78,11 +88,14 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
* 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 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
// 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 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
// We provide the necessary arguments for PDO to create objects of the
|
||||
// specified entity class.
|
||||
// @see Drupal\Core\Entity\EntityInterface::__construct()
|
||||
$query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
|
||||
$query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
|
||||
}
|
||||
$queried_entities = $query_result->fetchAllAssoc($this->idKey);
|
||||
|
||||
|
|
@ -223,6 +236,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
->condition($this->revisionKey, $revision->getRevisionId())
|
||||
->execute();
|
||||
$this->invokeFieldMethod('deleteRevision', $revision);
|
||||
$this->deleteFieldItemsRevision($revision);
|
||||
$this->invokeHook('revision_delete', $revision);
|
||||
}
|
||||
}
|
||||
|
|
@ -335,14 +349,9 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
* (optional) TRUE if the revision should be loaded, defaults to FALSE.
|
||||
*/
|
||||
protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
|
||||
// Attach fields.
|
||||
// Attach field values.
|
||||
if ($this->entityInfo['fieldable']) {
|
||||
if ($load_revision) {
|
||||
field_attach_load_revision($this->entityType, $queried_entities);
|
||||
}
|
||||
else {
|
||||
field_attach_load($this->entityType, $queried_entities);
|
||||
}
|
||||
$this->loadFieldItems($queried_entities, $load_revision ? FIELD_LOAD_REVISION : FIELD_LOAD_CURRENT);
|
||||
}
|
||||
|
||||
// Call hook_entity_load().
|
||||
|
|
@ -416,6 +425,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$entity_class::postDelete($this, $entities);
|
||||
foreach ($entities as $entity) {
|
||||
$this->invokeFieldMethod('delete', $entity);
|
||||
$this->deleteFieldItems($entity);
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
// Ignore slave server temporarily.
|
||||
|
|
@ -458,6 +468,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$this->resetCache(array($entity->id()));
|
||||
$entity->postSave($this, TRUE);
|
||||
$this->invokeFieldMethod('update', $entity);
|
||||
$this->saveFieldItems($entity, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
}
|
||||
else {
|
||||
|
|
@ -471,6 +482,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$entity->enforceIsNew(FALSE);
|
||||
$entity->postSave($this, FALSE);
|
||||
$this->invokeFieldMethod('insert', $entity);
|
||||
$this->saveFieldItems($entity, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
|
||||
|
|
@ -490,7 +502,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
/**
|
||||
* 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,34 +540,654 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Invokes a hook on behalf of the entity.
|
||||
*
|
||||
* @param $hook
|
||||
* One of 'presave', 'insert', 'update', 'predelete', 'delete', or
|
||||
* 'revision_delete'.
|
||||
* @param $entity
|
||||
* The entity object.
|
||||
*/
|
||||
protected function invokeHook($hook, EntityInterface $entity) {
|
||||
$function = 'field_attach_' . $hook;
|
||||
// @todo: field_attach_delete_revision() is named the wrong way round,
|
||||
// consider renaming it.
|
||||
if ($function == 'field_attach_revision_delete') {
|
||||
$function = 'field_attach_delete_revision';
|
||||
}
|
||||
if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
|
||||
$function($entity);
|
||||
}
|
||||
// Invoke the hook.
|
||||
module_invoke_all($this->entityType . '_' . $hook, $entity);
|
||||
// Invoke the respective entity-level hook.
|
||||
module_invoke_all('entity_' . $hook, $entity, $this->entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Entity\EntityStorageControllerInterface::getQueryServiceName().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryServiceName() {
|
||||
return 'entity.query.sql';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doLoadFieldItems($entities, $age) {
|
||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
||||
|
||||
// Collect entities ids and bundles.
|
||||
$bundles = array();
|
||||
$ids = array();
|
||||
foreach ($entities as $key => $entity) {
|
||||
$bundles[$entity->bundle()] = TRUE;
|
||||
$ids[] = $load_current ? $key : $entity->getRevisionId();
|
||||
}
|
||||
|
||||
// Collect impacted fields.
|
||||
$fields = array();
|
||||
foreach ($bundles as $bundle => $v) {
|
||||
foreach ($this->fieldInfo->getBundleInstances($this->entityType, $bundle) as $field_name => $instance) {
|
||||
$fields[$field_name] = $instance->getField();
|
||||
}
|
||||
}
|
||||
|
||||
// Load field data.
|
||||
foreach ($fields as $field_name => $field) {
|
||||
$table = $load_current ? static::_fieldTableName($field) : static::_fieldRevisionTableName($field);
|
||||
|
||||
$results = $this->database->select($table, 't')
|
||||
->fields('t')
|
||||
->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
|
||||
->condition('langcode', field_available_languages($this->entityType, $field), 'IN')
|
||||
->orderBy('delta')
|
||||
->condition('deleted', 0)
|
||||
->execute();
|
||||
|
||||
$delta_count = array();
|
||||
foreach ($results as $row) {
|
||||
if (!isset($delta_count[$row->entity_id][$row->langcode])) {
|
||||
$delta_count[$row->entity_id][$row->langcode] = 0;
|
||||
}
|
||||
|
||||
if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
|
||||
$item = array();
|
||||
// For each column declared by the field, populate the item from the
|
||||
// prefixed database column.
|
||||
foreach ($field['columns'] as $column => $attributes) {
|
||||
$column_name = static::_fieldColumnName($field, $column);
|
||||
// Unserialize the value if specified in the column schema.
|
||||
$item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
|
||||
}
|
||||
|
||||
// Add the item to the field values for the entity.
|
||||
$entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
|
||||
$delta_count[$row->entity_id][$row->langcode]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSaveFieldItems(EntityInterface $entity, $update) {
|
||||
$vid = $entity->getRevisionId();
|
||||
$id = $entity->id();
|
||||
$bundle = $entity->bundle();
|
||||
$entity_type = $entity->entityType();
|
||||
if (!isset($vid)) {
|
||||
$vid = $id;
|
||||
}
|
||||
|
||||
foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
|
||||
$field = $instance->getField();
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$revision_name = static::_fieldRevisionTableName($field);
|
||||
|
||||
$all_langcodes = field_available_languages($entity_type, $field);
|
||||
$field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
|
||||
|
||||
// Delete and insert, rather than update, in case a value was added.
|
||||
if ($update) {
|
||||
// Delete language codes present in the incoming $entity->$field_name.
|
||||
// Delete all language codes if $entity->$field_name is empty.
|
||||
$langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
|
||||
if ($langcodes) {
|
||||
// Only overwrite the field's base table if saving the default revision
|
||||
// of an entity.
|
||||
if ($entity->isDefaultRevision()) {
|
||||
$this->database->delete($table_name)
|
||||
->condition('entity_id', $id)
|
||||
->condition('langcode', $langcodes, 'IN')
|
||||
->execute();
|
||||
}
|
||||
$this->database->delete($revision_name)
|
||||
->condition('entity_id', $id)
|
||||
->condition('revision_id', $vid)
|
||||
->condition('langcode', $langcodes, 'IN')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the multi-insert query.
|
||||
$do_insert = FALSE;
|
||||
$columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
|
||||
foreach ($field['columns'] as $column => $attributes) {
|
||||
$columns[] = static::_fieldColumnName($field, $column);
|
||||
}
|
||||
$query = $this->database->insert($table_name)->fields($columns);
|
||||
$revision_query = $this->database->insert($revision_name)->fields($columns);
|
||||
|
||||
foreach ($field_langcodes as $langcode) {
|
||||
$items = (array) $entity->{$field_name}[$langcode];
|
||||
$delta_count = 0;
|
||||
foreach ($items as $delta => $item) {
|
||||
// We now know we have someting to insert.
|
||||
$do_insert = TRUE;
|
||||
$record = array(
|
||||
'entity_id' => $id,
|
||||
'revision_id' => $vid,
|
||||
'bundle' => $bundle,
|
||||
'delta' => $delta,
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
foreach ($field['columns'] as $column => $attributes) {
|
||||
$column_name = static::_fieldColumnName($field, $column);
|
||||
$value = isset($item[$column]) ? $item[$column] : NULL;
|
||||
// Serialize the value if specified in the column schema.
|
||||
$record[$column_name] = (!empty($attributes['serialize'])) ? serialize($value) : $value;
|
||||
}
|
||||
$query->values($record);
|
||||
$revision_query->values($record);
|
||||
|
||||
if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the query if we have values to insert.
|
||||
if ($do_insert) {
|
||||
// Only overwrite the field's base table if saving the default revision
|
||||
// of an entity.
|
||||
if ($entity->isDefaultRevision()) {
|
||||
$query->execute();
|
||||
}
|
||||
$revision_query->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteFieldItems(EntityInterface $entity) {
|
||||
foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
|
||||
$field = $instance->getField();
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$revision_name = static::_fieldRevisionTableName($field);
|
||||
$this->database->delete($table_name)
|
||||
->condition('entity_id', $entity->id())
|
||||
->execute();
|
||||
$this->database->delete($revision_name)
|
||||
->condition('entity_id', $entity->id())
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteFieldItemsRevision(EntityInterface $entity) {
|
||||
$vid = $entity->getRevisionId();
|
||||
if (isset($vid)) {
|
||||
foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
|
||||
$revision_name = static::_fieldRevisionTableName($instance->getField());
|
||||
$this->database->delete($revision_name)
|
||||
->condition('entity_id', $entity->id())
|
||||
->condition('revision_id', $vid)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldCreate(FieldInterface $field) {
|
||||
$schema = $this->_fieldSqlSchema($field);
|
||||
foreach ($schema as $name => $table) {
|
||||
$this->database->schema()->createTable($name, $table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldUpdate(FieldInterface $field) {
|
||||
$original = $field->original;
|
||||
|
||||
if (!$field->hasData()) {
|
||||
// There is no data. Re-create the tables completely.
|
||||
|
||||
if ($this->database->supportsTransactionalDDL()) {
|
||||
// If the database supports transactional DDL, we can go ahead and rely
|
||||
// on it. If not, we will have to rollback manually if something fails.
|
||||
$transaction = $this->database->startTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
$original_schema = $this->_fieldSqlSchema($original);
|
||||
foreach ($original_schema as $name => $table) {
|
||||
$this->database->schema()->dropTable($name, $table);
|
||||
}
|
||||
$schema = $this->_fieldSqlSchema($field);
|
||||
foreach ($schema as $name => $table) {
|
||||
$this->database->schema()->createTable($name, $table);
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
if ($this->database->supportsTransactionalDDL()) {
|
||||
$transaction->rollback();
|
||||
}
|
||||
else {
|
||||
// Recreate tables.
|
||||
$original_schema = $this->_fieldSqlSchema($original);
|
||||
foreach ($original_schema as $name => $table) {
|
||||
if (!$this->database->schema()->tableExists($name)) {
|
||||
$this->database->schema()->createTable($name, $table);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($field['columns'] != $original['columns']) {
|
||||
throw new FieldUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
|
||||
}
|
||||
// There is data, so there are no column changes. Drop all the prior
|
||||
// indexes and create all the new ones, except for all the priors that
|
||||
// exist unchanged.
|
||||
$table = static::_fieldTableName($original);
|
||||
$revision_table = static::_fieldRevisionTableName($original);
|
||||
|
||||
$schema = $field->getSchema();
|
||||
$original_schema = $original->getSchema();
|
||||
|
||||
foreach ($original_schema['indexes'] as $name => $columns) {
|
||||
if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
|
||||
$real_name = static::_fieldIndexName($field, $name);
|
||||
$this->database->schema()->dropIndex($table, $real_name);
|
||||
$this->database->schema()->dropIndex($revision_table, $real_name);
|
||||
}
|
||||
}
|
||||
$table = static::_fieldTableName($field);
|
||||
$revision_table = static::_fieldRevisionTableName($field);
|
||||
foreach ($schema['indexes'] as $name => $columns) {
|
||||
if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
|
||||
$real_name = static::_fieldIndexName($field, $name);
|
||||
$real_columns = array();
|
||||
foreach ($columns as $column_name) {
|
||||
// Indexes can be specified as either a column name or an array with
|
||||
// column name and length. Allow for either case.
|
||||
if (is_array($column_name)) {
|
||||
$real_columns[] = array(
|
||||
static::_fieldColumnName($field, $column_name[0]),
|
||||
$column_name[1],
|
||||
);
|
||||
}
|
||||
else {
|
||||
$real_columns[] = static::_fieldColumnName($field, $column_name);
|
||||
}
|
||||
}
|
||||
$this->database->schema()->addIndex($table, $real_name, $real_columns);
|
||||
$this->database->schema()->addIndex($revision_table, $real_name, $real_columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldDelete(FieldInterface $field) {
|
||||
// Mark all data associated with the field for deletion.
|
||||
$field['deleted'] = FALSE;
|
||||
$table = static::_fieldTableName($field);
|
||||
$revision_table = static::_fieldRevisionTableName($field);
|
||||
$this->database->update($table)
|
||||
->fields(array('deleted' => 1))
|
||||
->execute();
|
||||
|
||||
// Move the table to a unique name while the table contents are being
|
||||
// deleted.
|
||||
$field['deleted'] = TRUE;
|
||||
$new_table = static::_fieldTableName($field);
|
||||
$revision_new_table = static::_fieldRevisionTableName($field);
|
||||
$this->database->schema()->renameTable($table, $new_table);
|
||||
$this->database->schema()->renameTable($revision_table, $revision_new_table);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onInstanceDelete(FieldInstanceInterface $instance) {
|
||||
$field = $instance->getField();
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$revision_name = static::_fieldRevisionTableName($field);
|
||||
$this->database->update($table_name)
|
||||
->fields(array('deleted' => 1))
|
||||
->condition('bundle', $instance['bundle'])
|
||||
->execute();
|
||||
$this->database->update($revision_name)
|
||||
->fields(array('deleted' => 1))
|
||||
->condition('bundle', $instance['bundle'])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBundleRename($bundle, $bundle_new) {
|
||||
// We need to account for deleted or inactive fields and instances.
|
||||
$instances = field_read_instances(array('entity_type' => $this->entityType, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
|
||||
foreach ($instances as $instance) {
|
||||
$field = $instance->getField();
|
||||
if ($field['storage']['type'] == 'field_sql_storage') {
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$revision_name = static::_fieldRevisionTableName($field);
|
||||
$this->database->update($table_name)
|
||||
->fields(array('bundle' => $bundle_new))
|
||||
->condition('bundle', $bundle)
|
||||
->execute();
|
||||
$this->database->update($revision_name)
|
||||
->fields(array('bundle' => $bundle_new))
|
||||
->condition('bundle', $bundle)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
|
||||
$field = $instance->getField();
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC))
|
||||
->condition('entity_id', $entity->id())
|
||||
->orderBy('delta');
|
||||
foreach ($field->getColumns() as $column_name => $data) {
|
||||
$query->addField('t', static::_fieldColumnName($field, $column_name), $column_name);
|
||||
}
|
||||
return $query->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance) {
|
||||
$field = $instance->getField();
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$revision_name = static::_fieldRevisionTableName($field);
|
||||
$this->database->delete($table_name)
|
||||
->condition('entity_id', $entity->id())
|
||||
->execute();
|
||||
$this->database->delete($revision_name)
|
||||
->condition('entity_id', $entity->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldPurge(FieldInterface $field) {
|
||||
$table_name = static::_fieldTableName($field);
|
||||
$revision_name = static::_fieldRevisionTableName($field);
|
||||
$this->database->schema()->dropTable($table_name);
|
||||
$this->database->schema()->dropTable($revision_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL table schema.
|
||||
*
|
||||
* @private Calling this function circumvents the entity system and is
|
||||
* strongly discouraged. This function is not considered part of the public
|
||||
* API and modules relying on it might break even in minor releases.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field object
|
||||
* @param array $schema
|
||||
* The field schema array. Mandatory for upgrades, omit otherwise.
|
||||
*
|
||||
* @return array
|
||||
* The same as a hook_schema() implementation for the data and the
|
||||
* revision tables.
|
||||
*
|
||||
* @see hook_schema()
|
||||
*/
|
||||
public static function _fieldSqlSchema(FieldInterface $field, array $schema = NULL) {
|
||||
if ($field['deleted']) {
|
||||
$description_current = "Data storage for deleted field {$field['id']} ({$field['entity_type']}, {$field['field_name']}).";
|
||||
$description_revision = "Revision archive storage for deleted field {$field['id']} ({$field['entity_type']}, {$field['field_name']}).";
|
||||
}
|
||||
else {
|
||||
$description_current = "Data storage for {$field['entity_type']} field {$field['field_name']}.";
|
||||
$description_revision = "Revision archive storage for {$field['entity_type']} field {$field['field_name']}.";
|
||||
}
|
||||
|
||||
$current = array(
|
||||
'description' => $description_current,
|
||||
'fields' => array(
|
||||
'bundle' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
|
||||
),
|
||||
'deleted' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'A boolean indicating whether this data item has been deleted'
|
||||
),
|
||||
'entity_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity id this data is attached to',
|
||||
),
|
||||
'revision_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
|
||||
),
|
||||
'langcode' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The language code for this data item.',
|
||||
),
|
||||
'delta' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The sequence number for this data item, used for multi-value fields',
|
||||
),
|
||||
),
|
||||
'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'),
|
||||
'indexes' => array(
|
||||
'bundle' => array('bundle'),
|
||||
'deleted' => array('deleted'),
|
||||
'entity_id' => array('entity_id'),
|
||||
'revision_id' => array('revision_id'),
|
||||
'langcode' => array('langcode'),
|
||||
),
|
||||
);
|
||||
|
||||
if (!$schema) {
|
||||
$schema = $field->getSchema();
|
||||
}
|
||||
|
||||
// Add field columns.
|
||||
foreach ($schema['columns'] as $column_name => $attributes) {
|
||||
$real_name = static::_fieldColumnName($field, $column_name);
|
||||
$current['fields'][$real_name] = $attributes;
|
||||
}
|
||||
|
||||
// Add indexes.
|
||||
foreach ($schema['indexes'] as $index_name => $columns) {
|
||||
$real_name = static::_fieldIndexName($field, $index_name);
|
||||
foreach ($columns as $column_name) {
|
||||
// Indexes can be specified as either a column name or an array with
|
||||
// column name and length. Allow for either case.
|
||||
if (is_array($column_name)) {
|
||||
$current['indexes'][$real_name][] = array(
|
||||
static::_fieldColumnName($field, $column_name[0]),
|
||||
$column_name[1],
|
||||
);
|
||||
}
|
||||
else {
|
||||
$current['indexes'][$real_name][] = static::_fieldColumnName($field, $column_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add foreign keys.
|
||||
foreach ($schema['foreign keys'] as $specifier => $specification) {
|
||||
$real_name = static::_fieldIndexName($field, $specifier);
|
||||
$current['foreign keys'][$real_name]['table'] = $specification['table'];
|
||||
foreach ($specification['columns'] as $column_name => $referenced) {
|
||||
$sql_storage_column = static::_fieldColumnName($field, $column_name);
|
||||
$current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the revision table.
|
||||
$revision = $current;
|
||||
$revision['description'] = $description_revision;
|
||||
$revision['primary key'] = array('entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
|
||||
$revision['fields']['revision_id']['not null'] = TRUE;
|
||||
$revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
|
||||
|
||||
return array(
|
||||
static::_fieldTableName($field) => $current,
|
||||
static::_fieldRevisionTableName($field) => $revision,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a table name for a field data table.
|
||||
*
|
||||
* @private Calling this function circumvents the entity system and is
|
||||
* strongly discouraged. This function is not considered part of the public
|
||||
* API and modules relying on it might break even in minor releases. Only
|
||||
* call this function to write a query that \Drupal::entityQuery() does not
|
||||
* support. Always call entity_load() before using the data found in the
|
||||
* table.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field object.
|
||||
*
|
||||
* @return string
|
||||
* A string containing the generated name for the database table.
|
||||
*
|
||||
*/
|
||||
static public function _fieldTableName(FieldInterface $field) {
|
||||
if ($field['deleted']) {
|
||||
// When a field is a deleted, the table is renamed to
|
||||
// {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
|
||||
// table names longer than 64 characters, we hash the uuid and return the
|
||||
// first 10 characters so we end up with a short unique ID.
|
||||
return "field_deleted_data_" . substr(hash('sha256', $field['uuid']), 0, 10);
|
||||
}
|
||||
else {
|
||||
return static::_generateFieldTableName($field, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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::_generateFieldTableName($field, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a safe and unanbiguous field table name.
|
||||
*
|
||||
* The method accounts for a maximum table name length of 64 characters, and
|
||||
* takes care of disambiguation.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field object.
|
||||
* @param bool $revision
|
||||
* TRUE for revision table, FALSE otherwise.
|
||||
*
|
||||
* @return string
|
||||
* The final table name.
|
||||
*/
|
||||
static protected function _generateFieldTableName($field, $revision) {
|
||||
$separator = $revision ? '_revision__' : '__';
|
||||
$table_name = $field->entity_type . $separator . $field->name;
|
||||
// Limit the string to 48 characters, keeping a 16 characters margin for db
|
||||
// prefixes.
|
||||
if (strlen($table_name) > 48) {
|
||||
// Use a shorter separator, a truncated entity_type, and a hash of the
|
||||
// field UUID.
|
||||
$separator = $revision ? '_r__' : '__';
|
||||
$entity_type = substr($field->entity_type, 0, 38 - strlen($separator));
|
||||
$field_hash = substr(hash('sha256', $field->uuid), 0, 10);
|
||||
$table_name = $entity_type . $separator . $field_hash;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Entity;
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\field\FieldInfo;
|
||||
use PDO;
|
||||
|
||||
use Drupal\Core\Entity\Query\QueryInterface;
|
||||
|
|
@ -53,8 +54,8 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
/**
|
||||
* Overrides DatabaseStorageController::__construct().
|
||||
*/
|
||||
public function __construct($entity_type, array $entity_info, Connection $database) {
|
||||
parent::__construct($entity_type,$entity_info, $database);
|
||||
public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
|
||||
parent::__construct($entity_type,$entity_info, $database, $field_info);
|
||||
$this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
|
||||
$this->entityClass = $this->entityInfo['class'];
|
||||
|
||||
|
|
@ -367,6 +368,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$this->resetCache(array($entity->id()));
|
||||
$entity->postSave($this, TRUE);
|
||||
$this->invokeFieldMethod('update', $entity);
|
||||
$this->saveFieldItems($entity, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
if ($this->dataTable) {
|
||||
$this->invokeTranslationHooks($entity);
|
||||
|
|
@ -389,6 +391,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$entity->enforceIsNew(FALSE);
|
||||
$entity->postSave($this, FALSE);
|
||||
$this->invokeFieldMethod('insert', $entity);
|
||||
$this->saveFieldItems($entity, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
|
||||
|
|
@ -603,6 +606,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$entity_class::postDelete($this, $entities);
|
||||
foreach ($entities as $entity) {
|
||||
$this->invokeFieldMethod('delete', $entity);
|
||||
$this->deleteFieldItems($entity);
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
// Ignore slave server temporarily.
|
||||
|
|
|
|||
|
|
@ -333,8 +333,7 @@ class Entity implements IteratorAggregate, EntityInterface {
|
|||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,6 +269,7 @@ class EntityFormController extends FormBase implements EntityFormControllerInter
|
|||
*/
|
||||
public function validate(array $form, array &$form_state) {
|
||||
$entity = $this->buildEntity($form, $form_state);
|
||||
$entity_type = $entity->entityType();
|
||||
$entity_langcode = $entity->language()->id;
|
||||
|
||||
$violations = array();
|
||||
|
|
@ -285,9 +286,9 @@ class EntityFormController extends FormBase implements EntityFormControllerInter
|
|||
else {
|
||||
// For BC entities, iterate through each field instance and
|
||||
// instantiate NG items objects manually.
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity->entityType(), $entity->bundle());
|
||||
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $field_name => $instance) {
|
||||
$langcode = field_is_translatable($entity->entityType(), $instance->getField()) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity->bundle());
|
||||
foreach (field_info_instances($entity_type, $entity->bundle()) as $field_name => $instance) {
|
||||
$langcode = field_is_translatable($entity_type, $instance->getField()) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
|
||||
|
||||
// Create the field object.
|
||||
$items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
|
||||
|
|
@ -304,7 +305,7 @@ class EntityFormController extends FormBase implements EntityFormControllerInter
|
|||
// Map errors back to form elements.
|
||||
if ($violations) {
|
||||
foreach ($violations as $field_name => $field_violations) {
|
||||
$langcode = field_is_translatable($entity->entityType(), field_info_field($field_name)) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
|
||||
$langcode = field_is_translatable($entity_type , field_info_field($entity_type, $field_name)) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
|
||||
$field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
|
||||
$field_state['constraint_violations'] = $field_violations;
|
||||
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
|
||||
|
|
@ -433,9 +434,8 @@ class EntityFormController extends FormBase implements EntityFormControllerInter
|
|||
$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'];
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,15 @@ class EntityManager extends PluginManagerBase {
|
|||
*/
|
||||
protected $fieldDefinitions;
|
||||
|
||||
/**
|
||||
* The root paths.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityManager::__construct().
|
||||
*
|
||||
* @var \Traversable
|
||||
*/
|
||||
protected $namespaces;
|
||||
|
||||
/**
|
||||
* The string translationManager.
|
||||
*
|
||||
|
|
@ -114,22 +123,46 @@ class EntityManager extends PluginManagerBase {
|
|||
*/
|
||||
public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager, TranslationInterface $translation_manager) {
|
||||
// Allow the plugin definition to be altered by hook_entity_info_alter().
|
||||
$annotation_namespaces = array(
|
||||
'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
|
||||
);
|
||||
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->cache = $cache;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->namespaces = $namespaces;
|
||||
$this->translationManager = $translation_manager;
|
||||
|
||||
$this->doDiscovery($namespaces);
|
||||
$this->factory = new DefaultFactory($this->discovery);
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
protected function doDiscovery($namespaces) {
|
||||
$annotation_namespaces = array(
|
||||
'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
|
||||
);
|
||||
$this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
|
||||
$this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
|
||||
$this->discovery = new AlterDecorator($this->discovery, 'entity_info');
|
||||
$this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
|
||||
}
|
||||
|
||||
$this->factory = new DefaultFactory($this->discovery);
|
||||
$this->container = $container;
|
||||
/**
|
||||
* Add more namespaces to the entity manager.
|
||||
*
|
||||
* This is usually only necessary for uninstall purposes.
|
||||
*
|
||||
* @todo Remove this method, along with doDiscovery(), when
|
||||
* https://drupal.org/node/1199946 is fixed.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
*
|
||||
* @see comment_uninstall()
|
||||
*/
|
||||
public function addNamespaces(\Traversable $namespaces) {
|
||||
reset($this->namespaces);
|
||||
$iterator = new \AppendIterator;
|
||||
$iterator->append(new \IteratorIterator($this->namespaces));
|
||||
$iterator->append($namespaces);
|
||||
$this->doDiscovery($iterator);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -164,6 +197,9 @@ class EntityManager extends PluginManagerBase {
|
|||
*/
|
||||
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));
|
||||
|
|
|
|||
|
|
@ -233,6 +233,22 @@ abstract class EntityStorageControllerBase implements EntityStorageControllerInt
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\FieldableEntityStorageControllerBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
use Drupal\field\FieldInterface;
|
||||
use Drupal\field\FieldInstanceInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
|
||||
abstract class FieldableEntityStorageControllerBase extends EntityStorageControllerBase implements FieldableEntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Loads values of configurable fields for 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.
|
||||
*
|
||||
* This method is a wrapper that handles the field data cache. Subclasses
|
||||
* need to implement the doLoadFieldItems() method with the actual storage
|
||||
* logic.
|
||||
*
|
||||
* @param array $entities
|
||||
* An array of entities keyed by entity ID.
|
||||
* @param int $age
|
||||
* FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
|
||||
* FIELD_LOAD_REVISION to load the version indicated by each entity.
|
||||
*/
|
||||
protected function loadFieldItems(array $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) {
|
||||
// Let the storage controller actually load the values.
|
||||
$this->doLoadFieldItems($queried_entities, $age);
|
||||
|
||||
// Invoke the field type's prepareCache() method.
|
||||
foreach ($queried_entities as $entity) {
|
||||
$this->invokeFieldItemPrepareCache($entity);
|
||||
}
|
||||
|
||||
// Build cache data.
|
||||
if ($use_cache) {
|
||||
foreach ($queried_entities as $id => $entity) {
|
||||
$data = array();
|
||||
$instances = field_info_instances($this->entityType, $entity->bundle());
|
||||
foreach ($instances as $instance) {
|
||||
$data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
|
||||
}
|
||||
$cid = "field:{$this->entityType}:$id";
|
||||
cache('field')->set($cid, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves values of configurable fields for an entity.
|
||||
*
|
||||
* This method is a wrapper that handles the field data cache. Subclasses
|
||||
* need to implement the doSaveFieldItems() method with the actual storage
|
||||
* logic.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
* @param bool $update
|
||||
* TRUE if the entity is being updated, FALSE if it is being inserted.
|
||||
*/
|
||||
protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
|
||||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
$this->doSaveFieldItems($entity, $update);
|
||||
|
||||
if ($update) {
|
||||
$entity_info = $entity->entityInfo();
|
||||
if ($entity_info['field_cache']) {
|
||||
cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes values of configurable fields for all revisions of an entity.
|
||||
*
|
||||
* This method is a wrapper that handles the field data cache. Subclasses
|
||||
* need to implement the doDeleteFieldItems() method with the actual storage
|
||||
* logic.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
*/
|
||||
protected function deleteFieldItems(EntityInterface $entity) {
|
||||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
$this->doDeleteFieldItems($entity);
|
||||
|
||||
$entity_info = $entity->entityInfo();
|
||||
if ($entity_info['field_cache']) {
|
||||
cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes values of configurable fields for a single revision of an entity.
|
||||
*
|
||||
* This method is a wrapper that handles the field data cache. Subclasses
|
||||
* need to implement the doDeleteFieldItemsRevision() method with the actual
|
||||
* storage logic.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity. It must have a revision ID attribute.
|
||||
*/
|
||||
protected function deleteFieldItemsRevision(EntityInterface $entity) {
|
||||
$this->doDeleteFieldItemsRevision($entity->getBCEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads values of configurable fields for a group of entities.
|
||||
*
|
||||
* This is the method that holds the actual storage logic.
|
||||
*
|
||||
* @param array $entities
|
||||
* An array of entities keyed by entity ID.
|
||||
* @param int $age
|
||||
* FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
|
||||
* FIELD_LOAD_REVISION to load the version indicated by each entity.
|
||||
*/
|
||||
abstract protected function doLoadFieldItems($entities, $age);
|
||||
|
||||
/**
|
||||
* Saves values of configurable fields for an entity.
|
||||
*
|
||||
* This is the method that holds the actual storage logic.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
* @param bool $update
|
||||
* TRUE if the entity is being updated, FALSE if it is being inserted.
|
||||
*/
|
||||
abstract protected function doSaveFieldItems(EntityInterface $entity, $update);
|
||||
|
||||
/**
|
||||
* Deletes values of configurable fields for all revisions of an entity.
|
||||
*
|
||||
* This is the method that holds the actual storage logic.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
*/
|
||||
abstract protected function doDeleteFieldItems(EntityInterface $entity);
|
||||
|
||||
/**
|
||||
* Deletes values of configurable fields for a single revision of an entity.
|
||||
*
|
||||
* This is the method that holds the actual storage logic.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
*/
|
||||
abstract protected function doDeleteFieldItemsRevision(EntityInterface $entity);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldCreate(FieldInterface $field) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldUpdate(FieldInterface $field) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldDelete(FieldInterface $field) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onInstanceCreate(FieldInstanceInterface $instance) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onInstanceUpdate(FieldInstanceInterface $instance) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onInstanceDelete(FieldInstanceInterface $instance) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBundleCreate($bundle) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBundleRename($bundle, $bundle_new) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBundleDelete($bundle) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
|
||||
if ($values = $this->readFieldItemsToPurge($entity, $instance)) {
|
||||
$field = $instance->getField();
|
||||
$definition = _field_generate_entity_field_definition($field, $instance);
|
||||
$items = \Drupal::typedData()->create($definition, $values, $field->getFieldName(), $entity);
|
||||
$items->delete();
|
||||
}
|
||||
$this->purgeFieldItems($entity, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads values to be purged for a single field of a single entity.
|
||||
*
|
||||
* This method is called during field data purge, on fields for which
|
||||
* onFieldDelete() or onFieldInstanceDelete() has previously run.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
* @param \Drupal\field\FieldInstanceInterface $instance
|
||||
* The field instance.
|
||||
*
|
||||
* @return array
|
||||
* The field values, in their canonical array format (numerically indexed
|
||||
* array of items, each item being a property/value array).
|
||||
*/
|
||||
abstract protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance);
|
||||
|
||||
/**
|
||||
* Removes field data from storage during purge.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity whose values are being purged.
|
||||
* @param FieldInstanceInterface $instance
|
||||
* The field whose values are bing purged.
|
||||
*/
|
||||
abstract protected function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldPurge(FieldInterface $field) { }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\ExtensibleEntityStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
use Drupal\field\FieldInterface;
|
||||
use Drupal\field\FieldInstanceInterface;
|
||||
|
||||
interface FieldableEntityStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Allows reaction to the creation of a configurable field.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field being created.
|
||||
*/
|
||||
public function onFieldCreate(FieldInterface $field);
|
||||
|
||||
/**
|
||||
* Allows reaction to the update of a configurable field.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field being updated.
|
||||
*/
|
||||
public function onFieldUpdate(FieldInterface $field);
|
||||
|
||||
/**
|
||||
* Allows reaction to the deletion of a configurable field.
|
||||
*
|
||||
* Stored values should not be wiped at once, but marked as 'deleted' so that
|
||||
* they can go through a proper purge process later on.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field being deleted.
|
||||
*
|
||||
* @see fieldPurgeData()
|
||||
*/
|
||||
public function onFieldDelete(FieldInterface $field);
|
||||
|
||||
/**
|
||||
* Allows reaction to the creation of a configurable field instance.
|
||||
*
|
||||
* @param \Drupal\field\FieldInstanceInterface $instance
|
||||
* The instance being created.
|
||||
*/
|
||||
public function onInstanceCreate(FieldInstanceInterface $instance);
|
||||
|
||||
/**
|
||||
* Allows reaction to the update of a configurable field instance.
|
||||
*
|
||||
* @param \Drupal\field\FieldInstanceInterface $instance
|
||||
* The instance being updated.
|
||||
*/
|
||||
public function onInstanceUpdate(FieldInstanceInterface $instance);
|
||||
|
||||
/**
|
||||
* Allows reaction to the deletion of a configurable field instance.
|
||||
*
|
||||
* Stored values should not be wiped at once, but marked as 'deleted' so that
|
||||
* they can go through a proper purge process later on.
|
||||
*
|
||||
* @param \Drupal\field\FieldInstanceInterface $instance
|
||||
* The instance being deleted.
|
||||
*
|
||||
* @see fieldPurgeData()
|
||||
*/
|
||||
public function onInstanceDelete(FieldInstanceInterface $instance);
|
||||
|
||||
/**
|
||||
* Allows reaction to a bundle being created.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The name of the bundle created.
|
||||
*/
|
||||
public function onBundleCreate($bundle);
|
||||
|
||||
/**
|
||||
* Allows reaction to a bundle being renamed.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The name of the bundle being renamed.
|
||||
* @param string $bundle_new
|
||||
* The new name of the bundle.
|
||||
*/
|
||||
public function onBundleRename($bundle, $bundle_new);
|
||||
|
||||
/**
|
||||
* Allows reaction to a bundle being deleted.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The name of the bundle being deleted.
|
||||
*/
|
||||
public function onBundleDelete($bundle);
|
||||
|
||||
/**
|
||||
* Purges the field data for a single field on a single entity.
|
||||
*
|
||||
* 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 entity whose field data is being purged.
|
||||
* @param \Drupal\field\FieldInstanceInterface $instance
|
||||
* The deleted field instance whose data is being purged.
|
||||
*/
|
||||
public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceInterface $instance);
|
||||
|
||||
/**
|
||||
* Performs final cleanup after all data on all instances has been purged.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $instance
|
||||
* The field being purged.
|
||||
*/
|
||||
public function onFieldPurge(FieldInterface $field);
|
||||
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Entity\Query\Sql;
|
||||
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\DatabaseStorageController;
|
||||
use Drupal\Core\Entity\Plugin\DataType\EntityReference;
|
||||
use Drupal\Core\Entity\Query\QueryException;
|
||||
use Drupal\field\Entity\Field;
|
||||
|
|
@ -102,8 +103,8 @@ class Tables implements TablesInterface {
|
|||
if (substr($specifier, 0, 3) == 'id:') {
|
||||
$field = $field_info->getFieldById((substr($specifier, 3)));
|
||||
}
|
||||
elseif (isset($field_map[$specifier])) {
|
||||
$field = $field_info->getField($specifier);
|
||||
elseif (isset($field_map[$entity_type][$specifier])) {
|
||||
$field = $field_info->getField($entity_type, $specifier);
|
||||
}
|
||||
else {
|
||||
$field = FALSE;
|
||||
|
|
@ -136,7 +137,7 @@ class Tables implements TablesInterface {
|
|||
$field_name = $field->getFieldName();
|
||||
// If there are bundles, pick one.
|
||||
if (!empty($entity_info['entity_keys']['bundle'])) {
|
||||
$values[$entity_info['entity_keys']['bundle']] = reset($field_map[$field_name]['bundles'][$entity_type]);
|
||||
$values[$entity_info['entity_keys']['bundle']] = reset($field_map[$entity_type][$field_name]['bundles']);
|
||||
}
|
||||
$entity = $entity_manager
|
||||
->getStorageController($entity_type)
|
||||
|
|
@ -161,7 +162,7 @@ class Tables implements TablesInterface {
|
|||
$column = 'value';
|
||||
}
|
||||
$table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
|
||||
$sql_column = _field_sql_storage_columnname($field['field_name'], $column);
|
||||
$sql_column = DatabaseStorageController::_fieldColumnName($field, $column);
|
||||
}
|
||||
// This is an entity property (non-configurable field).
|
||||
else {
|
||||
|
|
@ -250,12 +251,12 @@ class Tables implements TablesInterface {
|
|||
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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -203,13 +203,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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use Drupal\Core\Entity\EntityStorageControllerInterface;
|
|||
* 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().
|
||||
|
|
|
|||
|
|
@ -65,13 +65,14 @@ class CustomBlockFieldTest extends CustomBlockTestBase {
|
|||
|
||||
// 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(
|
||||
|
|
|
|||
|
|
@ -160,9 +160,8 @@ function hook_comment_unpublish(Drupal\comment\Comment $comment) {
|
|||
/**
|
||||
* Act before comment deletion.
|
||||
*
|
||||
* This hook is invoked from entity_delete_multiple() before
|
||||
* field_attach_delete() is called and before the comment is actually removed
|
||||
* from the database.
|
||||
* This hook is invoked from entity_delete_multiple() before field values are
|
||||
* deleted and before the comment is actually removed from the database.
|
||||
*
|
||||
* @param Drupal\comment\Comment $comment
|
||||
* The comment object for the comment that is about to be deleted.
|
||||
|
|
@ -180,9 +179,8 @@ function hook_comment_predelete(Drupal\comment\Comment $comment) {
|
|||
/**
|
||||
* Respond to comment deletion.
|
||||
*
|
||||
* This hook is invoked from entity_delete_multiple() after
|
||||
* field_attach_delete() has called and after the comment has been removed from
|
||||
* the database.
|
||||
* This hook is invoked from entity_delete_multiple() after field values are
|
||||
* deleted and after the comment has been removed from the database.
|
||||
*
|
||||
* @param Drupal\comment\Comment $comment
|
||||
* The comment object for the comment that has been deleted.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -318,11 +318,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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class CommentFieldsTest extends CommentTestBase {
|
|||
}
|
||||
|
||||
// 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 @@ class CommentFieldsTest extends CommentTestBase {
|
|||
|
||||
// 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)));
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class CommentLanguageTest extends WebTestBase {
|
|||
$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.');
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class CommentTranslationUITest extends ContentTranslationUITest {
|
|||
*/
|
||||
function setupTestFields() {
|
||||
parent::setupTestFields();
|
||||
$field = field_info_field('comment_body');
|
||||
$field = field_info_field('comment', 'comment_body');
|
||||
$field['translatable'] = TRUE;
|
||||
$field->save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class CommentUninstallTest extends WebTestBase {
|
|||
*/
|
||||
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 @@ class CommentUninstallTest extends WebTestBase {
|
|||
$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 @@ class CommentUninstallTest extends WebTestBase {
|
|||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\contact\Tests\Views;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageController;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
|
||||
/**
|
||||
|
|
@ -40,13 +41,14 @@ class ContactFieldsTest extends ViewTestBase {
|
|||
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 @@ class ContactFieldsTest extends ViewTestBase {
|
|||
* Tests the views data generation.
|
||||
*/
|
||||
public function testViewsData() {
|
||||
$table_name = _field_sql_storage_tablename($this->field);
|
||||
// Test that the field is not exposed to views, since contact_message
|
||||
// entities have no storage.
|
||||
$table_name = DatabaseStorageController::_fieldTableName($this->field);
|
||||
$data = $this->container->get('views.views_data')->get($table_name);
|
||||
|
||||
// Test that the expected data array is returned.
|
||||
$expected = array('', '_value', '_format');
|
||||
$this->assertEqual(count($data), count($expected), 'The expected amount of array keys were found.');
|
||||
foreach ($expected as $suffix) {
|
||||
$this->assertTrue(isset($data[$this->field->id() . $suffix]));
|
||||
}
|
||||
$this->assertTrue(empty($data['table']['join']), 'The field is not joined to the non existent contact message base table.');
|
||||
$this->assertFalse($data, 'The field is not exposed to Views.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class ContentTranslationController implements ContentTranslationControllerInterf
|
|||
// 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ class TranslatableForm extends ConfirmFormBase {
|
|||
* {@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 @@ class TranslatableForm extends ConfirmFormBase {
|
|||
$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'] ? "<br>" . t("<strong>All the existing translations of this field will be deleted.</strong><br>This action cannot be undone.") : '';
|
||||
$description .= $this->field['translatable'] ? "<br>" . t("<strong>All the existing translations of this field will be deleted.</strong><br>This action cannot be undone.") : '';
|
||||
return $description;
|
||||
}
|
||||
|
||||
|
|
@ -71,12 +72,14 @@ class TranslatableForm extends ConfirmFormBase {
|
|||
|
||||
/**
|
||||
* {@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 @@ class TranslatableForm extends ConfirmFormBase {
|
|||
array(
|
||||
'content_translation_translatable_switch', array(
|
||||
!$translatable,
|
||||
$this->field['entity_type'],
|
||||
$this->fieldName,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class ContentTranslationSettingsTest extends WebTestBase {
|
|||
);
|
||||
$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.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ class ContentTranslationSyncImageTest extends ContentTranslationTestBase {
|
|||
$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,
|
||||
|
|
|
|||
|
|
@ -162,8 +162,9 @@ abstract class ContentTranslationTestBase extends WebTestBase {
|
|||
$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();
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ class DateTimeItemTest extends FieldUnitTestBase {
|
|||
|
||||
// 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();
|
||||
|
|
|
|||
|
|
@ -57,13 +57,14 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
|
||||
// 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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
$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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
'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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
* 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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
* 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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
* 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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
// 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 @@ class DatetimeFieldTest extends WebTestBase {
|
|||
// 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');
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ class EditEntityFieldAccessCheck implements StaticAccessCheckInterface, EditEnti
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function accessEditEntityField(EntityInterface $entity, $field_name) {
|
||||
return $entity->access('update') && ($field = $this->fieldInfo->getField($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
|
||||
$entity_type = $entity->entityType();
|
||||
return $entity->access('update') && ($field = $this->fieldInfo->getField($entity_type, $field_name)) && field_access('edit', $field, $entity_type, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class EditTestBase extends DrupalUnitTestBase {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'filter', 'user', 'text', 'edit');
|
||||
public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_test', 'number', 'filter', 'user', 'text', 'edit');
|
||||
|
||||
/**
|
||||
* Sets the default field storage backend for fields created during tests.
|
||||
*/
|
||||
|
|
@ -55,7 +56,8 @@ class EditTestBase extends DrupalUnitTestBase {
|
|||
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,
|
||||
));
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ class EmailFieldTest extends WebTestBase {
|
|||
// 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();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class EmailItemTest extends FieldUnitTestBase {
|
|||
|
||||
// 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(
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ class EntityDisplayTest extends DrupalUnitTestBase {
|
|||
* 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 @@ class EntityDisplayTest extends DrupalUnitTestBase {
|
|||
$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 @@ class EntityDisplayTest extends DrupalUnitTestBase {
|
|||
* 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 @@ class EntityDisplayTest extends DrupalUnitTestBase {
|
|||
* 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();
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class EntityFormDisplayTest extends DrupalUnitTestBase {
|
|||
* 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 @@ class EntityFormDisplayTest extends DrupalUnitTestBase {
|
|||
// 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();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ function entity_reference_field_info_alter(&$info) {
|
|||
function entity_reference_entity_field_info_alter(&$info, $entity_type) {
|
||||
foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
|
||||
foreach ($instances as $field_name => $instance) {
|
||||
$field = field_info_field($field_name);
|
||||
$field = $instance->getField();
|
||||
if ($field['type'] != 'entity_reference') {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ function entity_reference_field_entity_update(FieldInterface $field) {
|
|||
return;
|
||||
}
|
||||
|
||||
$field_name = $field->id();
|
||||
$field_name = $field->getFieldName();
|
||||
|
||||
foreach ($field->bundles() as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle) {
|
||||
|
|
@ -225,14 +225,14 @@ function entity_reference_create_instance($entity_type, $bundle, $field_name, $f
|
|||
}
|
||||
|
||||
// Look for or add the specified field to the requested entity bundle.
|
||||
$field = field_info_field($field_name);
|
||||
$field = field_info_field($entity_type, $field_name);
|
||||
$instance = field_info_instance($entity_type, $field_name, $bundle);
|
||||
|
||||
if (empty($field)) {
|
||||
$field = array(
|
||||
'field_name' => $field_name,
|
||||
'name' => $field_name,
|
||||
'type' => 'entity_reference',
|
||||
'entity_types' => array($entity_type),
|
||||
'entity_type' => $entity_type,
|
||||
'settings' => array(
|
||||
'target_type' => $target_entity_type,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class EntityReferenceController implements ControllerInterface {
|
|||
* 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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ class SelectionBase implements SelectionInterface {
|
|||
$fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table']));
|
||||
foreach (field_info_instances($target_type) as $bundle_instances) {
|
||||
foreach ($bundle_instances as $instance_name => $instance_info) {
|
||||
$field_info = field_info_field($instance_name);
|
||||
$field_info = $instance_info->getField();
|
||||
foreach ($field_info['columns'] as $column_name => $column_info) {
|
||||
$fields[$instance_name . '.' . $column_name] = t('@label (@column)', array('@label' => $instance_info['label'], '@column' => $column_name));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@ class EntityReferenceAutoCreateTest extends WebTestBase {
|
|||
$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();
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class EntityReferenceFieldTest extends EntityUnitTestBase {
|
|||
array('target_bundles' => array($this->bundle))
|
||||
);
|
||||
|
||||
$this->field = Field::fieldInfo()->getField($this->fieldName);
|
||||
$this->field = Field::fieldInfo()->getField($this->entityType, $this->fieldName);
|
||||
$instances = Field::fieldInfo()->getBundleInstances($this->entityType, $this->bundle);
|
||||
$this->instance = $instances[$this->fieldName];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
|
|||
* 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 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
|
|||
// 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.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
);
|
||||
}
|
||||
|
||||
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 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
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 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
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 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
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',
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
|
|||
);
|
||||
}
|
||||
|
||||
public static $modules = array('node', 'entity_reference');
|
||||
public static $modules = array('node', 'entity_reference', 'entity_test');
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
|
@ -37,7 +37,8 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
|
|||
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 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
|
|||
|
||||
// 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',
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use Drupal\simpletest\WebTestBase;
|
|||
*/
|
||||
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 @@ class SelectionTest extends WebTestBase {
|
|||
|
||||
// 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',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
default_storage: field_sql_storage
|
||||
language_fallback: '1'
|
||||
purge_batch_size: 10
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -333,27 +333,6 @@ function hook_field_attach_form(\Drupal\Core\Entity\EntityInterface $entity, &$f
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on field_attach_load().
|
||||
*
|
||||
* This hook is invoked after the field module has performed the operation.
|
||||
*
|
||||
* Unlike other field_attach hooks, this hook accounts for 'multiple loads'.
|
||||
* Instead of the usual $entity parameter, it accepts an array of entities,
|
||||
* indexed by entity ID. For performance reasons, information for all available
|
||||
* entities should be loaded in a single query where possible.
|
||||
*
|
||||
* The changes made to the entities' field values get cached by the field cache
|
||||
* for subsequent loads.
|
||||
*
|
||||
* See field_attach_load() for details and arguments.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function hook_field_attach_load($entity_type, $entities, $age, $options) {
|
||||
// @todo Needs function body.
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on field_attach_extract_form_values().
|
||||
*
|
||||
|
|
@ -399,31 +378,6 @@ function hook_field_attach_preprocess_alter(&$variables, $context) {
|
|||
// @todo Needs function body.
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*
|
||||
|
|
@ -511,681 +465,6 @@ function hook_field_available_languages_alter(&$langcodes, $context) {
|
|||
* @} End of "addtogroup field_attach".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
|
|
@ -1216,10 +495,6 @@ function hook_field_info_max_weight($entity_type, $bundle, $context, $context_mo
|
|||
return $weights ? max($weights) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup field_storage".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup field_crud
|
||||
* @{
|
||||
|
|
@ -1299,63 +574,6 @@ function hook_field_purge_instance($instance) {
|
|||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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".
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -8,45 +8,6 @@
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
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
|
||||
* @{
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -582,215 +588,6 @@ function field_attach_form(EntityInterface $entity, &$form, &$form_state, $langc
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads fields for the current revisions of a group of entities.
|
||||
*
|
||||
* Loads all fields for each entity object in a group of a single entity type.
|
||||
* The loaded field values are added directly to the entity objects.
|
||||
*
|
||||
* field_attach_load() is automatically called by the default entity controller
|
||||
* class, and thus, in most cases, doesn't need to be explicitly called by the
|
||||
* entity type module.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of entities in $entities; e.g., 'node' or 'user'.
|
||||
* @param $entities
|
||||
* An array of entities for which to load fields, keyed by entity ID. Each
|
||||
* entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
|
||||
* filled in. The function adds the loaded field data directly in the entity
|
||||
* objects of the $entities array.
|
||||
* @param $age
|
||||
* FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
|
||||
* FIELD_LOAD_REVISION to load the version indicated by each entity. Defaults
|
||||
* to FIELD_LOAD_CURRENT; use field_attach_load_revision() instead of passing
|
||||
* FIELD_LOAD_REVISION.
|
||||
* @param $options
|
||||
* An associative array of additional options, with the following keys:
|
||||
* - instance: A field instance entity, If provided, only values for the
|
||||
* corresponding field will be loaded, and no cache is written. This
|
||||
* option is only supported when all $entities are within the same bundle.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
|
||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
||||
$load_deleted = !empty($options['instance']->deleted);
|
||||
|
||||
// Merge default options.
|
||||
$options += array('instance' => NULL);
|
||||
// Set options for hook invocations.
|
||||
$hook_options = array(
|
||||
'deleted' => $load_deleted,
|
||||
);
|
||||
if ($options['instance']) {
|
||||
$hook_options['field_id'] = $options['instance']->field_uuid;
|
||||
}
|
||||
|
||||
$info = entity_get_info($entity_type);
|
||||
// Only the most current revision of non-deleted fields for cacheable entity
|
||||
// types can be cached.
|
||||
$cache_read = $load_current && $info['field_cache'] && !$load_deleted;
|
||||
// In addition, do not write to the cache when loading a single field.
|
||||
$cache_write = $cache_read && !isset($options['instance']);
|
||||
|
||||
if (empty($entities)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we are working with a BC mode entity.
|
||||
foreach ($entities as $id => $entity) {
|
||||
$entities[$id] = $entity->getBCEntity();
|
||||
}
|
||||
|
||||
// Assume all entities will need to be queried. Entities found in the cache
|
||||
// will be removed from the list.
|
||||
$queried_entities = $entities;
|
||||
|
||||
// Fetch available entities from cache, if applicable.
|
||||
if ($cache_read) {
|
||||
// Build the list of cache entries to retrieve.
|
||||
$cids = array();
|
||||
foreach ($entities as $id => $entity) {
|
||||
$cids[] = "field:$entity_type:$id";
|
||||
}
|
||||
$cache = cache('field')->getMultiple($cids);
|
||||
// Put the cached field values back into the entities and remove them from
|
||||
// the list of entities to query.
|
||||
foreach ($entities as $id => $entity) {
|
||||
$cid = "field:$entity_type:$id";
|
||||
if (isset($cache[$cid])) {
|
||||
unset($queried_entities[$id]);
|
||||
foreach ($cache[$cid]->data as $field_name => $values) {
|
||||
$entity->$field_name = $values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch other entities from their storage location.
|
||||
if ($queried_entities) {
|
||||
// The invoke order is:
|
||||
// - hook_field_storage_pre_load()
|
||||
// - storage backend's hook_field_storage_load()
|
||||
// - Field class's prepareCache() method.
|
||||
// - hook_field_attach_load()
|
||||
|
||||
// Invoke hook_field_storage_pre_load(): let any module load field
|
||||
// data before the storage engine, accumulating along the way.
|
||||
$skip_fields = array();
|
||||
foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_load') as $module) {
|
||||
$function = $module . '_field_storage_pre_load';
|
||||
$function($entity_type, $queried_entities, $age, $skip_fields, $hook_options);
|
||||
}
|
||||
|
||||
// Collect the storage backends used by the remaining fields in the entities.
|
||||
$storages = array();
|
||||
foreach ($queried_entities as $entity) {
|
||||
$id = $entity->id();
|
||||
$vid = $entity->getRevisionId();
|
||||
|
||||
// Determine the list of field instances to work on.
|
||||
if ($options['instance']) {
|
||||
$instances = array($options['instance']);
|
||||
}
|
||||
else {
|
||||
$instances = field_info_instances($entity_type, $entity->bundle());
|
||||
}
|
||||
|
||||
foreach ($instances as $instance) {
|
||||
$field = $instance->getField();
|
||||
$field_name = $field->id();
|
||||
if (!isset($queried_entities[$id]->{$field_name})) {
|
||||
$queried_entities[$id]->{$field_name} = array();
|
||||
}
|
||||
if (!isset($skip_fields[$field->uuid])) {
|
||||
$storages[$field->storage['type']][$field->uuid][] = $load_current ? $id : $vid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke hook_field_storage_load() on the relevant storage backends.
|
||||
foreach ($storages as $storage => $fields) {
|
||||
$storage_info = field_info_storage_types($storage);
|
||||
module_invoke($storage_info['module'], 'field_storage_load', $entity_type, $queried_entities, $age, $fields, $hook_options);
|
||||
}
|
||||
|
||||
// Invoke the field type's prepareCache() method.
|
||||
if (empty($options['instance'])) {
|
||||
foreach ($queried_entities as $entity) {
|
||||
\Drupal::entityManager()
|
||||
->getStorageController($entity_type)
|
||||
->invokeFieldItemPrepareCache($entity);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Do not rely on invokeFieldItemPrepareCache(), which only works on
|
||||
// fields listed in getFieldDefinitions(), and will fail if we are loading
|
||||
// values for a deleted field. Instead, generate FieldItem objects
|
||||
// directly, and call their prepareCache() method.
|
||||
foreach ($queried_entities as $entity) {
|
||||
$field = $options['instance']->getField();
|
||||
$field_name = $field->id();
|
||||
// Call the prepareCache() method on each item.
|
||||
foreach ($entity->{$field_name} as $langcode => $values) {
|
||||
$definition = _field_generate_entity_field_definition($field, $options['instance']);
|
||||
$items = \Drupal::typedData()->create($definition, $values, $field_name, $entity);
|
||||
foreach ($items as $item) {
|
||||
if ($item instanceof PrepareCacheInterface) {
|
||||
$item->prepareCache();
|
||||
}
|
||||
}
|
||||
$entity->{$field_name}[$langcode] = $items->getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke hook_field_attach_load(): let other modules act on loading the
|
||||
// entity.
|
||||
Drupal::moduleHandler()->invokeAll('field_attach_load', $entity_type, $queried_entities, $age, $options);
|
||||
|
||||
// Build cache data.
|
||||
if ($cache_write) {
|
||||
foreach ($queried_entities as $id => $entity) {
|
||||
$data = array();
|
||||
$instances = field_info_instances($entity_type, $entity->bundle());
|
||||
foreach ($instances as $instance) {
|
||||
$data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
|
||||
}
|
||||
$cid = "field:$entity_type:$id";
|
||||
cache('field')->set($cid, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all fields for previous versions of a group of entities.
|
||||
*
|
||||
* Loading different versions of the same entities is not supported, and should
|
||||
* be done by separate calls to the function.
|
||||
*
|
||||
* field_attach_load_revision() is automatically called by the default entity
|
||||
* controller class, and thus, in most cases, doesn't need to be explicitly
|
||||
* called by the entity type module.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of entities in $entities; e.g. 'node' or 'user'.
|
||||
* @param $entities
|
||||
* An array of entities for which to load fields, keyed by entity ID. Each
|
||||
* entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
|
||||
* filled. The function adds the loaded field data directly in the entity
|
||||
* objects of the $entities array.
|
||||
* @param $options
|
||||
* An associative array of additional options. See field_attach_load() for
|
||||
* details.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function field_attach_load_revision($entity_type, $entities, $options = array()) {
|
||||
return field_attach_load($entity_type, $entities, FIELD_LOAD_REVISION, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs field validation against form-submitted field values.
|
||||
*
|
||||
|
|
@ -837,7 +634,7 @@ function field_attach_form_validate(EntityInterface $entity, $form, &$form_state
|
|||
$has_violations = TRUE;
|
||||
|
||||
// Place violations in $form_state.
|
||||
$langcode = field_is_translatable($entity->entityType(), field_info_field($field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED;
|
||||
$langcode = field_is_translatable($entity->entityType(), field_info_field($entity->entityType(), $field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED;
|
||||
$field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
|
||||
$field_state['constraint_violations'] = $field_violations;
|
||||
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
|
||||
|
|
@ -888,166 +685,13 @@ function field_attach_extract_form_values(EntityInterface $entity, $form, &$form
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save field data for a new entity.
|
||||
*
|
||||
* The passed-in entity must already contain its id and (if applicable)
|
||||
* revision id attributes.
|
||||
* Default values (if any) will be saved for fields not present in the
|
||||
* $entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity with fields to save.
|
||||
* @return
|
||||
* Default values (if any) will be added to the $entity parameter for fields
|
||||
* it leaves unspecified.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function field_attach_insert(EntityInterface $entity) {
|
||||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
// Let any module insert field data before the storage engine, accumulating
|
||||
// saved fields along the way.
|
||||
$skip_fields = array();
|
||||
foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_insert') as $module) {
|
||||
$function = $module . '_field_storage_pre_insert';
|
||||
$function($entity, $skip_fields);
|
||||
}
|
||||
|
||||
// Collect the storage backends used by the remaining fields in the entities.
|
||||
$storages = array();
|
||||
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
|
||||
$field = field_info_field_by_id($instance['field_id']);
|
||||
$field_id = $field['uuid'];
|
||||
$field_name = $field['field_name'];
|
||||
if (!empty($entity->$field_name)) {
|
||||
// Collect the storage backend if the field has not been written yet.
|
||||
if (!isset($skip_fields[$field_id])) {
|
||||
$storages[$field['storage']['type']][$field_id] = $field_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Field storage backends save any remaining unsaved fields.
|
||||
foreach ($storages as $storage => $fields) {
|
||||
$storage_info = field_info_storage_types($storage);
|
||||
module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_INSERT, $fields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves field data for an existing entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity with fields to save.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function field_attach_update(EntityInterface $entity) {
|
||||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
// Let any module update field data before the storage engine, accumulating
|
||||
// saved fields along the way.
|
||||
$skip_fields = array();
|
||||
foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_update') as $module) {
|
||||
$function = $module . '_field_storage_pre_update';
|
||||
$function($entity, $skip_fields);
|
||||
}
|
||||
|
||||
// Collect the storage backends used by the remaining fields in the entities.
|
||||
$storages = array();
|
||||
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
|
||||
$field = field_info_field_by_id($instance['field_id']);
|
||||
$field_id = $field['uuid'];
|
||||
// Collect the storage backend if the field has not been written yet.
|
||||
if (!isset($skip_fields[$field_id])) {
|
||||
$storages[$field['storage']['type']][$field_id] = $field_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Field storage backends save any remaining unsaved fields.
|
||||
foreach ($storages as $storage => $fields) {
|
||||
$storage_info = field_info_storage_types($storage);
|
||||
module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_UPDATE, $fields);
|
||||
}
|
||||
|
||||
$entity_info = $entity->entityInfo();
|
||||
if ($entity_info['field_cache']) {
|
||||
cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes field data for an existing entity. This deletes all revisions of
|
||||
* field data for the entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity whose field data to delete.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function field_attach_delete(EntityInterface $entity) {
|
||||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
// Collect the storage backends used by the fields in the entities.
|
||||
$storages = array();
|
||||
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
|
||||
$field = field_info_field_by_id($instance['field_id']);
|
||||
$field_id = $field['uuid'];
|
||||
$storages[$field['storage']['type']][$field_id] = $field_id;
|
||||
}
|
||||
|
||||
// Field storage backends delete their data.
|
||||
foreach ($storages as $storage => $fields) {
|
||||
$storage_info = field_info_storage_types($storage);
|
||||
module_invoke($storage_info['module'], 'field_storage_delete', $entity, $fields);
|
||||
}
|
||||
|
||||
$entity_info = $entity->entityInfo();
|
||||
if ($entity_info['field_cache']) {
|
||||
cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete field data for a single revision of an existing entity. The passed
|
||||
* entity must have a revision ID attribute.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity with fields to save.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use the entity system instead.
|
||||
*/
|
||||
function field_attach_delete_revision(EntityInterface $entity) {
|
||||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
// Collect the storage backends used by the fields in the entities.
|
||||
$storages = array();
|
||||
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
|
||||
$field = field_info_field_by_id($instance['field_id']);
|
||||
$field_id = $field['uuid'];
|
||||
$storages[$field['storage']['type']][$field_id] = $field_id;
|
||||
}
|
||||
|
||||
// Field storage backends delete their data.
|
||||
foreach ($storages as $storage => $fields) {
|
||||
$storage_info = field_info_storage_types($storage);
|
||||
module_invoke($storage_info['module'], 'field_storage_delete_revision', $entity, $fields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares field data prior to display.
|
||||
*
|
||||
* This function lets field types and formatters load additional data needed for
|
||||
* display that is not automatically loaded during field_attach_load(). It
|
||||
* accepts an array of entities to allow query optimization when displaying
|
||||
* lists of entities.
|
||||
* display that is not automatically loaded during entity loading. It accepts an
|
||||
* array of entities to allow query optimization when displaying lists of
|
||||
* entities.
|
||||
*
|
||||
* field_attach_prepare_view() and field_attach_view() are two halves of the
|
||||
* same operation. It is safe to call field_attach_prepare_view() multiple times
|
||||
|
|
|
|||
|
|
@ -41,76 +41,9 @@ 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.
|
||||
*
|
||||
|
|
@ -134,31 +67,6 @@ function field_behaviors_widget($op, $instance) {
|
|||
return isset($info[$op]) ? $info[$op] : FIELD_BEHAVIOR_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
@ -251,22 +149,6 @@ function field_info_extra_fields($entity_type, $bundle, $context) {
|
|||
return isset($info[$context]) ? $info[$context] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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".
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
@ -97,6 +93,75 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc
|
|||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes field data directly to SQL storage.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param string $bundle
|
||||
* The bundle.
|
||||
* @param int $entity_id
|
||||
* The entity ID.
|
||||
* @param int $revision_id
|
||||
* The entity revision ID.
|
||||
* @param string $field_name
|
||||
* The field name.
|
||||
* @param array $data
|
||||
* The field values, as an array keyed by langcode, delta and property name.
|
||||
*
|
||||
* @ingroup update_api
|
||||
*/
|
||||
function _update_8006_field_write_data_sql($entity_type, $bundle, $entity_id, $revision_id, $field_name, array $data) {
|
||||
if (!isset($revision_id)) {
|
||||
$revision_id = $entity_id;
|
||||
}
|
||||
|
||||
$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);
|
||||
$revision_query->values($record);
|
||||
}
|
||||
$query->execute();
|
||||
$revision_query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup updates-7.x-to-8.x
|
||||
* @{
|
||||
|
|
@ -255,27 +320,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 +355,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 +429,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 +473,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.
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ require_once __DIR__ . '/field.deprecated.inc';
|
|||
* type 'image'. The administrator (again, via a UI) creates two Field
|
||||
* Instances, one attaching the field 'subtitle' to the 'node' bundle 'article'
|
||||
* and one attaching the field 'photo' to the 'node' bundle 'article'. When the
|
||||
* node system uses the Field Attach API to load all fields for an Article node,
|
||||
* it passes the node's entity type (which is 'node') and content type (which is
|
||||
* 'article') as the node's bundle. field_attach_load() then loads the
|
||||
* node storage controller loads an Article node, it loads the values of the
|
||||
* 'subtitle' and 'photo' fields because they are both attached to the 'node'
|
||||
* bundle 'article'.
|
||||
*
|
||||
|
|
@ -272,7 +270,7 @@ function field_entity_field_info($entity_type) {
|
|||
function _field_generate_entity_field_definition(FieldInterface $field, FieldInstanceInterface $instance = NULL) {
|
||||
// @todo: Allow for adding field type settings.
|
||||
$definition = array(
|
||||
'label' => t('Field !name', array('!name' => $field->id())),
|
||||
'label' => t('Field !name', array('!name' => $field->name)),
|
||||
'type' => 'field_item:' . $field->type,
|
||||
'list' => TRUE,
|
||||
'configurable' => TRUE,
|
||||
|
|
@ -399,24 +397,8 @@ function field_sync_field_status() {
|
|||
// modules.
|
||||
$changed = array();
|
||||
$modules = $module_handler->getModuleList();
|
||||
foreach ($modules as $module => $module_info) {
|
||||
// Collect storage backends exposed by the module.
|
||||
$storage_types = (array) $module_handler->invoke($module, 'field_storage_info');
|
||||
|
||||
if ($storage_types) {
|
||||
foreach ($fields as $uuid => &$field) {
|
||||
// Associate storage backends.
|
||||
if (isset($storage_types[$field['storage']['type']]) && ($field['storage']['module'] !== $module || !$field['storage']['active'])) {
|
||||
$field['storage']['module'] = $module;
|
||||
$field['storage']['active'] = TRUE;
|
||||
$changed[$uuid] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$field_types = Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
|
||||
// Set fields with missing field type or storage modules to inactive.
|
||||
// Set fields with missing field type modules to inactive.
|
||||
foreach ($fields as $uuid => &$field) {
|
||||
// Associate field types.
|
||||
if (isset($field_types[$field['type']]) && ($field['module'] != $field_types[$field['type']]['provider'] || !$field['active'])) {
|
||||
|
|
@ -428,11 +410,6 @@ function field_sync_field_status() {
|
|||
$field['active'] = FALSE;
|
||||
$changed[$uuid] = $field;
|
||||
}
|
||||
// Disassociate storage backends.
|
||||
if (!isset($modules[$field['storage']['module']]) && $field['storage']['active']) {
|
||||
$field['storage']['active'] = FALSE;
|
||||
$changed[$uuid] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the updated field definitions.
|
||||
|
|
@ -444,8 +421,6 @@ function field_sync_field_status() {
|
|||
Drupal::config('field.field.' . $field['id'])
|
||||
->set('module', $field['module'])
|
||||
->set('active', $field['active'])
|
||||
->set('storage.module', $field['storage']['module'])
|
||||
->set('storage.active', $field['storage']['active'])
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
|
@ -641,7 +616,7 @@ function field_view_value(EntityInterface $entity, $field_name, $item, $display
|
|||
// Ensure we are working with a BC mode entity.
|
||||
$entity = $entity->getBCEntity();
|
||||
|
||||
if ($field = field_info_field($field_name)) {
|
||||
if ($field = field_info_field($entity->entityType(), $field_name)) {
|
||||
// Determine the langcode that will be used by language fallback.
|
||||
$langcode = field_language($entity, $field_name, $langcode);
|
||||
|
||||
|
|
@ -731,7 +706,7 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options
|
|||
$view_mode = '_custom';
|
||||
// hook_field_attach_display_alter() needs to receive the 'prepared'
|
||||
// $display_options, so we cannot let preparation happen internally.
|
||||
$field = field_info_field($field_name);
|
||||
$field = field_info_field($entity_type, $field_name);
|
||||
$formatter_manager = drupal_container()->get('plugin.manager.field.formatter');
|
||||
$display_options = $formatter_manager->prepareConfiguration($field['type'], $display_options);
|
||||
$formatter = $formatter_manager->getInstance(array(
|
||||
|
|
@ -876,10 +851,12 @@ function template_preprocess_field(&$variables, $hook) {
|
|||
// Add default CSS classes. Since there can be many fields rendered on a page,
|
||||
// save some overhead by calling strtr() directly instead of
|
||||
// drupal_html_class().
|
||||
$variables['entity_type_css'] = strtr($element['#entity_type'], '_', '-');
|
||||
$variables['field_name_css'] = strtr($element['#field_name'], '_', '-');
|
||||
$variables['field_type_css'] = strtr($element['#field_type'], '_', '-');
|
||||
$variables['attributes']['class'] = array(
|
||||
'field',
|
||||
'field-' . $variables['entity_type_css'] . '--' . $variables['field_name_css'],
|
||||
'field-name-' . $variables['field_name_css'],
|
||||
'field-type-' . $variables['field_type_css'],
|
||||
'field-label-' . $element['#label_display'],
|
||||
|
|
@ -894,8 +871,9 @@ function template_preprocess_field(&$variables, $hook) {
|
|||
$variables['theme_hook_suggestions'] = array(
|
||||
'field__' . $element['#field_type'],
|
||||
'field__' . $element['#field_name'],
|
||||
'field__' . $element['#bundle'],
|
||||
'field__' . $element['#field_name'] . '__' . $element['#bundle'],
|
||||
'field__' . $element['#entity_type'] . '__' . $element['#bundle'],
|
||||
'field__' . $element['#entity_type'] . '__' . $element['#field_name'],
|
||||
'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'],
|
||||
);
|
||||
|
||||
static $default_attributes;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
* Field CRUD API, handling field and field instance creation and deletion.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\field\Entity\Field;
|
||||
use Drupal\field\FieldException;
|
||||
|
||||
|
|
@ -111,7 +110,7 @@ function field_purge_batch($batch_size) {
|
|||
'bundle' => $instance['bundle'],
|
||||
);
|
||||
// field_purge_data() will need the field array.
|
||||
$field = field_info_field_by_id($instance['field_id']);
|
||||
$field = $instance->getField();
|
||||
// Retrieve some entities.
|
||||
$query = $factory->get($entity_type)
|
||||
->condition('id:' . $field['uuid'] . '.deleted', 1)
|
||||
|
|
@ -121,21 +120,12 @@ function field_purge_batch($batch_size) {
|
|||
$query->condition($info[$entity_type]['entity_keys']['bundle'], $ids->bundle);
|
||||
}
|
||||
$results = $query->execute();
|
||||
|
||||
if ($results) {
|
||||
$entities = array();
|
||||
foreach ($results as $revision_id => $entity_id) {
|
||||
// This might not be the revision id if the entity type does not support
|
||||
// revisions but _field_create_entity_from_ids() checks that and
|
||||
// disregards this value so there is no harm setting it.
|
||||
$ids->revision_id = $revision_id;
|
||||
$ids->entity_id = $entity_id;
|
||||
$entities[$entity_id] = _field_create_entity_from_ids($ids);
|
||||
}
|
||||
field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
|
||||
foreach ($entities as $entity) {
|
||||
// Purge the data for the entity.
|
||||
field_purge_data($entity, $field, $instance);
|
||||
$entity = _field_create_entity_from_ids($ids);
|
||||
Drupal::entityManager()->getStorageController($entity_type)->onFieldItemsPurge($entity, $instance);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -155,37 +145,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.
|
||||
*
|
||||
|
|
@ -196,10 +155,6 @@ function field_purge_data(EntityInterface $entity, $field, $instance) {
|
|||
* The instance record to purge.
|
||||
*/
|
||||
function field_purge_instance($instance) {
|
||||
// Notify the storage engine.
|
||||
$field = field_info_field_by_id($instance['field_id']);
|
||||
module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance);
|
||||
|
||||
$state = Drupal::state();
|
||||
$deleted_instances = $state->get('field.instance.deleted');
|
||||
unset($deleted_instances[$instance['uuid']]);
|
||||
|
|
@ -232,8 +187,8 @@ function field_purge_field($field) {
|
|||
unset($deleted_fields[$field['uuid']]);
|
||||
$state->set('field.field.deleted', $deleted_fields);
|
||||
|
||||
// Notify the storage engine.
|
||||
module_invoke($field['storage']['module'], 'field_storage_purge_field', $field);
|
||||
// Notify the storage layer.
|
||||
Drupal::entityManager()->getStorageController($field->entity_type)->onFieldPurge($field);
|
||||
|
||||
// Clear the cache.
|
||||
field_info_cache_clear();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
$module_handler = Drupal::moduleHandler();
|
||||
|
||||
foreach (field_info_fields() as $field) {
|
||||
if ($field['storage']['type'] != 'field_sql_storage') {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
|
||||
$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);
|
||||
|
||||
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();
|
||||
|
||||
// Check the field module is available.
|
||||
if (!isset($field_types[$field['type']])) {
|
||||
return;
|
||||
}
|
||||
|
||||
function field_views_field_default_views_data(FieldInterface $field) {
|
||||
$data = array();
|
||||
|
||||
$current_table = _field_sql_storage_tablename($field);
|
||||
$revision_table = _field_sql_storage_revision_tablename($field);
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
$field_name = $field['field_name'];
|
||||
|
||||
$group_name = count($field['bundles']) > 1 ? t('Field') : NULL;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 ($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));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
$also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $title));
|
||||
else {
|
||||
$alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field_name, '!column' => $column));
|
||||
}
|
||||
$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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\field\Entity;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
|
|
@ -38,24 +39,35 @@ use Drupal\field\FieldInterface;
|
|||
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 +78,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.
|
||||
*
|
||||
|
|
@ -118,16 +137,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.
|
||||
*
|
||||
|
|
@ -142,25 +151,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.
|
||||
*
|
||||
|
|
@ -201,13 +191,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* The storage information for the field.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $storageDetails;
|
||||
|
||||
/**
|
||||
* The original field.
|
||||
*
|
||||
|
|
@ -223,8 +206,9 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
* elements will be used to set the corresponding properties on the class;
|
||||
* see the class property documentation for details. Some array elements
|
||||
* have special meanings and a few are required. Special elements are:
|
||||
* - id: required. As a temporary Backwards Compatibility layer right now,
|
||||
* - name: required. As a temporary Backwards Compatibility layer right now,
|
||||
* a 'field_name' property can be accepted in place of 'id'.
|
||||
* - entity_type: required.
|
||||
* - type: required.
|
||||
*
|
||||
* In most cases, Field entities are created via
|
||||
|
|
@ -237,26 +221,29 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
public function __construct(array $values, $entity_type = 'field_entity') {
|
||||
// Check required properties.
|
||||
if (empty($values['name'])) {
|
||||
throw new FieldException('Attempt to create an unnamed field.');
|
||||
}
|
||||
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.');
|
||||
}
|
||||
// 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'])) {
|
||||
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'])) {
|
||||
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['entity_type'])) {
|
||||
throw new FieldException('Attempt to create a field with no entity_type.');
|
||||
}
|
||||
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->entity_type . '.' . $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -266,12 +253,12 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
'uuid',
|
||||
'status',
|
||||
'langcode',
|
||||
'name',
|
||||
'entity_type',
|
||||
'type',
|
||||
'settings',
|
||||
'module',
|
||||
'active',
|
||||
'entity_types',
|
||||
'storage',
|
||||
'locked',
|
||||
'cardinality',
|
||||
'translatable',
|
||||
|
|
@ -297,7 +284,7 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
public function save() {
|
||||
// Clear the derived data about the field.
|
||||
unset($this->schema, $this->storageDetails);
|
||||
unset($this->schema);
|
||||
|
||||
if ($this->isNew()) {
|
||||
return $this->saveNew();
|
||||
|
|
@ -319,18 +306,20 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
* In case of failures at the configuration storage level.
|
||||
*/
|
||||
protected function saveNew() {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$storage_controller = $entity_manager->getStorageController($this->entityType);
|
||||
|
||||
// Field name cannot be longer than Field::ID_MAX_LENGTH characters. We
|
||||
// use drupal_strlen() because the DB layer assumes that column widths
|
||||
// Assign the ID.
|
||||
$this->id = $this->id();
|
||||
|
||||
// Field name cannot be longer than Field::NAME_MAX_LENGTH characters. We
|
||||
// use Unicode::strlen() because the DB layer assumes that column widths
|
||||
// are given in characters rather than bytes.
|
||||
if (drupal_strlen($this->id) > static::ID_MAX_LENGTH) {
|
||||
if (Unicode::strlen($this->name) > static::NAME_MAX_LENGTH) {
|
||||
throw new FieldException(format_string(
|
||||
'Attempt to create a field with an ID longer than @max characters: %id', array(
|
||||
'@max' => static::ID_MAX_LENGTH,
|
||||
'%id' => $this->id,
|
||||
'Attempt to create a field with an ID longer than @max characters: %name', array(
|
||||
'@max' => static::NAME_MAX_LENGTH,
|
||||
'%name' => $this->name,
|
||||
)
|
||||
));
|
||||
}
|
||||
|
|
@ -338,17 +327,17 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
// Ensure the field name is unique (we do not care about deleted fields).
|
||||
if ($prior_field = $storage_controller->load($this->id)) {
|
||||
$message = $prior_field->active ?
|
||||
'Attempt to create field name %id which already exists and is active.' :
|
||||
'Attempt to create field name %id which already exists, although it is inactive.';
|
||||
throw new FieldException(format_string($message, array('%id' => $this->id)));
|
||||
'Attempt to create field name %name which already exists and is active.' :
|
||||
'Attempt to create field name %name which already exists, although it is inactive.';
|
||||
throw new FieldException(format_string($message, array('%name' => $this->name)));
|
||||
}
|
||||
|
||||
// Disallow reserved field names. This can't prevent all field name
|
||||
// collisions with existing entity properties, but some is better than
|
||||
// none.
|
||||
foreach ($entity_manager->getDefinitions() as $type => $info) {
|
||||
if (in_array($this->id, $info['entity_keys'])) {
|
||||
throw new FieldException(format_string('Attempt to create field %id which is reserved by entity type %type.', array('%id' => $this->id, '%type' => $type)));
|
||||
if (in_array($this->name, $info['entity_keys'])) {
|
||||
throw new FieldException(format_string('Attempt to create field %name which is reserved by entity type %type.', array('%name' => $this->name, '%type' => $type)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -364,23 +353,8 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
// definition is passed to the various hooks and written to config.
|
||||
$this->settings += $field_type['settings'];
|
||||
|
||||
// Provide default storage.
|
||||
$this->storage += array(
|
||||
'type' => variable_get('field_storage_default', 'field_sql_storage'),
|
||||
'settings' => array(),
|
||||
);
|
||||
// Check that the storage type is known.
|
||||
$storage_type = field_info_storage_types($this->storage['type']);
|
||||
if (!$storage_type) {
|
||||
throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array('%type' => $this->storage['type'])));
|
||||
}
|
||||
$this->storage['module'] = $storage_type['module'];
|
||||
$this->storage['active'] = TRUE;
|
||||
// Provide default storage settings.
|
||||
$this->storage['settings'] += $storage_type['settings'];
|
||||
|
||||
// Invoke the storage backend's hook_field_storage_create_field().
|
||||
$module_handler->invoke($this->storage['module'], 'field_storage_create_field', array($this));
|
||||
// Notify the entity storage controller.
|
||||
$entity_manager->getStorageController($this->entity_type)->onFieldCreate($this);
|
||||
|
||||
// Save the configuration.
|
||||
$result = parent::save();
|
||||
|
|
@ -402,7 +376,8 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
protected function saveUpdated() {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$storage_controller = \Drupal::entityManager()->getStorageController($this->entityType);
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$storage_controller = $entity_manager->getStorageController($this->entityType);
|
||||
|
||||
$original = $storage_controller->loadUnchanged($this->id());
|
||||
$this->original = $original;
|
||||
|
|
@ -411,11 +386,8 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
if ($this->type != $original->type) {
|
||||
throw new FieldException("Cannot change an existing field's type.");
|
||||
}
|
||||
if ($this->entity_types != $original->entity_types) {
|
||||
throw new FieldException("Cannot change an existing field's entity_types property.");
|
||||
}
|
||||
if ($this->storage['type'] != $original->storage['type']) {
|
||||
throw new FieldException("Cannot change an existing field's storage type.");
|
||||
if ($this->entity_type != $original->entity_type) {
|
||||
throw new FieldException("Cannot change an existing field's entity_type.");
|
||||
}
|
||||
|
||||
// Make sure all settings are present, so that a complete field definition
|
||||
|
|
@ -427,11 +399,10 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
// invokes hook_field_update_forbid().
|
||||
$module_handler->invokeAll('field_update_forbid', array($this, $original));
|
||||
|
||||
// Tell the storage engine to update the field by invoking the
|
||||
// hook_field_storage_update_field(). The storage engine can reject the
|
||||
// definition update as invalid by raising an exception, which stops
|
||||
// execution before the definition is written to config.
|
||||
$module_handler->invoke($this->storage['module'], 'field_storage_update_field', array($this, $original));
|
||||
// Notify the storage controller. The controller can reject the definition
|
||||
// update as invalid by raising an exception, which stops execution before
|
||||
// the definition is written to config.
|
||||
$entity_manager->getStorageController($this->entity_type)->onFieldUpdate($this, $original);
|
||||
|
||||
// Save the configuration.
|
||||
$result = parent::save();
|
||||
|
|
@ -445,16 +416,13 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
public function delete() {
|
||||
if (!$this->deleted) {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$instance_controller = \Drupal::entityManager()->getStorageController('field_instance');
|
||||
$state = \Drupal::state();
|
||||
|
||||
// Delete all non-deleted instances.
|
||||
$instance_ids = array();
|
||||
foreach ($this->getBundles() as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle) {
|
||||
$instance_ids[] = "$entity_type.$bundle.$this->id";
|
||||
}
|
||||
foreach ($this->getBundles() as $bundle) {
|
||||
$instance_ids[] = "{$this->entity_type}.$bundle.{$this->name}";
|
||||
}
|
||||
foreach ($instance_controller->loadMultiple($instance_ids) as $instance) {
|
||||
// By default, FieldInstance::delete() will automatically try to delete
|
||||
|
|
@ -464,9 +432,7 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
$instance->delete(FALSE);
|
||||
}
|
||||
|
||||
// Mark field data for deletion by invoking
|
||||
// hook_field_storage_delete_field().
|
||||
$module_handler->invoke($this->storage['module'], 'field_storage_delete_field', array($this));
|
||||
\Drupal::entityManager()->getStorageController($this->entity_type)->onFieldDelete($this);
|
||||
|
||||
// Delete the configuration of this field and save the field configuration
|
||||
// in the key_value table so we can use it later during
|
||||
|
|
@ -526,33 +492,14 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
return $schema['columns'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStorageDetails() {
|
||||
if (!isset($this->storageDetails)) {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
|
||||
// Collect the storage details from the storage backend, and let other
|
||||
// modules alter it. This invokes hook_field_storage_details() and
|
||||
// hook_field_storage_details_alter().
|
||||
$details = (array) $module_handler->invoke($this->storage['module'], 'field_storage_details', array($this));
|
||||
$module_handler->alter('field_storage_details', $details, $this);
|
||||
|
||||
$this->storageDetails = $details;
|
||||
}
|
||||
|
||||
return $this->storageDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBundles() {
|
||||
if (empty($this->deleted)) {
|
||||
$map = field_info_field_map();
|
||||
if (isset($map[$this->id]['bundles'])) {
|
||||
return $map[$this->id]['bundles'];
|
||||
if (isset($map[$this->entity_type][$this->name]['bundles'])) {
|
||||
return $map[$this->entity_type][$this->name]['bundles'];
|
||||
}
|
||||
}
|
||||
return array();
|
||||
|
|
@ -562,7 +509,7 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldName() {
|
||||
return $this->id;
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -670,7 +617,7 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
return $this->uuid;
|
||||
|
||||
case 'field_name':
|
||||
return $this->id;
|
||||
return $this->name;
|
||||
|
||||
case 'columns':
|
||||
$this->getSchema();
|
||||
|
|
@ -683,10 +630,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
case 'bundles':
|
||||
$bundles = $this->getBundles();
|
||||
return $bundles;
|
||||
|
||||
case 'storage_details':
|
||||
$this->getStorageDetails();
|
||||
return $this->storageDetails;
|
||||
}
|
||||
|
||||
return $this->{$offset};
|
||||
|
|
@ -726,19 +669,19 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
* 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)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\field\Entity;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
|
|
@ -35,7 +36,7 @@ use Drupal\field\FieldInstanceInterface;
|
|||
class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
||||
|
||||
/**
|
||||
* The instance ID (machine name).
|
||||
* The instance ID.
|
||||
*
|
||||
* The ID consists of 3 parts: the entity type, bundle and the field name.
|
||||
*
|
||||
|
|
@ -235,10 +236,10 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
public function __construct(array $values, $entity_type = 'field_instance') {
|
||||
// Accept incoming 'field_name' instead of 'field_uuid', for easier DX on
|
||||
// creation of new instances.
|
||||
if (isset($values['field_name']) && !isset($values['field_uuid'])) {
|
||||
$field = field_info_field($values['field_name']);
|
||||
if (isset($values['field_name']) && isset($values['entity_type']) && !isset($values['field_uuid'])) {
|
||||
$field = field_info_field($values['entity_type'], $values['field_name']);
|
||||
if (!$field) {
|
||||
throw new FieldException(format_string('Attempt to create an instance of unknown, disabled, or deleted field @field_id', array('@field_id' => $values['field_name'])));
|
||||
throw new FieldException(format_string('Attempt to create an instance of field @field_name that does not exist on entity type @entity_type.', array('@field_name' => $values['field_name'], '@entity_type' => $values['entity_type'])));
|
||||
}
|
||||
$values['field_uuid'] = $field->uuid;
|
||||
}
|
||||
|
|
@ -267,16 +268,16 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
|
||||
// Check required properties.
|
||||
if (empty($values['entity_type'])) {
|
||||
throw new FieldException(format_string('Attempt to create an instance of field @field_id without an entity type.', array('@field_id' => $this->field->id)));
|
||||
throw new FieldException(format_string('Attempt to create an instance of field @field_name without an entity_type.', array('@field_name' => $this->field->name)));
|
||||
}
|
||||
if (empty($values['bundle'])) {
|
||||
throw new FieldException(format_string('Attempt to create an instance of field @field_id without a bundle.', array('@field_id' => $this->field->id)));
|
||||
throw new FieldException(format_string('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $this->field->name)));
|
||||
}
|
||||
|
||||
// 'Label' defaults to the field ID (mostly useful for field instances
|
||||
// 'Label' defaults to the field name (mostly useful for field instances
|
||||
// created in tests).
|
||||
$values += array(
|
||||
'label' => $this->field->id,
|
||||
'label' => $this->field->name,
|
||||
);
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
|
@ -285,7 +286,7 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->entity_type . '.' . $this->bundle . '.' . $this->field->id;
|
||||
return $this->entity_type . '.' . $this->bundle . '.' . $this->field->name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -357,14 +358,9 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
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.
|
||||
|
|
@ -412,6 +408,9 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
// Ensure default values are present.
|
||||
$this->prepareSave();
|
||||
|
||||
// Notify the entity storage controller.
|
||||
\Drupal::entityManager()->getStorageController($this->entity_type)->onInstanceUpdate($this);
|
||||
|
||||
// Save the configuration.
|
||||
$result = parent::save();
|
||||
field_cache_clear();
|
||||
|
|
@ -439,7 +438,6 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
*/
|
||||
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
|
||||
|
|
@ -453,16 +451,15 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
|
||||
parent::delete();
|
||||
|
||||
// Notify the entity storage controller.
|
||||
\Drupal::entityManager()->getStorageController($this->entity_type)->onInstanceDelete($this);
|
||||
|
||||
// Clear the cache.
|
||||
field_cache_clear();
|
||||
|
||||
// Mark instance data for deletion by invoking
|
||||
// hook_field_storage_delete_instance().
|
||||
$module_handler->invoke($this->field->storage['module'], 'field_storage_delete_instance', array($this));
|
||||
|
||||
// Remove the instance from the entity form displays.
|
||||
if ($form_display = entity_load('entity_form_display', $this->entity_type . '.' . $this->bundle . '.default')) {
|
||||
$form_display->removeComponent($this->field->id())->save();
|
||||
$form_display->removeComponent($this->field->name)->save();
|
||||
}
|
||||
|
||||
// Remove the instance from the entity displays.
|
||||
|
|
@ -472,7 +469,7 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
$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.
|
||||
|
|
@ -493,7 +490,7 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldName() {
|
||||
return $this->field->id;
|
||||
return $this->field->name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -620,7 +617,7 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
return $this->field_uuid;
|
||||
}
|
||||
if ($offset == 'field_name') {
|
||||
return $this->field->id;
|
||||
return $this->field->name;
|
||||
}
|
||||
return $this->{$offset};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,10 +165,10 @@ class FieldInfo {
|
|||
* 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 @@ class FieldInfo {
|
|||
// 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 @@ class FieldInfo {
|
|||
// 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 @@ class FieldInfo {
|
|||
// 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 @@ class FieldInfo {
|
|||
$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 @@ class FieldInfo {
|
|||
*
|
||||
* 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 @@ class FieldInfo {
|
|||
// Store in the static cache.
|
||||
$this->fieldsById[$field['uuid']] = $field;
|
||||
if (!$field['deleted']) {
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['uuid'];
|
||||
$this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
|
||||
}
|
||||
|
||||
return $field;
|
||||
|
|
@ -413,7 +415,7 @@ class FieldInfo {
|
|||
if (!isset($this->fieldsById[$field['uuid']])) {
|
||||
$this->fieldsById[$field['uuid']] = $field;
|
||||
if (!$field['deleted']) {
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['uuid'];
|
||||
$this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -435,43 +437,46 @@ class FieldInfo {
|
|||
}
|
||||
|
||||
// Cache miss: collect from the definitions.
|
||||
$field_map = $this->getFieldMap();
|
||||
$instances = array();
|
||||
$fields = array();
|
||||
|
||||
// Do not return anything for unknown entity types.
|
||||
if (entity_get_info($entity_type)) {
|
||||
if (entity_get_info($entity_type) && !empty($field_map[$entity_type])) {
|
||||
|
||||
// Collect names of fields and instances involved in the bundle, using the
|
||||
// field map. The field map is already filtered to active, non-deleted
|
||||
// fields and instances, so those are kept out of the persistent caches.
|
||||
$config_ids = array();
|
||||
foreach ($this->getFieldMap() as $field_name => $field_data) {
|
||||
if (isset($field_data['bundles'][$entity_type]) && in_array($bundle, $field_data['bundles'][$entity_type])) {
|
||||
$config_ids[$field_name] = "$entity_type.$bundle.$field_name";
|
||||
foreach ($field_map[$entity_type] as $field_name => $field_data) {
|
||||
if (in_array($bundle, $field_data['bundles'])) {
|
||||
$config_ids["$entity_type.$field_name"] = "$entity_type.$bundle.$field_name";
|
||||
}
|
||||
}
|
||||
|
||||
// Load and prepare the corresponding fields and instances entities.
|
||||
if ($config_ids) {
|
||||
// Place the fields in our global "static".
|
||||
$loaded_fields = entity_load_multiple('field_entity', array_keys($config_ids));
|
||||
$loaded_instances = entity_load_multiple('field_instance', array_values($config_ids));
|
||||
|
||||
foreach ($loaded_instances as $instance) {
|
||||
$field = $loaded_fields[$instance['field_name']];
|
||||
|
||||
$instance = $this->prepareInstance($instance, $field['type']);
|
||||
$instances[$field['field_name']] = $instance;
|
||||
|
||||
// If the field is not in our global "static" list yet, add it.
|
||||
foreach ($loaded_fields as $field) {
|
||||
if (!isset($this->fieldsById[$field['uuid']])) {
|
||||
$field = $this->prepareField($field);
|
||||
|
||||
$this->fieldsById[$field['uuid']] = $field;
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['uuid'];
|
||||
$this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
|
||||
}
|
||||
|
||||
$fields[] = $this->fieldsById[$field['uuid']];
|
||||
}
|
||||
|
||||
// Then collect the instances.
|
||||
$loaded_instances = entity_load_multiple('field_instance', array_values($config_ids));
|
||||
foreach ($loaded_instances as $instance) {
|
||||
$field = $instance->getField();
|
||||
|
||||
$instance = $this->prepareInstance($instance, $field['type']);
|
||||
$instances[$field['field_name']] = $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -567,7 +572,6 @@ class FieldInfo {
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,6 @@ class FieldInstanceStorageController extends ConfigStorageController {
|
|||
// 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 @@ class FieldInstanceStorageController extends ConfigStorageController {
|
|||
// 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;
|
||||
|
|
|
|||
|
|
@ -44,34 +44,6 @@ interface FieldInterface extends ConfigEntityInterface, FieldDefinitionInterface
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -98,9 +98,10 @@ class FieldStorageController extends ConfigStorageController {
|
|||
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 @@ class FieldStorageController extends ConfigStorageController {
|
|||
// 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 @@ class FieldStorageController extends ConfigStorageController {
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ class LegacyConfigField extends ConfigField {
|
|||
$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...
|
||||
|
|
|
|||
|
|
@ -90,8 +90,8 @@ abstract class LegacyConfigFieldItem extends ConfigFieldItemBase implements Prep
|
|||
$langcode = $entity->language()->id;
|
||||
$entity_id = $entity->id();
|
||||
|
||||
// hook_field_attach_load() receives items keyed by entity id, and alter
|
||||
// then by reference.
|
||||
// hook_field_load() receives items keyed by entity id, and alters then by
|
||||
// reference.
|
||||
$items = array($entity_id => array(0 => $this->getValue(TRUE)));
|
||||
$args = array(
|
||||
$entity->entityType(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ class Field extends FieldPluginBase {
|
|||
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 @@ class Field extends FieldPluginBase {
|
|||
}
|
||||
|
||||
$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 @@ class Field extends FieldPluginBase {
|
|||
$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 = '';
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use Drupal\Component\Annotation\PluginID;
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 @@ class ActiveTest extends FieldTestBase {
|
|||
* 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();
|
||||
))->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'));
|
||||
}
|
||||
// Check that the field is correctly found.
|
||||
$field = field_read_field('entity_test', $field_name);
|
||||
$this->assertFalse(empty($field), 'The field was properly read.');
|
||||
|
||||
/**
|
||||
* 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'];
|
||||
// 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.');
|
||||
|
||||
// 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.');
|
||||
// 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.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
|
||||
// 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 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
|
||||
// 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 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
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 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
$this->assertEqual(count($found), 10, 'Correct number of entities found before deleting');
|
||||
|
||||
// Delete the instance.
|
||||
$instance = field_info_instance($this->entity_type, $field->id(), $bundle);
|
||||
$instance = field_info_instance($this->entity_type, $field->name, $bundle);
|
||||
$instance->delete();
|
||||
|
||||
// The instance still exists, deleted.
|
||||
|
|
@ -184,6 +185,17 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
$instance = $instances[0];
|
||||
$this->assertEqual($instance['bundle'], $bundle, 'The deleted instance is for the correct bundle');
|
||||
|
||||
// Check that the actual stored content did not change during delete.
|
||||
$schema = DatabaseStorageController::_fieldSqlSchema($field);
|
||||
$table = DatabaseStorageController::_fieldTableName($field);
|
||||
$column = DatabaseStorageController::_fieldColumnName($field, 'value');
|
||||
$result = db_select($table, 't')
|
||||
->fields('t', array_keys($schema[$table]['fields']))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$this->assertEqual($this->entities[$row->entity_id]->{$field->name}->value, $row->$column);
|
||||
}
|
||||
|
||||
// There are 0 entities of this bundle with non-deleted data.
|
||||
$found = $factory->get('entity_test')
|
||||
->condition('type', $bundle)
|
||||
|
|
@ -198,20 +210,8 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
->condition("$field_name.deleted", 1)
|
||||
->sort('id')
|
||||
->execute();
|
||||
$ids = (object) array(
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => $bundle,
|
||||
);
|
||||
$entities = array();
|
||||
foreach ($found as $entity_id) {
|
||||
$ids->entity_id = $entity_id;
|
||||
$entities[$entity_id] = _field_create_entity_from_ids($ids);
|
||||
}
|
||||
field_attach_load($this->entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
|
||||
$this->assertEqual(count($found), 10, 'Correct number of entities found after deleting');
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->assertEqual($this->entities[$id]->{$field->id()}->value, $entity->{$field->id()}[Language::LANGCODE_NOT_SPECIFIED][0]['value'], "Entity $id with deleted data loaded correctly");
|
||||
}
|
||||
$this->assertFalse(array_diff($found, array_keys($this->entities)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -226,7 +226,7 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
$field = reset($this->fields);
|
||||
|
||||
// Delete the instance.
|
||||
$instance = field_info_instance($this->entity_type, $field->id(), $bundle);
|
||||
$instance = field_info_instance($this->entity_type, $field->name, $bundle);
|
||||
$instance->delete();
|
||||
|
||||
// No field hooks were called.
|
||||
|
|
@ -241,19 +241,18 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
// There are $count deleted entities left.
|
||||
$found = \Drupal::entityQuery('entity_test')
|
||||
->condition('type', $bundle)
|
||||
->condition($field->id() . '.deleted', 1)
|
||||
->condition($field->name . '.deleted', 1)
|
||||
->execute();
|
||||
$this->assertEqual(count($found), $count, 'Correct number of entities found after purging 2');
|
||||
}
|
||||
|
||||
// Check hooks invocations.
|
||||
// hook_field_load() and hook_field_delete() should have been called once
|
||||
// for each entity in the bundle.
|
||||
// hook_field_delete() should have been called once for each entity in the
|
||||
// bundle.
|
||||
$actual_hooks = field_test_memorize();
|
||||
$hooks = array();
|
||||
$entities = $this->entities_by_bundles[$bundle];
|
||||
foreach ($entities as $id => $entity) {
|
||||
$hooks['field_test_field_load'][] = array($id => $entity);
|
||||
$hooks['field_test_field_delete'][] = $entity;
|
||||
}
|
||||
$this->checkHooksInvocations($hooks, $actual_hooks);
|
||||
|
|
@ -286,7 +285,7 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
|
||||
// Delete the first instance.
|
||||
$bundle = reset($this->bundles);
|
||||
$instance = field_info_instance($this->entity_type, $field->id(), $bundle);
|
||||
$instance = field_info_instance($this->entity_type, $field->name, $bundle);
|
||||
$instance->delete();
|
||||
|
||||
// Assert that hook_field_delete() was not called yet.
|
||||
|
|
@ -297,13 +296,12 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
field_purge_batch(10);
|
||||
|
||||
// Check hooks invocations.
|
||||
// hook_field_load() and hook_field_delete() should have been called once
|
||||
// for each entity in the bundle.
|
||||
// hook_field_delete() should have been called once for each entity in the
|
||||
// bundle.
|
||||
$actual_hooks = field_test_memorize();
|
||||
$hooks = array();
|
||||
$entities = $this->entities_by_bundles[$bundle];
|
||||
foreach ($entities as $id => $entity) {
|
||||
$hooks['field_test_field_load'][] = array($id => $entity);
|
||||
$hooks['field_test_field_delete'][] = $entity;
|
||||
}
|
||||
$this->checkHooksInvocations($hooks, $actual_hooks);
|
||||
|
|
@ -317,7 +315,7 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
|
||||
// Delete the second instance.
|
||||
$bundle = next($this->bundles);
|
||||
$instance = field_info_instance($this->entity_type, $field->id(), $bundle);
|
||||
$instance = field_info_instance($this->entity_type, $field->name, $bundle);
|
||||
$instance->delete();
|
||||
|
||||
// Assert that hook_field_delete() was not called yet.
|
||||
|
|
@ -332,7 +330,6 @@ class BulkDeleteTest extends FieldUnitTestBase {
|
|||
$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);
|
||||
|
|
|
|||
|
|
@ -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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
*/
|
||||
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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
$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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
$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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
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 @@ class CrudTest extends FieldUnitTestBase {
|
|||
*/
|
||||
function testCreateFieldWithExplicitSchema() {
|
||||
$field_definition = array(
|
||||
'field_name' => 'field_2',
|
||||
'name' => 'field_2',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'schema' => array(
|
||||
'dummy' => 'foobar'
|
||||
|
|
@ -171,69 +189,32 @@ class CrudTest extends FieldUnitTestBase {
|
|||
$this->assertEqual($field->getSchema(), $field_definition['schema']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
// 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 +227,14 @@ class CrudTest extends FieldUnitTestBase {
|
|||
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 +242,17 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 +260,18 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 +284,38 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 +324,7 @@ class CrudTest extends FieldUnitTestBase {
|
|||
$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 +332,7 @@ class CrudTest extends FieldUnitTestBase {
|
|||
$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 +341,18 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 +360,11 @@ class CrudTest extends FieldUnitTestBase {
|
|||
}
|
||||
|
||||
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 +387,8 @@ class CrudTest extends FieldUnitTestBase {
|
|||
// 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 +401,18 @@ class CrudTest extends FieldUnitTestBase {
|
|||
$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 +425,8 @@ class CrudTest extends FieldUnitTestBase {
|
|||
*/
|
||||
function testUpdateFieldForbid() {
|
||||
$field = entity_create('field_entity', array(
|
||||
'field_name' => 'forbidden',
|
||||
'name' => 'forbidden',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'settings' => array(
|
||||
'changeable' => 0,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$this->cardinality = 4;
|
||||
|
||||
$field = array(
|
||||
'field_name' => $this->field_name,
|
||||
'name' => $this->field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => $this->cardinality,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -52,12 +52,13 @@ class FieldAccessTest extends FieldTestBase {
|
|||
$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 @@ class FieldAccessTest extends FieldTestBase {
|
|||
// 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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
|
|||
*/
|
||||
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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
|
|||
// 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');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,26 +62,22 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
// 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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
}
|
||||
|
||||
// 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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
);
|
||||
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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
$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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
$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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
// Insert: Field is NULL.
|
||||
$entity = clone($entity_init);
|
||||
$entity->getBCEntity()->{$this->field_name} = NULL;
|
||||
field_attach_insert($entity);
|
||||
|
||||
$entity = clone($entity_init);
|
||||
$entity->getBCEntity()->{$this->field_name} = array();
|
||||
field_attach_load($entity_type, array($entity->id() => $entity));
|
||||
$entity->enforceIsNew();
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
|
||||
|
||||
// Verify that prepopulated field values are not overwritten by defaults.
|
||||
|
|
@ -365,58 +247,60 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test field_attach_delete().
|
||||
* Test entity deletion.
|
||||
*/
|
||||
function testFieldAttachDelete() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithInstance('', $entity_type);
|
||||
$rev[0] = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
|
||||
$entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
|
||||
$vids = array();
|
||||
|
||||
// Create revision 0
|
||||
$values = $this->_generateTestFieldValues($this->field['cardinality']);
|
||||
$rev[0]->{$this->field_name} = $values;
|
||||
field_attach_insert($rev[0]);
|
||||
$entity->{$this->field_name} = $values;
|
||||
$entity->save();
|
||||
$vids[] = $entity->getRevisionId();
|
||||
|
||||
// Create revision 1
|
||||
$rev[1] = entity_create($entity_type, array('id' => 0, 'revision_id' => 1, 'type' => $this->instance['bundle']));
|
||||
$rev[1]->{$this->field_name} = $values;
|
||||
field_attach_update($rev[1]);
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$vids[] = $entity->getRevisionId();
|
||||
|
||||
// Create revision 2
|
||||
$rev[2] = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
|
||||
$rev[2]->{$this->field_name} = $values;
|
||||
field_attach_update($rev[2]);
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$vids[] = $entity->getRevisionId();
|
||||
$controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
|
||||
$controller->resetCache();
|
||||
|
||||
// Confirm each revision loads
|
||||
foreach (array_keys($rev) as $vid) {
|
||||
$read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
|
||||
field_attach_load_revision($entity_type, array(0 => $read));
|
||||
$this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
|
||||
foreach ($vids as $vid) {
|
||||
$revision = $controller->loadRevision($vid);
|
||||
$this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
|
||||
}
|
||||
|
||||
// Delete revision 1, confirm the other two still load.
|
||||
field_attach_delete_revision($rev[1]);
|
||||
foreach (array(0, 2) as $vid) {
|
||||
$read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
|
||||
field_attach_load_revision($entity_type, array(0 => $read));
|
||||
$this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
|
||||
$controller->deleteRevision($vids[1]);
|
||||
$controller->resetCache();
|
||||
foreach (array(0, 2) as $key) {
|
||||
$vid = $vids[$key];
|
||||
$revision = $controller->loadRevision($vid);
|
||||
$this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
|
||||
}
|
||||
|
||||
// Confirm the current revision still loads
|
||||
$read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
|
||||
field_attach_load($entity_type, array(0 => $read));
|
||||
$this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
|
||||
$controller->resetCache();
|
||||
$current = $controller->load($entity->id());
|
||||
$this->assertEqual(count($current->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
|
||||
|
||||
// Delete all field data, confirm nothing loads
|
||||
field_attach_delete($rev[2]);
|
||||
$entity->delete();
|
||||
$controller->resetCache();
|
||||
foreach (array(0, 1, 2) as $vid) {
|
||||
$read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
|
||||
field_attach_load_revision($entity_type, array(0 => $read));
|
||||
$this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), "The test entity revision $vid is deleted.");
|
||||
$revision = $controller->loadRevision($vid);
|
||||
$this->assertFalse($revision);
|
||||
}
|
||||
$read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
|
||||
field_attach_load($entity_type, array(0 => $read));
|
||||
$this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), 'The test entity current revision is deleted.');
|
||||
$this->assertFalse($controller->load($entity->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -435,14 +319,12 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
$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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
|
||||
// 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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
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 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
|
|||
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');
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@ class FieldImportCreateTest extends FieldUnitTestBase {
|
|||
* 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 @@ class FieldImportCreateTest extends FieldUnitTestBase {
|
|||
*/
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -31,11 +31,13 @@ class FieldImportDeleteTest extends FieldUnitTestBase {
|
|||
* 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";
|
||||
|
|
|
|||
|
|
@ -32,15 +32,6 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
$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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
// 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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
*/
|
||||
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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
*/
|
||||
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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
'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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
// 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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
}
|
||||
|
||||
$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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
// 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 @@ class FieldInfoTest extends FieldUnitTestBase {
|
|||
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.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -44,13 +44,14 @@ class FieldInstanceCrudTest extends FieldUnitTestBase {
|
|||
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 @@ class FieldInstanceCrudTest extends FieldUnitTestBase {
|
|||
$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 @@ class FieldInstanceCrudTest extends FieldUnitTestBase {
|
|||
$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.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ abstract class FieldUnitTestBase extends DrupalUnitTestBase {
|
|||
*
|
||||
* @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 @@ abstract class FieldUnitTestBase extends DrupalUnitTestBase {
|
|||
$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(
|
||||
|
|
@ -91,6 +96,22 @@ abstract class FieldUnitTestBase extends DrupalUnitTestBase {
|
|||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves and reloads an entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to save.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The entity, freshly reloaded from storage.
|
||||
*/
|
||||
protected function entitySaveReload(EntityInterface $entity) {
|
||||
$entity->save();
|
||||
$controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
|
||||
$controller->resetCache();
|
||||
return $controller->load($entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random values for a field_test field.
|
||||
*
|
||||
|
|
@ -125,9 +146,13 @@ abstract class FieldUnitTestBase extends DrupalUnitTestBase {
|
|||
* (Optional) the name of the column to check.
|
||||
*/
|
||||
function assertFieldValues(EntityInterface $entity, $field_name, $langcode, $expected_values, $column = 'value') {
|
||||
$e = clone $entity;
|
||||
field_attach_load('entity_test', array($e->id() => $e));
|
||||
$values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
|
||||
// Re-load the entity to make sure we have the latest changes.
|
||||
entity_get_controller($entity->entityType())->resetCache(array($entity->id()));
|
||||
$e = entity_load($entity->entityType(), $entity->id());
|
||||
$field = $values = $e->getTranslation($langcode)->$field_name;
|
||||
// Filter out empty values so that they don't mess with the assertions.
|
||||
$field->filterEmptyValues();
|
||||
$values = $field->getValue();
|
||||
$this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
|
||||
foreach ($expected_values as $key => $value) {
|
||||
$this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
|
||||
|
|
|
|||
|
|
@ -61,9 +61,23 @@ class FormTest extends FieldTestBase {
|
|||
$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 @@ class FormTest extends FieldTestBase {
|
|||
|
||||
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 @@ class FormTest extends FieldTestBase {
|
|||
*/
|
||||
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 @@ class FormTest extends FieldTestBase {
|
|||
|
||||
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 @@ class FormTest extends FieldTestBase {
|
|||
|
||||
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 @@ class FormTest extends FieldTestBase {
|
|||
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 @@ class FormTest extends FieldTestBase {
|
|||
|
||||
// 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 @@ class FormTest extends FieldTestBase {
|
|||
|
||||
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 @@ class FormTest extends FieldTestBase {
|
|||
// 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 @@ class FormTest extends FieldTestBase {
|
|||
* 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 @@ class FormTest extends FieldTestBase {
|
|||
|
||||
// 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 @@ class FormTest extends FieldTestBase {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -33,8 +33,17 @@ class NestedFormTest extends FieldTestBase {
|
|||
$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',
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ class ShapeItemTest extends FieldUnitTestBase {
|
|||
|
||||
// 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();
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ class TestItemTest extends FieldUnitTestBase {
|
|||
|
||||
// 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();
|
||||
|
|
|
|||
|
|
@ -85,13 +85,14 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
$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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
// 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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
}
|
||||
|
||||
// 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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
// 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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
|
||||
// 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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
// 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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
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 @@ class TranslationTest extends FieldUnitTestBase {
|
|||
// 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.
|
||||
|
|
|
|||
|
|
@ -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 @@ class TranslationWebTest extends FieldTestBase {
|
|||
|
||||
$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,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
use Drupal\Core\Entity\DatabaseStorageController;
|
||||
|
||||
/**
|
||||
* Test the produced views_data.
|
||||
|
|
@ -28,9 +29,9 @@ class ApiDataTest extends FieldTestBase {
|
|||
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 @@ class ApiDataTest extends FieldTestBase {
|
|||
);
|
||||
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 @@ class ApiDataTest extends FieldTestBase {
|
|||
// 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 @@ class ApiDataTest extends FieldTestBase {
|
|||
'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 @@ class ApiDataTest extends FieldTestBase {
|
|||
'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']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,11 @@ abstract class FieldTestBase extends ViewTestBase {
|
|||
$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 @@ abstract class FieldTestBase extends ViewTestBase {
|
|||
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',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -47,10 +47,20 @@ class HandlerFieldFieldTest extends FieldTestBase {
|
|||
$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 @@ class HandlerFieldFieldTest extends FieldTestBase {
|
|||
$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'];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ class reEnableModuleFieldTest extends WebTestBase {
|
|||
|
||||
// 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();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use Drupal\field\FieldInterface;
|
|||
|
||||
require_once __DIR__ . '/field_test.entity.inc';
|
||||
require_once __DIR__ . '/field_test.field.inc';
|
||||
require_once __DIR__ . '/field_test.storage.inc';
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue