Contains \Drupal\Core\Entity\EntityBCDecorator.
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityBCDecorator.
*/
namespace Drupal\Core\Entity;
use Drupal\Core\Language\Language;
use IteratorAggregate;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides backwards compatible (BC) access to entity fields.
*
* Allows using entities converted to the new Entity Field API with the previous
* way of accessing fields or properties. For example, via the backwards
* compatible (BC) decorator you can do:
* @code
* $node->title = $value;
* $node->body[LANGUAGE_NONE][0]['value'] = $value;
* @endcode
* Without the BC decorator the same assignment would have to look like this:
* @code
* $node->title->value = $value;
* $node->body->value = $value;
* @endcode
* Without the BC decorator the language always default to the entity language,
* whereas a specific translation can be access via the getTranslation() method.
*
* The BC decorator should be only used during conversion to the new entity
* field API, such that existing code can be converted iteratively. Any new code
* should directly use the new entity field API and avoid using the
* EntityBCDecorator, if possible.
*
* @todo: Remove once everything is converted to use the new entity field API.
*/
class EntityBCDecorator implements IteratorAggregate, EntityInterface {
/**
* The EntityInterface object being decorated.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $decorated;
/**
* Local cache for field definitions.
*
* @var array
*/
protected $definitions;
/**
* Constructs a Drupal\Core\Entity\EntityCompatibilityDecorator object.
*
* @param \Drupal\Core\Entity\EntityInterface $decorated
* The decorated entity.
* @param array &$definitions
* An array of field definitions.
*/
function __construct(EntityNG $decorated, array &$definitions) {
$this->decorated = $decorated;
$this->definitions =& $definitions;
}
/**
* Overrides Entity::getNGEntity().
*/
public function getNGEntity() {
return $this->decorated;
}
/**
* Overrides Entity::getBCEntity().
*/
public function getBCEntity() {
return $this;
}
/**
* Implements the magic method for getting object properties.
*
* Directly accesses the plain field values, as done in Drupal 7.
*/
public function &__get($name) {
// Directly return the original property.
if ($name == 'original') {
return $this->decorated->values[$name];
}
// We access the protected 'values' and 'fields' properties of the decorated
// entity via the magic getter - which returns them by reference for us. We
// do so, as providing references to these arrays would make $entity->values
// and $entity->fields reference themselves, which is problematic during
// __clone() (this is something we cannot work-a-round easily as an unset()
// on the variable is problematic in conjunction with the magic
// getter/setter).
if (!empty($this->decorated->fields[$name])) {
// Any field value set via the new Entity Field API will be stored inside
// the field objects managed by the entity, thus we need to ensure
// $this->decorated->values reflects the latest values first.
foreach ($this->decorated->fields[$name] as $langcode => $field) {
// Only set if it's not empty, otherwise there can be ghost values.
if (!$field
->isEmpty()) {
$this->decorated->values[$name][$langcode] = $field
->getValue();
}
}
// The returned values might be changed by reference, so we need to remove
// the field object to avoid the field object and the value getting out of
// sync. That way, the next field object instantiated by EntityNG will
// receive the possibly updated value.
unset($this->decorated->fields[$name]);
}
// When accessing values for entity properties that have been converted to
// an entity field, provide direct access to the plain value. This makes it
// possible to use the BC-decorator with properties; e.g., $node->title.
if (isset($this->definitions[$name]) && empty($this->definitions[$name]['configurable'])) {
if (!isset($this->decorated->values[$name][Language::LANGCODE_DEFAULT])) {
$this->decorated->values[$name][Language::LANGCODE_DEFAULT][0]['value'] = NULL;
}
if (is_array($this->decorated->values[$name][Language::LANGCODE_DEFAULT])) {
// This will work with all defined properties that have a single value.
// We need to ensure the key doesn't matter. Mostly it's 'value' but
// e.g. EntityReferenceItem uses target_id.
if (isset($this->decorated->values[$name][Language::LANGCODE_DEFAULT][0]) && count($this->decorated->values[$name][Language::LANGCODE_DEFAULT][0]) == 1) {
return $this->decorated->values[$name][Language::LANGCODE_DEFAULT][0][key($this->decorated->values[$name][Language::LANGCODE_DEFAULT][0])];
}
}
return $this->decorated->values[$name][Language::LANGCODE_DEFAULT];
}
else {
// Allow accessing field values in an entity default language other than
// Language::LANGCODE_DEFAULT by mapping the values to
// Language::LANGCODE_DEFAULT. This is necessary as EntityNG always keys
// default language values with Language::LANGCODE_DEFAULT while field API
// expects them to be keyed by langcode.
$langcode = $this->decorated
->language()->langcode;
if ($langcode != Language::LANGCODE_DEFAULT && isset($this->decorated->values[$name]) && is_array($this->decorated->values[$name])) {
if (isset($this->decorated->values[$name][Language::LANGCODE_DEFAULT]) && !isset($this->decorated->values[$name][$langcode])) {
$this->decorated->values[$name][$langcode] =& $this->decorated->values[$name][Language::LANGCODE_DEFAULT];
}
}
if (!isset($this->decorated->values[$name])) {
$this->decorated->values[$name] = NULL;
}
return $this->decorated->values[$name];
}
}
/**
* Implements the magic method for setting object properties.
*
* Directly writes to the plain field values, as done by Drupal 7.
*/
public function __set($name, $value) {
$defined = isset($this->definitions[$name]);
// When updating values for entity properties that have been converted to
// an entity field, directly write to the plain value. This makes it
// possible to use the BC-decorator with properties; e.g., $node->title.
if ($defined && empty($this->definitions[$name]['configurable'])) {
$this->decorated->values[$name][Language::LANGCODE_DEFAULT] = $value;
}
else {
if ($defined && is_array($value)) {
// If field API sets a value with a langcode in entity language, move it
// to Language::LANGCODE_DEFAULT.
// This is necessary as EntityNG always keys default language values
// with Language::LANGCODE_DEFAULT while field API expects them to be
// keyed by langcode.
foreach ($value as $langcode => $data) {
if ($langcode != Language::LANGCODE_DEFAULT && $langcode == $this->decorated
->language()->langcode) {
$value[Language::LANGCODE_DEFAULT] = $data;
unset($value[$langcode]);
}
}
}
$this->decorated->values[$name] = $value;
}
// Remove the field object to avoid the field object and the value getting
// out of sync. That way, the next field object instantiated by EntityNG
// will hold the updated value.
unset($this->decorated->fields[$name]);
}
/**
* Implements the magic method for isset().
*/
public function __isset($name) {
$value = $this
->__get($name);
return isset($value);
}
/**
* Implements the magic method for unset().
*/
public function __unset($name) {
// Set the value to NULL.
$value =& $this
->__get($name);
$value = NULL;
}
/**
* Implements the magic method for clone().
*/
function __clone() {
$this->decorated = clone $this->decorated;
}
/**
* Forwards the call to the decorated entity.
*/
public function uriRelationships() {
return $this->decorated
->uriRelationships();
}
/**
* Forwards the call to the decorated entity.
*/
public function access($operation = 'view', AccountInterface $account = NULL) {
return $this->decorated
->access($operation, $account);
}
/**
* Forwards the call to the decorated entity.
*/
public function get($property_name) {
// Ensure this works with not yet defined fields.
if (!isset($this->definitions[$property_name])) {
return $this
->__get($property_name);
}
return $this->decorated
->get($property_name);
}
/**
* Forwards the call to the decorated entity.
*/
public function set($property_name, $value, $notify = TRUE) {
// Ensure this works with not yet defined fields.
if (!isset($this->definitions[$property_name])) {
return $this
->__set($property_name, $value);
}
return $this->decorated
->set($property_name, $value);
}
/**
* Forwards the call to the decorated entity.
*/
public function getProperties($include_computed = FALSE) {
return $this->decorated
->getProperties($include_computed);
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyValues() {
return $this->decorated
->getPropertyValues();
}
/**
* Forwards the call to the decorated entity.
*/
public function setPropertyValues($values) {
return $this->decorated
->setPropertyValues($values);
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyDefinition($name) {
return $this->decorated
->getPropertyDefinition($name);
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyDefinitions() {
return $this->decorated
->getPropertyDefinitions();
}
/**
* Forwards the call to the decorated entity.
*/
public function isEmpty() {
return $this->decorated
->isEmpty();
}
/**
* Forwards the call to the decorated entity.
*/
public function getIterator() {
return $this->decorated
->getIterator();
}
/**
* Forwards the call to the decorated entity.
*/
public function id() {
return $this->decorated
->id();
}
/**
* Forwards the call to the decorated entity.
*/
public function uuid() {
return $this->decorated
->uuid();
}
/**
* Forwards the call to the decorated entity.
*/
public function isNew() {
return $this->decorated
->isNew();
}
/**
* Forwards the call to the decorated entity.
*/
public function isNewRevision() {
return $this->decorated
->isNewRevision();
}
/**
* Forwards the call to the decorated entity.
*/
public function setNewRevision($value = TRUE) {
return $this->decorated
->setNewRevision($value);
}
/**
* Forwards the call to the decorated entity.
*/
public function enforceIsNew($value = TRUE) {
return $this->decorated
->enforceIsNew($value);
}
/**
* Forwards the call to the decorated entity.
*/
public function entityType() {
return $this->decorated
->entityType();
}
/**
* Forwards the call to the decorated entity.
*/
public function bundle() {
return $this->decorated
->bundle();
}
/**
* Forwards the call to the decorated entity.
*/
public function label($langcode = NULL) {
return $this->decorated
->label($langcode);
}
/**
* Forwards the call to the decorated entity.
*/
public function uri($rel = 'canonical') {
return $this->decorated
->uri($rel);
}
/**
* Forwards the call to the decorated entity.
*/
public function save() {
return $this->decorated
->save();
}
/**
* Forwards the call to the decorated entity.
*/
public function delete() {
return $this->decorated
->delete();
}
/**
* Forwards the call to the decorated entity.
*/
public function createDuplicate() {
return $this->decorated
->createDuplicate();
}
/**
* Forwards the call to the decorated entity.
*/
public function entityInfo() {
return $this->decorated
->entityInfo();
}
/**
* Forwards the call to the decorated entity.
*/
public function getRevisionId() {
return $this->decorated
->getRevisionId();
}
/**
* Forwards the call to the decorated entity.
*/
public function isDefaultRevision($new_value = NULL) {
return $this->decorated
->isDefaultRevision($new_value);
}
/**
* Forwards the call to the decorated entity.
*/
public function language() {
return $this->decorated
->language();
}
/**
* Forwards the call to the decorated entity.
*/
public function getTranslationLanguages($include_default = TRUE) {
return $this->decorated
->getTranslationLanguages($include_default);
}
/**
* Forwards the call to the decorated entity.
*/
public function getTranslation($langcode, $strict = TRUE) {
return $this->decorated
->getTranslation($langcode, $strict);
}
/**
* Forwards the call to the decorated entity.
*/
public function getType() {
return $this->decorated
->getType();
}
/**
* Forwards the call to the decorated entity.
*/
public function getDefinition() {
return $this->decorated
->getDefinition();
}
/**
* Forwards the call to the decorated entity.
*/
public function getValue() {
return $this->decorated
->getValue();
}
/**
* Forwards the call to the decorated entity.
*/
public function setValue($value, $notify = TRUE) {
return $this->decorated
->setValue($value, $notify);
}
/**
* Forwards the call to the decorated entity.
*/
public function getString() {
return $this->decorated
->getString();
}
/**
* Forwards the call to the decorated entity.
*/
public function getConstraints() {
return $this->decorated
->getConstraints();
}
/**
* Forwards the call to the decorated entity.
*/
public function validate() {
return $this->decorated
->validate();
}
/**
* Forwards the call to the decorated entity.
*/
public function getName() {
return $this->decorated
->getName();
}
/**
* Forwards the call to the decorated entity.
*/
public function getRoot() {
return $this->decorated
->getRoot();
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyPath() {
return $this->decorated
->getPropertyPath();
}
/**
* Forwards the call to the decorated entity.
*/
public function getParent() {
return $this->decorated
->getParent();
}
/**
* Forwards the call to the decorated entity.
*/
public function setContext($name = NULL, TypedDataInterface $parent = NULL) {
$this->decorated
->setContext($name, $parent);
}
/**
* Forwards the call to the decorated entity.
*/
public function getExportProperties() {
$this->decorated
->getExportProperties();
}
/**
* Forwards the call to the decorated entity.
*/
public function onChange($property_name) {
$this->decorated
->onChange($property_name);
}
/**
* Forwards the call to the decorated entity.
*/
public function isTranslatable() {
return $this->decorated
->isTranslatable();
}
}
Name | Description |
---|---|
EntityBCDecorator | Provides backwards compatible (BC) access to entity fields. |