Issue #2114707 by Berdir, yched, amateescu, effulgentsia, fago: Allow per-bundle overrides of field definitions.

8.0.x
Alex Pott 2014-03-04 10:27:25 +00:00
parent eeefed730b
commit 7a6fb338b3
42 changed files with 462 additions and 384 deletions

View File

@ -213,9 +213,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
*/
public function getDataDefinition() {
$definition = EntityDataDefinition::create($this->getEntityTypeId());
if ($this->bundle() != $this->getEntityTypeId()) {
$definition->setBundles(array($this->bundle()));
}
return $definition;
}
@ -485,8 +483,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
*/
public function getFieldDefinitions() {
if (!isset($this->fieldDefinitions)) {
$bundle = $this->bundle != $this->entityTypeId ? $this->bundle : NULL;
$this->fieldDefinitions = \Drupal::entityManager()->getFieldDefinitions($this->entityTypeId, $bundle);
$this->fieldDefinitions = \Drupal::entityManager()->getFieldDefinitions($this->entityTypeId, $this->bundle());
}
return $this->fieldDefinitions;
}
@ -987,4 +984,11 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
return $referenced_entities;
}
/**
* {@inheritdoc}
*/
public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
return array();
}
}

View File

@ -40,7 +40,7 @@ interface ContentEntityInterface extends EntityInterface, RevisionableInterface,
public function initTranslation($langcode);
/**
* Defines the base fields of the entity type.
* Provides base field definitions for an entity type.
*
* Implementations typically use the class \Drupal\Core\Field\FieldDefinition
* for creating the field definitions; for example a 'name' field could be
@ -50,16 +50,47 @@ interface ContentEntityInterface extends EntityInterface, RevisionableInterface,
* ->setLabel(t('Name'));
* @endcode
*
* @param string $entity_type
* The entity type to return properties for. Useful when a single class is
* used for multiple, possibly dynamic entity types.
* If some elements in a field definition need to vary by bundle, use
* \Drupal\Core\Entity\ContentEntityInterface::bundleFieldDefinitions().
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition. Useful when a single class is used for multiple,
* possibly dynamic entity types.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of entity field definitions, keyed by field name.
* An array of base field definitions for the entity type, keyed by field
* name.
*
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
* @see \Drupal\Core\Entity\ContentEntityInterface::bundleFieldDefinitions()
*/
public static function baseFieldDefinitions($entity_type);
public static function baseFieldDefinitions(EntityTypeInterface $entity_type);
/**
* Provides or alters field definitions for a specific bundle.
*
* The field definitions returned here for the bundle take precedence on the
* base field definitions specified by baseFieldDefinitions() for the entity
* type.
*
* @todo Provide a better DX for field overrides.
* See https://drupal.org/node/2145115.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition. Useful when a single class is used for multiple,
* possibly dynamic entity types.
* @param string $bundle
* The bundle.
* @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
* The list of base field definitions.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of bundle field definitions, keyed by field name.
*
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
* @see \Drupal\Core\Entity\ContentEntityInterface::baseFieldDefinitions()
*/
public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions);
/**
* Returns whether the entity has a field with the given name.

View File

@ -78,13 +78,11 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
protected $languageManager;
/**
* An array of field information per entity type, i.e. containing definitions.
* Static cache of base field definitions.
*
* @var array
*
* @see hook_entity_field_info()
*/
protected $entityFieldInfo;
protected $baseFieldDefinitions;
/**
* Static cache of field definitions per bundle and entity type.
@ -296,87 +294,140 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
/**
* {@inheritdoc}
*/
public function getFieldDefinitions($entity_type_id, $bundle = NULL) {
if (!isset($this->entityFieldInfo[$entity_type_id])) {
// First, try to load from cache.
$cid = 'entity_field_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->id;
public function getBaseFieldDefinitions($entity_type_id) {
// Check the static cache.
if (!isset($this->baseFieldDefinitions[$entity_type_id])) {
// Not prepared, try to load from cache.
$cid = 'entity_base_field_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->id;
if ($cache = $this->cache->get($cid)) {
$this->entityFieldInfo[$entity_type_id] = $cache->data;
$this->baseFieldDefinitions[$entity_type_id] = $cache->data;
}
else {
// @todo: Refactor to allow for per-bundle overrides.
// See https://drupal.org/node/2114707.
// Rebuild the definitions and put it into the cache.
$this->baseFieldDefinitions[$entity_type_id] = $this->buildBaseFieldDefinitions($entity_type_id);
$this->cache->set($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
}
}
return $this->baseFieldDefinitions[$entity_type_id];
}
/**
* Builds base field definitions for an entity type.
*
* @param string $entity_type_id
* The entity type ID. Only entity types that implement
* \Drupal\Core\Entity\ContentEntityInterface are supported
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of field definitions, keyed by field name.
*
* @throws \LogicException
* Thrown if one of the entity keys is flagged as translatable.
*/
protected function buildBaseFieldDefinitions($entity_type_id) {
$entity_type = $this->getDefinition($entity_type_id);
$class = $entity_type->getClass();
$base_definitions = $class::baseFieldDefinitions($entity_type_id);
foreach ($base_definitions as &$base_definition) {
$base_definition->setTargetEntityTypeId($entity_type_id);
}
$this->entityFieldInfo[$entity_type_id] = array(
'definitions' => $base_definitions,
// Contains definitions of optional (per-bundle) fields.
'optional' => array(),
// An array keyed by bundle name containing the optional fields added
// by the bundle.
'bundle map' => array(),
);
$base_field_definitions = $class::baseFieldDefinitions($entity_type);
// Invoke hooks.
$result = $this->moduleHandler->invokeAll($entity_type_id . '_field_info');
$this->entityFieldInfo[$entity_type_id] = NestedArray::mergeDeep($this->entityFieldInfo[$entity_type_id], $result);
$result = $this->moduleHandler->invokeAll('entity_field_info', array($entity_type_id));
$this->entityFieldInfo[$entity_type_id] = NestedArray::mergeDeep($this->entityFieldInfo[$entity_type_id], $result);
// Invoke hook.
$result = $this->moduleHandler->invokeAll('entity_base_field_info', array($entity_type));
$base_field_definitions = NestedArray::mergeDeep($base_field_definitions, $result);
// Automatically set the field name for non-configurable fields.
foreach (array('definitions', 'optional') as $key) {
foreach ($this->entityFieldInfo[$entity_type_id][$key] as $field_name => &$definition) {
if ($definition instanceof FieldDefinition) {
$definition->setName($field_name);
}
foreach ($base_field_definitions as $field_name => $base_field_definition) {
if ($base_field_definition instanceof FieldDefinition) {
$base_field_definition->setName($field_name);
$base_field_definition->setTargetEntityTypeId($entity_type_id);
}
}
// Invoke alter hooks.
$hooks = array('entity_field_info', $entity_type_id . '_field_info');
$this->moduleHandler->alter($hooks, $this->entityFieldInfo[$entity_type_id], $entity_type_id);
// Invoke alter hook.
$this->moduleHandler->alter('entity_base_field_info', $base_field_definitions, $entity_type);
// Ensure all basic fields are not defined as translatable.
$keys = array_intersect_key(array_filter($entity_type->getKeys()), array_flip(array('id', 'revision', 'uuid', 'bundle')));
$untranslatable_fields = array_flip(array('langcode') + $keys);
foreach (array('definitions', 'optional') as $key) {
foreach ($this->entityFieldInfo[$entity_type_id][$key] as $field_name => &$definition) {
foreach ($base_field_definitions as $field_name => $definition) {
if (isset($untranslatable_fields[$field_name]) && $definition->isTranslatable()) {
throw new \LogicException(String::format('The @field field cannot be translatable.', array('@field' => $definition->getLabel())));
}
}
return $base_field_definitions;
}
$this->cache->set($cid, $this->entityFieldInfo[$entity_type_id], Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
}
}
if (!$bundle) {
return $this->entityFieldInfo[$entity_type_id]['definitions'];
/**
* {@inheritdoc}
*/
public function getFieldDefinitions($entity_type_id, $bundle) {
if (!isset($this->fieldDefinitions[$entity_type_id][$bundle])) {
$base_field_definitions = $this->getBaseFieldDefinitions($entity_type_id);
// Not prepared, try to load from cache.
$cid = 'entity_bundle_field_definitions:' . $entity_type_id . ':' . $bundle . ':' . $this->languageManager->getCurrentLanguage()->id;
if ($cache = $this->cache->get($cid)) {
$bundle_field_definitions = $cache->data;
}
else {
// Add in per-bundle fields.
if (!isset($this->fieldDefinitions[$entity_type_id][$bundle])) {
$this->fieldDefinitions[$entity_type_id][$bundle] = $this->entityFieldInfo[$entity_type_id]['definitions'];
if (isset($this->entityFieldInfo[$entity_type_id]['bundle map'][$bundle])) {
$this->fieldDefinitions[$entity_type_id][$bundle] += array_intersect_key($this->entityFieldInfo[$entity_type_id]['optional'], array_flip($this->entityFieldInfo[$entity_type_id]['bundle map'][$bundle]));
// Rebuild the definitions and put it into the cache.
$bundle_field_definitions = $this->buildBuildFieldDefinitions($entity_type_id, $bundle, $base_field_definitions);
$this->cache->set($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
}
// Field definitions consist of the bundle specific overrides and the
// base fields, merge them together. Use array_replace() to replace base
// fields with by bundle overrides and keep them in order, append
// additional by bundle fields.
$this->fieldDefinitions[$entity_type_id][$bundle] = array_replace($base_field_definitions, $bundle_field_definitions);
}
return $this->fieldDefinitions[$entity_type_id][$bundle];
}
/**
* Builds field definitions for a specific bundle within an entity type.
*
* @param string $entity_type_id
* The entity type ID. Only entity types that implement
* \Drupal\Core\Entity\ContentEntityInterface are supported.
* @param string $bundle
* The bundle.
* @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
* The list of base field definitions.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of bundle field definitions, keyed by field name. Does
* not include base fields.
*/
protected function buildBuildFieldDefinitions($entity_type_id, $bundle, array $base_field_definitions) {
$entity_type = $this->getDefinition($entity_type_id);
$class = $entity_type->getClass();
// Allow the entity class to override the base fields.
$bundle_field_definitions = $class::bundleFieldDefinitions($entity_type, $bundle, $base_field_definitions);
// Invoke 'per bundle' hook.
$result = $this->moduleHandler->invokeAll('entity_bundle_field_info', array($entity_type, $bundle, $base_field_definitions));
$bundle_field_definitions = NestedArray::mergeDeep($bundle_field_definitions, $result);
// Automatically set the field name for non-configurable fields.
foreach ($bundle_field_definitions as $field_name => $field_definition) {
if ($field_definition instanceof FieldDefinition) {
$field_definition->setName($field_name);
$field_definition->setTargetEntityTypeId($entity_type_id);
}
}
// Invoke 'per bundle' alter hook.
$this->moduleHandler->alter('entity_bundle_field_info', $bundle_field_definitions, $entity_type, $bundle);
return $bundle_field_definitions;
}
/**
* {@inheritdoc}
*/
public function clearCachedFieldDefinitions() {
unset($this->entityFieldInfo);
unset($this->fieldDefinitions);
$this->baseFieldDefinitions = array();
$this->fieldDefinitions = array();
Cache::deleteTags(array('entity_field_info' => TRUE));
}

View File

@ -23,23 +23,38 @@ interface EntityManagerInterface extends PluginManagerInterface {
public function getEntityTypeLabels();
/**
* Gets an array of content entity field definitions.
* Gets the base field definitions for a content entity type.
*
* If a bundle is passed, fields specific to this bundle are included.
* Only fields that are not specific to a given bundle or set of bundles are
* returned. This excludes configurable fields, as they are always attached
* to a specific bundle.
*
* @param string $entity_type_id
* The entity type to get field definitions for. Only entity types that
* implement \Drupal\Core\Entity\ContentEntityInterface are supported.
* @param string $bundle
* (optional) The entity bundle for which to get field definitions. If NULL
* is passed, no bundle-specific fields are included. Defaults to NULL.
* The entity type ID. Only entity types that implement
* \Drupal\Core\Entity\ContentEntityInterface are supported.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of entity field definitions, keyed by field name.
* The array of base field definitions for the entity type, keyed by field
* name.
*
* @see \Drupal\Core\TypedData\TypedDataManager::create()
* @throws \LogicException
* Thrown if one of the entity keys is flagged as translatable.
*/
public function getFieldDefinitions($entity_type_id, $bundle = NULL);
public function getBaseFieldDefinitions($entity_type_id);
/**
* Gets the field definitions for a specific bundle.
*
* @param string $entity_type_id
* The entity type ID. Only entity types that implement
* \Drupal\Core\Entity\ContentEntityInterface are supported.
* @param string $bundle
* The bundle.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* The array of field definitions for the bundle, keyed by field name.
*/
public function getFieldDefinitions($entity_type_id, $bundle);
/**
* Creates a new access controller instance.

View File

@ -268,7 +268,7 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle
}
$data = $query->execute();
$field_definitions = \Drupal::entityManager()->getFieldDefinitions($this->entityTypeId);
$field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($this->entityTypeId);
$translations = array();
if ($this->revisionDataTable) {
$data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityType->getRevisionDataTable()), drupal_schema_fields_sql($this->entityType->getBaseTable())));
@ -1187,7 +1187,7 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle
$entity_type_id = $field->entity_type;
$entity_manager = \Drupal::entityManager();
$entity_type = $entity_manager->getDefinition($entity_type_id);
$definitions = $entity_manager->getFieldDefinitions($entity_type_id);
$definitions = $entity_manager->getBaseFieldDefinitions($entity_type_id);
// Define the entity ID schema based on the field definitions.
$id_definition = $definitions[$entity_type->getKey('id')];

View File

@ -60,8 +60,12 @@ class EntityDataDefinition extends ComplexDataDefinitionBase implements EntityDa
// @todo: Add support for handling multiple bundles.
// See https://drupal.org/node/2169813.
$bundles = $this->getBundles();
$bundle = is_array($bundles) && count($bundles) == 1 ? reset($bundles) : NULL;
$this->propertyDefinitions = \Drupal::entityManager()->getFieldDefinitions($entity_type_id, $bundle);
if (is_array($bundles) && count($bundles) == 1) {
$this->propertyDefinitions = \Drupal::entityManager()->getFieldDefinitions($entity_type_id, reset($bundles));
}
else {
$this->propertyDefinitions = \Drupal::entityManager()->getBaseFieldDefinitions($entity_type_id);
}
}
else {
// No entity type given.

View File

@ -7,60 +7,16 @@
namespace Drupal\Core\Field;
use Drupal\field\FieldInstanceConfigInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\field\Field;
/**
* Represents a configurable entity field item list.
*/
class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListInterface {
/**
* The Field instance definition.
*
* @var \Drupal\field\FieldInstanceConfigInterface
*/
protected $instance;
/**
* {@inheritdoc}
*/
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
// Definition can be the field config or field instance.
if ($definition instanceof FieldInstanceConfigInterface) {
$this->instance = $definition;
}
}
/**
* {@inheritdoc}
*/
public function getFieldDefinition() {
// Configurable fields have the field_config entity injected as definition,
// but we want to return the more specific field instance here.
// @todo: Overhaul this once we have per-bundle field definitions injected,
// see https://drupal.org/node/2114707.
if (!isset($this->instance)) {
$entity = $this->getEntity();
$instances = Field::fieldInfo()->getBundleInstances($entity->getEntityTypeId(), $entity->bundle());
if (isset($instances[$this->getName()])) {
$this->instance = $instances[$this->getName()];
}
else {
// For base fields, fall back to return the general definition.
return parent::getFieldDefinition();
}
}
return $this->instance;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = array();
$constraints = parent::getConstraints();
// Check that the number of values doesn't exceed the field cardinality. For
// form submitted values, this can only happen with 'multiple value'
// widgets.

View File

@ -437,4 +437,13 @@ class TypedDataManager extends DefaultPluginManager {
return $constraints;
}
/**
* {@inheritdoc}
*/
public function clearCachedDefinitions() {
parent::clearCachedDefinitions();
$this->prototypes = array();
}
}

View File

@ -8,6 +8,7 @@
namespace Drupal\aggregator\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Symfony\Component\DependencyInjection\Container;
use Drupal\Core\Entity\EntityStorageControllerInterface;
@ -120,7 +121,7 @@ class Feed extends ContentEntityBase implements FeedInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['fid'] = FieldDefinition::create('integer')
->setLabel(t('Feed ID'))
->setDescription(t('The ID of the aggregator feed.'))

View File

@ -10,6 +10,7 @@ namespace Drupal\aggregator\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\aggregator\ItemInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
/**
@ -60,7 +61,7 @@ class Item extends ContentEntityBase implements ItemInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['iid'] = FieldDefinition::create('integer')
->setLabel(t('Aggregator item ID'))
->setDescription(t('The ID of the feed item.'))

View File

@ -9,6 +9,7 @@ namespace Drupal\custom_block\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\custom_block\CustomBlockInterface;
@ -163,7 +164,7 @@ class CustomBlock extends ContentEntityBase implements CustomBlockInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['id'] = FieldDefinition::create('integer')
->setLabel(t('Custom block ID'))
->setDescription(t('The custom block ID.'))

View File

@ -11,6 +11,7 @@ use Drupal\Component\Utility\Number;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\comment\CommentInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\DataDefinition;
@ -210,7 +211,7 @@ class Comment extends ContentEntityBase implements CommentInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['cid'] = FieldDefinition::create('integer')
->setLabel(t('Comment ID'))
->setDescription(t('The comment ID.'))

View File

@ -69,6 +69,7 @@ class CommentValidationTest extends EntityUnitTestBase {
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'comment_body' => $this->randomName(),
));
$violations = $comment->validate();

View File

@ -9,6 +9,7 @@ namespace Drupal\contact\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\contact\MessageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
/**
@ -139,7 +140,7 @@ class Message extends ContentEntityBase implements MessageInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['category'] = FieldDefinition::create('entity_reference')
->setLabel(t('Category ID'))
->setDescription(t('The ID of the associated category.'))

View File

@ -9,6 +9,8 @@ use Drupal\content_translation\Plugin\Derivative\ContentTranslationLocalTasks;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFormControllerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\TranslatableInterface;
@ -143,27 +145,23 @@ function content_translation_entity_bundle_info_alter(&$bundles) {
}
/**
* Implements hook_entity_field_info_alter().
* Implements hook_entity_base_field_info_alter().
*/
function content_translation_entity_field_info_alter(&$info, $entity_type) {
$translation_settings = \Drupal::config('content_translation.settings')->get($entity_type);
function content_translation_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
$translation_settings = \Drupal::config('content_translation.settings')->get($entity_type->id());
if ($translation_settings) {
// Currently field translatability is defined per-field but we may want to
// make it per-instance instead, so leaving the possibility open for further
// easier refactoring.
$fields = array();
// make it per-instance instead. In that case, we will need to implement
// hook_bundle_field_info_alter() instead.
$field_settings = array();
foreach ($translation_settings as $bundle => $settings) {
$fields += !empty($settings['content_translation']['fields']) ? $settings['content_translation']['fields'] : array();
$field_settings += !empty($settings['content_translation']['fields']) ? $settings['content_translation']['fields'] : array();
}
$keys = array('definitions', 'optional');
foreach ($fields as $name => $translatable) {
foreach ($keys as $key) {
if (isset($info[$key][$name])) {
$info[$key][$name]->setTranslatable((bool) $translatable);
break;
}
foreach ($field_settings as $name => $translatable) {
if (isset($fields[$name]) && $fields[$name] instanceof FieldDefinition) {
$fields[$name]->setTranslatable((bool) $translatable);
}
}
}

View File

@ -10,7 +10,6 @@ namespace Drupal\entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Entity\Display\EntityDisplayInterface;
/**
@ -325,20 +324,14 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
* Returns the definitions of the fields that are candidate for display.
*/
protected function getFieldDefinitions() {
// Entity displays are sometimes created for non-content entities.
// @todo Prevent this in https://drupal.org/node/2095195.
if (!\Drupal::entityManager()->getDefinition($this->targetEntityType)->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
return array();
}
if (!isset($this->fieldDefinitions)) {
// @todo Replace this with \Drupal::entityManager()->getFieldDefinition()
// when it can hand the $instance objects (and then reconsider the
// $this->fieldDefinitions static cache ?)
// https://drupal.org/node/2114707
$entity_manager = \Drupal::entityManager();
$entity_type = $entity_manager->getDefinition($this->targetEntityType);
$definitions = array();
if ($entity_type->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
$entity = _field_create_entity_from_ids((object) array('entity_type' => $this->targetEntityType, 'bundle' => $this->bundle, 'entity_id' => NULL));
foreach ($entity as $field_name => $items) {
$definitions[$field_name] = $items->getFieldDefinition();
}
}
$definitions = \Drupal::entityManager()->getFieldDefinitions($this->targetEntityType, $this->bundle);
// The display only cares about fields that specify display options.
// Discard base fields that are not rendered through formatters / widgets.

View File

@ -23,14 +23,14 @@ class EntityReferenceAutocompleteTest extends EntityUnitTestBase {
*
* @var string
*/
protected $entityType = 'entity_test_label';
protected $entityType = 'entity_test';
/**
* The bundle used in this test.
*
* @var string
*/
protected $bundle = 'entity_test_label';
protected $bundle = 'entity_test';
/**
* The name of the field used in this test.

View File

@ -124,31 +124,18 @@ function field_invoke_method($method, $target_function, EntityInterface $entity,
* @param $options
* An associative array of options, as provided to field_invoke_method(). Only
* the following keys are considered:
* - deleted
* - field_name
* - field_id
* See field_invoke_method() for details.
*
* @return
* The array of selected field definitions.
*/
function _field_invoke_get_field_definitions($entity_type, $bundle, $options) {
// @todo Replace with \Drupal::entityManager()->getFieldDefinition() after
// [#2047229] lands.
$entity = _field_create_entity_from_ids((object) array('entity_type' => $entity_type, 'bundle' => $bundle, 'entity_id' => NULL));
$field_definitions = array();
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $bundle);
if (isset($options['field_name'])) {
if ($entity->hasField($options['field_name'])) {
$field_definitions[] = $entity->get($options['field_name'])->getFieldDefinition();
$definitions = array_intersect_key($definitions, array($options['field_name'] => TRUE));
}
}
else {
foreach ($entity as $items) {
$field_definitions[] = $items->getFieldDefinition();
}
}
return $field_definitions;
return $definitions;
}
/**

View File

@ -7,8 +7,10 @@
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Template\Attribute;
use Drupal\entity\Entity\EntityViewDisplay;
use Drupal\field\Field;
/*
* Load all public Field API functions. Drupal currently has no
@ -189,29 +191,14 @@ function field_system_info_alter(&$info, $file, $type) {
}
/**
* Implements hook_entity_field_info() to define all configured fields.
* Implements hook_entity_bundle_field_info().
*/
function field_entity_field_info($entity_type) {
$property_info = array();
foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
$optional = $bundle_name != $entity_type;
// @todo: Improve hook_entity_field_info() to allow per-bundle field
// definitions, such that we can pass on field instances as field
// definitions here. See https://drupal.org/node/2114707.
foreach ($instances as $field_name => $instance) {
if ($optional) {
$property_info['optional'][$field_name] = $instance->getField();
$property_info['bundle map'][$bundle_name][] = $field_name;
function field_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
if ($entity_type->isFieldable()) {
// Configurable fields, which are always attached to a specific bundle, are
// added 'by bundle'.
return Field::fieldInfo()->getBundleInstances($entity_type->id(), $bundle);
}
else {
$property_info['definitions'][$field_name] = $instance->getField();
}
}
}
return $property_info;
}
/**

View File

@ -162,7 +162,7 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
// Create two entities.
$entity1 = entity_create('entity_test', array('id' => 1, 'type' => 'entity_test'));
$entity1->{$this->field_name}->setValue($this->_generateTestFieldValues(1));
$entity2 = entity_create('entity_test', array('id' => 2, 'type' => 'test_bundle'));
$entity2 = entity_create('entity_test', array('id' => 2, 'type' => 'entity_test'));
$entity2->{$this->field_name}->setValue($this->_generateTestFieldValues(1));
// Run buildMultiple(), and check that the entities come out as expected.

View File

@ -104,17 +104,8 @@ abstract class DisplayOverviewBase extends OverviewBase {
* The array of field definitions
*/
protected function getFieldDefinitions() {
// @todo Replace this entire implementation with
// \Drupal::entityManager()->getFieldDefinition() when it can hand the
// $instance objects - https://drupal.org/node/2114707
$entity = _field_create_entity_from_ids((object) array('entity_type' => $this->entity_type, 'bundle' => $this->bundle, 'entity_id' => NULL));
$field_definitions = array();
foreach ($entity as $field_name => $items) {
$field_definitions[$field_name] = $items->getFieldDefinition();
}
$context = $this->displayContext;
return array_filter($field_definitions, function(FieldDefinitionInterface $field_definition) use ($context) {
return array_filter($this->entityManager->getFieldDefinitions($this->entity_type, $this->bundle), function(FieldDefinitionInterface $field_definition) use ($context) {
return $field_definition->isDisplayConfigurable($context);
});
}

View File

@ -9,6 +9,7 @@ namespace Drupal\file\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\file\FileInterface;
@ -240,7 +241,7 @@ class File extends ContentEntityBase implements FileInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['fid'] = FieldDefinition::create('integer')
->setLabel(t('File ID'))
->setDescription(t('The file ID.'))

View File

@ -9,6 +9,7 @@ namespace Drupal\node\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
@ -351,7 +352,7 @@ class Node extends ContentEntityBase implements NodeInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['nid'] = FieldDefinition::create('integer')
->setLabel(t('Node ID'))
->setDescription(t('The node ID.'))
@ -378,11 +379,8 @@ class Node extends ContentEntityBase implements NodeInterface {
->setDescription(t('The node language code.'));
$fields['title'] = FieldDefinition::create('text')
// @todo Account for $node_type->title_label when per-bundle overrides are
// possible - https://drupal.org/node/2114707.
->setLabel(t('Title'))
->setDescription(t('The title of this node, always treated as non-markup plain text.'))
->setClass('\Drupal\node\NodeTitleItemList')
->setRequired(TRUE)
->setTranslatable(TRUE)
->setSettings(array(
@ -451,4 +449,17 @@ class Node extends ContentEntityBase implements NodeInterface {
return $fields;
}
/**
* {@inheritdoc}
*/
public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
$node_type = node_type_load($bundle);
$fields = array();
if (isset($node_type->title_label)) {
$fields['title'] = clone $base_field_definitions['title'];
$fields['title']->setLabel($node_type->title_label);
}
return $fields;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\node\NodeTitleItemList.
*/
namespace Drupal\node;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Field\FieldItemList;
/**
* @todo This class is a temporary hack for allowing the label of the node title
* field to vary by node type. Remove it when https://drupal.org/node/2114707
* is solved.
*/
class NodeTitleItemList extends FieldItemList {
/**
* {@inheritdoc}
*
* The typehint for $definition is a class rather than an interface, because
* there is no interface for setLabel().
*/
public function __construct(FieldDefinition $definition, $name, NodeInterface $node) {
$node_type = node_type_load($node->getType());
if (isset($node_type->title_label)) {
$definition->setLabel($node_type->title_label);
}
parent::__construct($definition, $name, $node);
}
}

View File

@ -9,6 +9,7 @@
* a special 'node test view' permission.
*/
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\node\NodeInterface;
@ -78,15 +79,15 @@ function node_access_test_permission() {
}
/**
* Implements hook_entity_field_info().
* Implements hook_entity_base_field_info().
*/
function node_access_test_entity_field_info($entity_type) {
if ($entity_type === 'node') {
$info['definitions']['private'] = FieldDefinition::create('boolean')
function node_access_test_entity_base_field_info(EntityTypeInterface $entity_type) {
if ($entity_type->id() === 'node') {
$fields['private'] = FieldDefinition::create('boolean')
->setLabel(t('Private'))
->setComputed(TRUE);
return $info;
return $fields;
}
}

View File

@ -5,6 +5,7 @@
* Enables users to rename URLs.
*/
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\ContentEntityInterface;
@ -197,14 +198,14 @@ function path_form_taxonomy_term_form_alter(&$form, $form_state) {
}
/**
* Implements hook_entity_field_info().
* Implements hook_entity_base_field_info().
*/
function path_entity_field_info($entity_type) {
if ($entity_type === 'taxonomy_term' || $entity_type === 'node') {
$info['definitions']['path'] = FieldDefinition::create('path')
function path_entity_base_field_info(EntityTypeInterface $entity_type) {
if ($entity_type->id() === 'taxonomy_term' || $entity_type->id() === 'node') {
$fields['path'] = FieldDefinition::create('path')
->setLabel(t('The path alias'))
->setComputed(TRUE);
return $info;
return $fields;
}
}

View File

@ -9,6 +9,7 @@ namespace Drupal\shortcut\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Routing\UrlMatcher;
use Drupal\Core\Url;
@ -133,7 +134,7 @@ class Shortcut extends ContentEntityBase implements ShortcutInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['id'] = FieldDefinition::create('integer')
->setLabel(t('ID'))
->setDescription(t('The ID of the shortcut.'))

View File

@ -659,62 +659,101 @@ function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDi
}
/**
* Define custom entity fields.
* Provides custom base field definitions for a content entity type.
*
* @param string $entity_type_id
* The entity type for which to define entity fields.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
*
* @return array
* An array of entity field information having the following optional entries:
* - definitions: An array of field definitions to add to all entities of this
* type, keyed by field name.
* - optional: An array of field definitions for optional entity fields, keyed
* by field name. Optional fields are fields that only exist for certain
* bundles of the entity type.
* - bundle map: An array keyed by bundle name, containing the names of
* optional fields that entities of this bundle have.
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of field definitions, keyed by field name.
*
* @see hook_entity_field_info_alter()
* @see hook_entity_base_field_info_alter()
* @see hook_entity_bundle_field_info()
* @see hook_entity_bundle_field_info_alter()
* @see \Drupal\Core\Field\FieldDefinitionInterface
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
* @see \Drupal\Core\TypedData\TypedDataManager::create()
*/
function hook_entity_field_info($entity_type_id) {
if (mymodule_uses_entity_type($entity_type_id)) {
$info = array();
$info['definitions']['mymodule_text'] = FieldDefinition::create('string')
function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
if ($entity_type->id() == 'node') {
$fields = array();
$fields['mymodule_text'] = FieldDefinition::create('string')
->setLabel(t('The text'))
->setDescription(t('A text property added by mymodule.'))
->setComputed(TRUE)
->setClass('\Drupal\mymodule\EntityComputedText');
if ($entity_type_id == 'node') {
// Add a property only to entities of the 'article' bundle.
$info['optional']['mymodule_text_more'] = FieldDefinition::create('string')
->setLabel(t('More text'))
->setComputed(TRUE)
->setClass('\Drupal\mymodule\EntityComputedMoreText');
$info['bundle map']['article'][0] = 'mymodule_text_more';
}
return $info;
return $fields;
}
}
/**
* Alter defined entity fields.
* Alters base field definitions for a content entity type.
*
* @param array $info
* The entity field info array as returned by hook_entity_field_info().
* @param string $entity_type_id
* The entity type for which entity fields are defined.
* @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
* The array of base field definitions for the entity type.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
*
* @see hook_entity_field_info()
* @see hook_entity_base_field_info()
* @see hook_entity_bundle_field_info()
* @see hook_entity_bundle_field_info_alter()
*/
function hook_entity_field_info_alter(&$info, $entity_type_id) {
if (!empty($info['definitions']['mymodule_text'])) {
function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
// Alter the mymodule_text field to use a custom class.
$info['definitions']['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
$fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
}
}
/**
* Provides field definitions for a specific bundle within an entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param string $bundle
* The bundle.
* @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
* The list of base field definitions for the entity type.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of bundle field definitions, keyed by field name.
*
* @see hook_entity_base_field_info()
* @see hook_entity_base_field_info_alter()
* @see hook_entity_bundle_field_info_alter()
* @see \Drupal\Core\Field\FieldDefinitionInterface
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
*/
function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
// Add a property only to nodes of the 'article' bundle.
if ($entity_type->id() == 'node' && $bundle == 'article') {
$fields = array();
$fields['mymodule_text_more'] = FieldDefinition::create('string')
->setLabel(t('More text'))
->setComputed(TRUE)
->setClass('\Drupal\mymodule\EntityComputedMoreText');
return $fields;
}
}
/**
* Alters bundle field definitions.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
* The array of bundle field definitions.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param string $bundle
* The bundle.
*
* @see hook_entity_base_field_info()
* @see hook_entity_base_field_info_alter()
* @see hook_entity_bundle_field_info()
*/
function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) {
// Alter the mymodule_text field to use a custom class.
$fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
}
}

View File

@ -356,8 +356,9 @@ class EntityFieldTest extends EntityUnitTestBase {
* The entity type to run the tests with.
*/
protected function checkIntrospection($entity_type) {
// Test getting metadata upfront.
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type);
// Test getting metadata upfront. The entity types used for this test have
// a default bundle that is the same as the entity type.
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
$this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.');
$this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
$this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');

View File

@ -513,16 +513,16 @@ class EntityTranslationTest extends EntityLanguageTestBase {
$entity_type = 'entity_test_mulrev';
$this->state->set('entity_test.field_definitions.translatable', array('name' => FALSE));
$this->entityManager->clearCachedFieldDefinitions();
$definitions = $this->entityManager->getFieldDefinitions($entity_type);
$definitions = $this->entityManager->getBaseFieldDefinitions($entity_type);
$this->assertFalse($definitions['name']->isTranslatable(), 'Field translatability can be disabled programmatically.');
$this->state->set('entity_test.field_definitions.translatable', array('name' => TRUE));
$this->entityManager->clearCachedFieldDefinitions();
$definitions = $this->entityManager->getFieldDefinitions($entity_type);
$definitions = $this->entityManager->getBaseFieldDefinitions($entity_type);
$this->assertTrue($definitions['name']->isTranslatable(), 'Field translatability can be enabled programmatically.');
// Check that field translatability is disabled by default.
$base_field_definitions = EntityTestMulRev::baseFieldDefinitions($entity_type);
$base_field_definitions = EntityTestMulRev::baseFieldDefinitions($this->entityManager->getDefinition($entity_type));
$this->assertTrue(!isset($base_field_definitions['id']->translatable), 'Translatability for the <em>id</em> field is not defined.');
$this->assertFalse($definitions['id']->isTranslatable(), 'Field translatability is disabled by default.');
@ -533,7 +533,7 @@ class EntityTranslationTest extends EntityLanguageTestBase {
$message = format_string('Field %field cannot be translatable.', array('%field' => $name));
try {
$definitions = $this->entityManager->getFieldDefinitions($entity_type);
$this->entityManager->getBaseFieldDefinitions($entity_type);
$this->fail($message);
}
catch (\LogicException $e) {

View File

@ -100,7 +100,7 @@ class EntityTypedDataDefinitionTest extends DrupalUnitTestBase {
$field_definitions = $entity_definition->getPropertyDefinitions();
// Comparison should ignore the internal static cache, so compare the
// serialized objects instead.
$this->assertEqual(serialize($field_definitions), serialize(\Drupal::entityManager()->getFieldDefinitions('node')));
$this->assertEqual(serialize($field_definitions), serialize(\Drupal::entityManager()->getBaseFieldDefinitions('node')));
$this->assertEqual($entity_definition->getPropertyDefinition('title')->getItemDefinition()->getDataType(), 'field_item:text');
$this->assertNull($entity_definition->getMainPropertyName());
$this->assertNull($entity_definition->getPropertyDefinition('invalid'));

View File

@ -6,6 +6,7 @@
*/
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Session\AccountInterface;
@ -73,12 +74,12 @@ function entity_test_entity_type_alter(array &$entity_types) {
}
/**
* Implements hook_entity_field_info_alter().
* Implements hook_entity_base_field_info_alter().
*/
function entity_test_entity_field_info_alter(&$info, $entity_type) {
if ($entity_type == 'entity_test_mulrev' && ($names = \Drupal::state()->get('entity_test.field_definitions.translatable'))) {
function entity_test_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
if ($entity_type->id() == 'entity_test_mulrev' && ($names = \Drupal::state()->get('entity_test.field_definitions.translatable'))) {
foreach ($names as $name => $value) {
$info['definitions'][$name]->setTranslatable($value);
$fields[$name]->setTranslatable($value);
}
}
}

View File

@ -8,6 +8,7 @@
namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Language\Language;
@ -108,7 +109,7 @@ class EntityTest extends ContentEntityBase implements EntityOwnerInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['id'] = FieldDefinition::create('integer')
->setLabel(t('ID'))
->setDescription(t('The ID of the test entity.'))

View File

@ -7,6 +7,7 @@
namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
/**
@ -41,7 +42,7 @@ class EntityTestBaseFieldDisplay extends EntityTest {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['test_no_display'] = FieldDefinition::create('text')

View File

@ -7,6 +7,7 @@
namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\entity_test\Entity\EntityTest;
@ -46,7 +47,7 @@ class EntityTestMul extends EntityTest {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['default_langcode'] = FieldDefinition::create('boolean')

View File

@ -7,6 +7,7 @@
namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\entity_test\Entity\EntityTestRev;
@ -46,7 +47,7 @@ class EntityTestMulRev extends EntityTestRev {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['revision_id'] = FieldDefinition::create('integer')

View File

@ -7,6 +7,7 @@
namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\entity_test\Entity\EntityTest;
@ -30,7 +31,8 @@ use Drupal\entity_test\Entity\EntityTest;
* "id" = "id",
* "uuid" = "uuid",
* "revision" = "revision_id",
* "bundle" = "type"
* "bundle" = "type",
* "label" = "name",
* },
* links = {
* "canonical" = "entity_test.edit_entity_test_rev",
@ -65,7 +67,7 @@ class EntityTestRev extends EntityTest {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['revision_id'] = FieldDefinition::create('integer')

View File

@ -9,6 +9,7 @@ namespace Drupal\taxonomy\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\DataDefinition;
@ -197,7 +198,7 @@ class Term extends ContentEntityBase implements TermInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['tid'] = FieldDefinition::create('integer')
->setLabel(t('Term ID'))
->setDescription(t('The term ID.'))

View File

@ -10,6 +10,7 @@ namespace Drupal\user\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\user\UserInterface;
@ -423,7 +424,7 @@ class User extends ContentEntityBase implements UserInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['uid'] = FieldDefinition::create('integer')
->setLabel(t('User ID'))
->setDescription(t('The user ID.'))

View File

@ -102,7 +102,7 @@ class Entity extends ArgumentValidatorPluginBase {
}
$bundles_title = $entity_type->getBundleLabel() ?: $this->t('Bundles');
if ($entity_type->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) {
$fields = $this->entityManager->getFieldDefinitions($entity_type_id);
$fields = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
}
$bundle_name = (empty($fields) || empty($fields[$bundle_type]['label'])) ? t('bundles') : $fields[$bundle_type]['label'];
$form['bundles'] = array(

View File

@ -451,6 +451,18 @@ class EntityManagerTest extends UnitTestCase {
$this->assertNull($this->entityManager->getAdminRouteInfo('apple', 'delicious'));
}
/**
* Tests the getBaseFieldDefinitions() method.
*
* @covers ::getBaseFieldDefinitions()
*/
public function testGetBaseFieldDefinitions() {
$field_definition = $this->setUpEntityWithFieldDefinition();
$expected = array('id' => $field_definition);
$this->assertSame($expected, $this->entityManager->getBaseFieldDefinitions('test_entity_type'));
}
/**
* Tests the getFieldDefinitions() method.
*
@ -460,84 +472,84 @@ class EntityManagerTest extends UnitTestCase {
$field_definition = $this->setUpEntityWithFieldDefinition();
$expected = array('id' => $field_definition);
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type', 'test_entity_bundle'));
}
/**
* Tests the getBaseFieldDefinitions() method with caching.
*
* @covers ::getBaseFieldDefinitions()
*/
public function testGetBaseFieldDefinitionsWithCaching() {
$field_definition = $this->setUpEntityWithFieldDefinition();
$expected = array('id' => $field_definition);
$this->cache->expects($this->at(0))
->method('get')
->with('entity_base_field_definitions:test_entity_type:en', FALSE)
->will($this->returnValue(FALSE));
$this->cache->expects($this->once())
->method('set');
$this->cache->expects($this->at(2))
->method('get')
->with('entity_base_field_definitions:test_entity_type:en', FALSE)
->will($this->returnValue((object) array('data' => $expected)));
$this->assertSame($expected, $this->entityManager->getBaseFieldDefinitions('test_entity_type'));
$this->entityManager->testClearEntityFieldInfo();
$this->assertSame($expected, $this->entityManager->getBaseFieldDefinitions('test_entity_type'));
}
/**
* Tests the getFieldDefinitions() method with caching.
*
* @covers ::getFieldDefinitions()
*/
public function testGetFieldDefinitionsWithCaching() {
$field_definition = $this->setUpEntityWithFieldDefinition();
$field_definition = $this->setUpEntityWithFieldDefinition(FALSE, 'id', 0);
$expected = array('id' => $field_definition);
// @todo Investigate why this is 0 and 2, not 0/1 or 1/2.
$this->cache->expects($this->at(0))
->method('get')
->with('entity_field_definitions:test_entity_type:en', FALSE)
->with('entity_base_field_definitions:test_entity_type:en', FALSE)
->will($this->returnValue((object) array('data' => $expected)));
$this->cache->expects($this->at(1))
->method('get')
->with('entity_bundle_field_definitions:test_entity_type:test_bundle:en', FALSE)
->will($this->returnValue(FALSE));
$this->cache->expects($this->at(2))
->method('get')
->with('entity_field_definitions:test_entity_type:en', FALSE)
->will($this->returnValue((object) array('data' => array('definitions' => $expected))));
$this->cache->expects($this->once())
->method('set');
$this->cache->expects($this->at(3))
->method('get')
->with('entity_base_field_definitions:test_entity_type:en', FALSE)
->will($this->returnValue((object) array('data' => $expected)));
$this->cache->expects($this->at(4))
->method('get')
->with('entity_bundle_field_definitions:test_entity_type:test_bundle:en', FALSE)
->will($this->returnValue((object) array('data' => $expected)));
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type', 'test_bundle'));
$this->entityManager->testClearEntityFieldInfo();
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type', 'test_bundle'));
}
/**
* Tests the getFieldDefinitions() method with bundle map.
* Tests the getBaseFieldDefinitions() method with an invalid definition.
*
* @covers ::getFieldDefinitions()
*/
public function testGetFieldDefinitionsWithBundleMap() {
$field_definition = $this->setUpEntityWithFieldDefinition(TRUE);
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->will($this->returnValue(array()));
$this->moduleHandler->expects($this->at(1))
->method('invokeAll')
->will($this->returnValue(array(
'bundle map' => array(
'test_entity_bundle' => array(
'custom_field',
),
),
'optional' => array(
'custom_field' => $field_definition,
),
)));
$expected = array('id' => $field_definition);
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type', 'test_entity_type'));
$expected['custom_field'] = $field_definition;
$this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type', 'test_entity_bundle'));
}
/**
* Tests the getFieldDefinitions() method with an invalid definition.
*
* @covers ::getFieldDefinitions()
* @covers ::getBaseFieldDefinitions()
*
* @expectedException \LogicException
*/
public function testGetFieldDefinitionsInvalidDefinition() {
public function testGetBaseFieldDefinitionsInvalidDefinition() {
$langcode_definition = $this->setUpEntityWithFieldDefinition(FALSE, 'langcode');
$langcode_definition->expects($this->once())
->method('isTranslatable')
->will($this->returnValue(TRUE));
$this->entityManager->getFieldDefinitions('test_entity_type');
$this->entityManager->getBaseFieldDefinitions('test_entity_type');
}
/**
@ -552,30 +564,33 @@ class EntityManagerTest extends UnitTestCase {
* @return \Drupal\Core\Field\FieldDefinition|\PHPUnit_Framework_MockObject_MockObject
* A field definition object.
*/
protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $field_definition_id = 'id') {
protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $field_definition_id = 'id', $base_field_definition_calls = 1) {
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity = $this->getMock('Drupal\Tests\Core\Entity\TestContentEntityInterface');
$entity_class = get_class($entity);
$entity_type->expects($this->exactly(2))
$entity_type->expects($this->any())
->method('getClass')
->will($this->returnValue($entity_class));
$entity_type->expects($this->once())
$entity_type->expects($this->any())
->method('getKeys')
->will($this->returnValue(array()));
$field_definition = $this->getMockBuilder('Drupal\Core\Field\FieldDefinition')
->disableOriginalConstructor()
->getMock();
$entity_class::staticExpects($this->once())
$entity_class::staticExpects($this->exactly($base_field_definition_calls))
->method('baseFieldDefinitions')
->will($this->returnValue(array(
$field_definition_id => $field_definition,
)));
$entity_class::staticExpects($this->any())
->method('bundleFieldDefinitions')
->will($this->returnValue(array()));
$this->moduleHandler->expects($this->once())
$this->moduleHandler->expects($this->any())
->method('alter');
if (!$custom_invoke_all) {
$this->moduleHandler->expects($this->exactly(2))
$this->moduleHandler->expects($this->any())
->method('invokeAll')
->will($this->returnValue(array()));
}
@ -804,7 +819,8 @@ class TestEntityManager extends EntityManager {
* Allows the $entityFieldInfo property to be cleared.
*/
public function testClearEntityFieldInfo() {
$this->entityFieldInfo = NULL;
$this->baseFieldDefinitions = array();
$this->fieldDefinitions = array();
}
}

View File

@ -77,7 +77,7 @@ class FieldableDatabaseStorageControllerTest extends UnitTestCase {
->with('test_entity')
->will($this->returnValue($definition));
$entity_manager->expects($this->any())
->method('getFieldDefinitions')
->method('getBaseFieldDefinitions')
->with('test_entity')
->will($this->returnValue($fields));