Implements Entity Field API specific enhancements to the Entity class.
An entity implements the ComplexDataInterface, thus is complex data containing fields as its data properties. The entity fields have to implement the \Drupal\Core\Entity\Field\FieldInterface.
@todo: Once all entity types have been converted, merge improvements into the Entity class and overhaul the EntityInterface.
Expanded class hierarchy of EntityNG
class EntityNG extends Entity {
/**
* The plain data values of the contained fields.
*
* This always holds the original, unchanged values of the entity. The values
* are keyed by language code, whereas LANGUAGE_NOT_SPECIFIED is used for
* values in default language.
*
* @todo: Add methods for getting original fields and for determining
* changes.
* @todo: Provide a better way for defining default values.
*
* @var array
*/
protected $values = array(
'langcode' => array(
LANGUAGE_DEFAULT => array(
0 => array(
'value' => LANGUAGE_NOT_SPECIFIED,
),
),
),
);
/**
* The array of fields, each being an instance of FieldInterface.
*
* @var array
*/
protected $fields = array();
/**
* Whether the entity is in pre-Entity Field API compatibility mode.
*
* If set to TRUE, field values are written directly to $this->values, thus
* must be plain property values keyed by language code. This must be enabled
* when calling legacy field API attachers.
*
* @var bool
*/
protected $compatibilityMode = FALSE;
/**
* Overrides Entity::id().
*/
public function id() {
return $this
->get('id')->value;
}
/**
* Overrides Entity::uuid().
*/
public function uuid() {
return $this
->get('uuid')->value;
}
/**
* Implements ComplexDataInterface::get().
*/
public function get($property_name) {
// Values in default language are always stored using the LANGUAGE_DEFAULT
// constant.
if (!isset($this->fields[$property_name][LANGUAGE_DEFAULT])) {
return $this
->getTranslatedField($property_name, LANGUAGE_DEFAULT);
}
return $this->fields[$property_name][LANGUAGE_DEFAULT];
}
/**
* Gets a translated field.
*
* @return \Drupal\Core\Entity\Field\FieldInterface
*/
protected function getTranslatedField($property_name, $langcode) {
// Populate $this->properties to fasten further lookups and to keep track of
// property objects, possibly holding changes to properties.
if (!isset($this->fields[$property_name][$langcode])) {
$definition = $this
->getPropertyDefinition($property_name);
if (!$definition) {
throw new InvalidArgumentException('Field ' . check_plain($property_name) . ' is unknown.');
}
// Non-translatable properties always use default language.
if ($langcode != LANGUAGE_DEFAULT && empty($definition['translatable'])) {
$this->fields[$property_name][$langcode] = $this
->getTranslatedField($property_name, LANGUAGE_DEFAULT);
}
else {
$value = isset($this->values[$property_name][$langcode]) ? $this->values[$property_name][$langcode] : NULL;
$context = array(
'parent' => $this,
'name' => $property_name,
);
$this->fields[$property_name][$langcode] = typed_data()
->create($definition, $value, $context);
}
}
return $this->fields[$property_name][$langcode];
}
/**
* Implements ComplexDataInterface::set().
*/
public function set($property_name, $value) {
$this
->get($property_name)
->setValue($value);
}
/**
* Implements ComplexDataInterface::getProperties().
*/
public function getProperties($include_computed = FALSE) {
$properties = array();
foreach ($this
->getPropertyDefinitions() as $name => $definition) {
if ($include_computed || empty($definition['computed'])) {
$properties[$name] = $this
->get($name);
}
}
return $properties;
}
/**
* Implements IteratorAggregate::getIterator().
*/
public function getIterator() {
return new ArrayIterator($this
->getProperties());
}
/**
* Implements ComplexDataInterface::getPropertyDefinition().
*/
public function getPropertyDefinition($name) {
// First try getting property definitions which apply to all entities of
// this type. Then if this fails add in definitions of optional properties
// as well. That way we can use property definitions of base properties
// when determining the optional properties of an entity.
$definitions = entity_get_controller($this->entityType)
->getFieldDefinitions(array());
if (isset($definitions[$name])) {
return $definitions[$name];
}
// Add in optional properties if any.
if ($definitions = $this
->getPropertyDefinitions()) {
return isset($definitions[$name]) ? $definitions[$name] : FALSE;
}
}
/**
* Implements ComplexDataInterface::getPropertyDefinitions().
*/
public function getPropertyDefinitions() {
return entity_get_controller($this->entityType)
->getFieldDefinitions(array(
'entity type' => $this->entityType,
'bundle' => $this
->bundle(),
));
}
/**
* Implements ComplexDataInterface::getPropertyValues().
*/
public function getPropertyValues() {
$values = array();
foreach ($this
->getProperties() as $name => $property) {
$values[$name] = $property
->getValue();
}
return $values;
}
/**
* Implements ComplexDataInterface::setPropertyValues().
*/
public function setPropertyValues($values) {
foreach ($values as $name => $value) {
$this
->get($name)
->setValue($value);
}
}
/**
* Implements ComplexDataInterface::isEmpty().
*/
public function isEmpty() {
if (!$this
->isNew()) {
return FALSE;
}
foreach ($this
->getProperties() as $property) {
if ($property
->getValue() !== NULL) {
return FALSE;
}
}
return TRUE;
}
/**
* Implements TranslatableInterface::language().
*/
public function language() {
return $this
->get('langcode')->language;
}
/**
* Implements TranslatableInterface::getTranslation().
*
* @return \Drupal\Core\Entity\Field\Type\EntityTranslation
*/
public function getTranslation($langcode, $strict = TRUE) {
// If the default language is LANGUAGE_NOT_SPECIFIED, the entity is not
// translatable, so we use LANGUAGE_DEFAULT.
if ($langcode == LANGUAGE_DEFAULT || in_array($this
->language()->langcode, array(
LANGUAGE_NOT_SPECIFIED,
$langcode,
))) {
// No translation needed, return the entity.
return $this;
}
// Check whether the language code is valid, thus is of an available
// language.
$languages = language_list(LANGUAGE_ALL);
if (!isset($languages[$langcode])) {
throw new InvalidArgumentException("Unable to get translation for the invalid language '{$langcode}'.");
}
$fields = array();
foreach ($this
->getPropertyDefinitions() as $name => $definition) {
// Load only translatable properties in strict mode.
if (!empty($definition['translatable']) || !$strict) {
$fields[$name] = $this
->getTranslatedField($name, $langcode);
}
}
$translation_definition = array(
'type' => 'entity_translation',
'constraints' => array(
'entity type' => $this
->entityType(),
'bundle' => $this
->bundle(),
),
);
$translation = typed_data()
->create($translation_definition, $fields, array(
'parent' => $this,
'name' => $langcode,
));
$translation
->setStrictMode($strict);
return $translation;
}
/**
* Implements TranslatableInterface::getTranslationLanguages().
*/
public function getTranslationLanguages($include_default = TRUE) {
$translations = array();
// Build an array with the translation langcodes set as keys.
foreach ($this
->getProperties() as $name => $property) {
if (isset($this->values[$name])) {
$translations += $this->values[$name];
}
$translations += $this->fields[$name];
}
unset($translations[LANGUAGE_DEFAULT]);
if ($include_default) {
$translations[$this
->language()->langcode] = TRUE;
}
// Now get languages based upon translation langcodes. Empty languages must
// be filtered out as they concern empty/unset properties.
$languages = array_intersect_key(language_list(LANGUAGE_ALL), array_filter($translations));
return $languages;
}
/**
* Enables or disable the compatibility mode.
*
* @param bool $enabled
* Whether to enable the mode.
*
* @see EntityNG::compatibilityMode
*/
public function setCompatibilityMode($enabled) {
$this->compatibilityMode = (bool) $enabled;
if ($enabled) {
$this
->updateOriginalValues();
$this->fields = array();
}
}
/**
* Returns whether the compatibility mode is active.
*/
public function getCompatibilityMode() {
return $this->compatibilityMode;
}
/**
* Updates the original values with the interim changes.
*
* Note: This should be called by the storage controller during a save
* operation.
*/
public function updateOriginalValues() {
foreach ($this->fields as $name => $properties) {
foreach ($properties as $langcode => $property) {
$this->values[$name][$langcode] = $property
->getValue();
}
}
}
/**
* Magic getter: Gets the property in default language.
*
* For compatibility mode to work this must return a reference.
*/
public function &__get($name) {
if ($this->compatibilityMode) {
if (!isset($this->values[$name])) {
$this->values[$name] = NULL;
}
return $this->values[$name];
}
if (isset($this->fields[$name][LANGUAGE_DEFAULT])) {
return $this->fields[$name][LANGUAGE_DEFAULT];
}
if ($this
->getPropertyDefinition($name)) {
$return = $this
->get($name);
return $return;
}
if (!isset($this->{$name})) {
$this->{$name} = NULL;
}
return $this->{$name};
}
/**
* Magic getter: Sets the property in default language.
*/
public function __set($name, $value) {
// Support setting values via property objects.
if ($value instanceof TypedDataInterface) {
$value = $value
->getValue();
}
if ($this->compatibilityMode) {
$this->values[$name] = $value;
}
elseif (isset($this->fields[$name][LANGUAGE_DEFAULT])) {
$this->fields[$name][LANGUAGE_DEFAULT]
->setValue($value);
}
elseif ($this
->getPropertyDefinition($name)) {
$this
->get($name)
->setValue($value);
}
else {
$this->{$name} = $value;
}
}
/**
* Magic method.
*/
public function __isset($name) {
if ($this->compatibilityMode) {
return isset($this->values[$name]);
}
elseif ($this
->getPropertyDefinition($name)) {
return (bool) count($this
->get($name));
}
}
/**
* Magic method.
*/
public function __unset($name) {
if ($this->compatibilityMode) {
unset($this->values[$name]);
}
elseif ($this
->getPropertyDefinition($name)) {
$this
->get($name)
->setValue(array());
}
}
/**
* Overrides Entity::createDuplicate().
*/
public function createDuplicate() {
$duplicate = clone $this;
$entity_info = $this
->entityInfo();
$duplicate->{$entity_info['entity_keys']['id']}->value = NULL;
// Check if the entity type supports UUIDs and generate a new one if so.
if (!empty($entity_info['entity_keys']['uuid'])) {
$uuid = new Uuid();
$duplicate->{$entity_info['entity_keys']['uuid']}->value = $uuid
->generate();
}
return $duplicate;
}
/**
* Implements a deep clone.
*/
public function __clone() {
foreach ($this->fields as $name => $properties) {
foreach ($properties as $langcode => $property) {
$this->fields[$name][$langcode] = clone $property;
if ($property instanceof ContextAwareInterface) {
$this->fields[$name][$langcode]
->setParent($this);
}
}
}
}
}
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Entity:: |
protected | property | Boolean indicating whether the entity should be forced to be new. | |
Entity:: |
protected | property | The entity type. | |
Entity:: |
protected | property | Indicates whether this is the default revision. | 1 |
Entity:: |
public | property | The language code of the entity's default language. | 4 |
Entity:: |
protected | property | Boolean indicating whether a new revision should be created on save. | |
Entity:: |
public | function |
Implements AccessibleInterface::access(). Overrides AccessibleInterface:: |
|
Entity:: |
public | function |
Implements EntityInterface::bundle(). Overrides EntityInterface:: |
4 |
Entity:: |
public | function |
Implements EntityInterface::delete(). Overrides EntityInterface:: |
|
Entity:: |
public | function |
Implements EntityInterface::enforceIsNew(). Overrides EntityInterface:: |
|
Entity:: |
public | function |
Implements EntityInterface::entityInfo(). Overrides EntityInterface:: |
|
Entity:: |
public | function |
Implements EntityInterface::entityType(). Overrides EntityInterface:: |
|
Entity:: |
public | function |
Implements Drupal\Core\Entity\EntityInterface::getRevisionId(). Overrides EntityInterface:: |
3 |
Entity:: |
public | function |
Implements Drupal\Core\Entity\EntityInterface::isDefaultRevision(). Overrides EntityInterface:: |
1 |
Entity:: |
public | function |
Implements EntityInterface::isNew(). Overrides EntityInterface:: |
1 |
Entity:: |
public | function |
Implements EntityInterface::isNewRevision(). Overrides EntityInterface:: |
|
Entity:: |
public | function |
Implements EntityInterface::label(). Overrides EntityInterface:: |
1 |
Entity:: |
public | function |
Implements EntityInterface::save(). Overrides EntityInterface:: |
3 |
Entity:: |
public | function |
Implements EntityInterface::setNewRevision(). Overrides EntityInterface:: |
|
Entity:: |
public | function | Returns the languages the entity is translated to. | |
Entity:: |
public | function |
Implements EntityInterface::uri(). Overrides EntityInterface:: |
1 |
Entity:: |
public | function | Constructs an Entity object. | 2 |
EntityNG:: |
protected | property | Whether the entity is in pre-Entity Field API compatibility mode. | |
EntityNG:: |
protected | property | The array of fields, each being an instance of FieldInterface. | |
EntityNG:: |
protected | property | The plain data values of the contained fields. | |
EntityNG:: |
public | function |
Overrides Entity::createDuplicate(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::get(). Overrides Entity:: |
|
EntityNG:: |
public | function | Returns whether the compatibility mode is active. | |
EntityNG:: |
public | function |
Implements IteratorAggregate::getIterator(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::getProperties(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::getPropertyDefinition(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::getPropertyDefinitions(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::getPropertyValues(). Overrides Entity:: |
|
EntityNG:: |
protected | function | Gets a translated field. | |
EntityNG:: |
public | function |
Implements TranslatableInterface::getTranslation(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements TranslatableInterface::getTranslationLanguages(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Overrides Entity::id(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::isEmpty(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements TranslatableInterface::language(). Overrides Entity:: |
|
EntityNG:: |
public | function |
Implements ComplexDataInterface::set(). Overrides Entity:: |
|
EntityNG:: |
public | function | Enables or disable the compatibility mode. | |
EntityNG:: |
public | function |
Implements ComplexDataInterface::setPropertyValues(). Overrides Entity:: |
|
EntityNG:: |
public | function | Updates the original values with the interim changes. | |
EntityNG:: |
public | function |
Overrides Entity::uuid(). Overrides Entity:: |
|
EntityNG:: |
public | function | Implements a deep clone. | |
EntityNG:: |
public | function | Magic getter: Gets the property in default language. | |
EntityNG:: |
public | function | Magic method. | |
EntityNG:: |
public | function | Magic getter: Sets the property in default language. | |
EntityNG:: |
public | function | Magic method. |