From e7d47587e0c026dcc38101469de85ca771b62fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Tue, 10 Oct 2023 18:36:59 +0200 Subject: [PATCH 1/3] Add magic get/setters for non-base fields (#948) --- src/Entity/FieldableEdgeEntityBase.php | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/Entity/FieldableEdgeEntityBase.php b/src/Entity/FieldableEdgeEntityBase.php index 431391d0..eb780121 100644 --- a/src/Entity/FieldableEdgeEntityBase.php +++ b/src/Entity/FieldableEdgeEntityBase.php @@ -26,6 +26,8 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\TypedData\TypedDataInterface; /** * Base field support for Apigee Entities without making them content entities. @@ -536,4 +538,128 @@ public function getIterator(): \Traversable { return new \ArrayIterator($this->getFields()); } + /** + * 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 to follow core's suit, regardless that + * Apigee Edge entities are NOT translatable. + * + * @todo: Add methods for getting original fields and for determining + * changes. + * @todo: Provide a better way for defining default values. + * + * @var array + */ + private $values = []; + + /** + * Implements the magic method for getting object properties. + * + * @see https://www.drupal.org/project/drupal/issues/3281720 + * @see \Drupal\Core\Entity\ContentEntityBase + */ + public function &__get($name) { + // If this is an entity field, handle it accordingly. We first check whether + // a field object has been already created. If not, we create one. + if (isset($this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED])) { + return $this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED]; + } + // Inline getFieldDefinition() to speed things up. + if (!isset($this->fieldDefinitions)) { + $this->getFieldDefinitions(); + } + // Apigee base fields are special, so they need special treatment. + if (isset($this->fieldDefinitions[$name]) && !($this->fieldDefinitions[$name] instanceof BaseFieldDefinition)) { + $return = $this->getField($name); + return $return; + } + // Else directly read/write plain values. That way, non-field entity + // properties can always be accessed directly. + if (!isset($this->values[$name])) { + $this->values[$name] = NULL; + } + return $this->values[$name]; + } + + /** + * Implements the magic method for setting object properties. + * + * Uses default language always. + */ + public function __set($name, $value) { + // Inline getFieldDefinition() to speed things up. + if (!isset($this->fieldDefinitions)) { + $this->getFieldDefinitions(); + } + // Handle Field API fields. + if (isset($this->fieldDefinitions[$name])) { + // Support setting values via property objects. + if ($value instanceof TypedDataInterface) { + $value = $value->getValue(); + } + // If a FieldItemList object already exists, set its value. + if (isset($this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED])) { + $this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED]->setValue($value); + } + // If not, create one. + else { + $this->getField($name)->setValue($value); + } + } + // Directly write non-field values. + else { + $this->values[$name] = $value; + } + } + + /** + * Implements the magic method for isset(). + */ + public function __isset($name) { + // "Official" Field API fields are always set. For non-field properties, + // check the internal values. + return $this->hasField($name) ? TRUE : isset($this->values[$name]); + } + + /** + * Implements the magic method for unset(). + */ + public function __unset($name) { + // Unsetting a field means emptying it. + if ($this->hasField($name)) { + $this->get($name)->setValue([]); + } + // For non-field properties, unset the internal value. + else { + unset($this->values[$name]); + } + } + + /** + * Gets a non-translatable field. + * + * @return \Drupal\Core\Field\FieldItemListInterface + */ + private function getField($name) { + // Populate $this->fields to speed-up further look-ups and to keep track of + // fields objects, possibly holding changes to field values. + if (!isset($this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED])) { + $definition = $this->getFieldDefinition($name); + if (!$definition) { + throw new \InvalidArgumentException("Field $name is unknown."); + } + // Non-translatable fields are always stored with + // LanguageInterface::LANGCODE_DEFAULT as key. + $value = NULL; + if (isset($this->values[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED])) { + $value = $this->values[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED]; + } + $field = \Drupal::service('plugin.manager.field.field_type')->createFieldItemList($this->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED), $name, $value); + $field->setLangcode(LanguageInterface::LANGCODE_NOT_SPECIFIED); + $this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED] = $field; + } + return $this->fields[$name][LanguageInterface::LANGCODE_NOT_SPECIFIED]; + } + } From 001dcd23236055f9714fb760a0a4483d394bcf45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Wed, 11 Oct 2023 19:44:50 +0200 Subject: [PATCH 2/3] Distinguish apigee baseFields during __get (#948) --- src/Entity/FieldableEdgeEntityBase.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Entity/FieldableEdgeEntityBase.php b/src/Entity/FieldableEdgeEntityBase.php index eb780121..0a0a391a 100644 --- a/src/Entity/FieldableEdgeEntityBase.php +++ b/src/Entity/FieldableEdgeEntityBase.php @@ -569,8 +569,7 @@ public function &__get($name) { if (!isset($this->fieldDefinitions)) { $this->getFieldDefinitions(); } - // Apigee base fields are special, so they need special treatment. - if (isset($this->fieldDefinitions[$name]) && !($this->fieldDefinitions[$name] instanceof BaseFieldDefinition)) { + if (isset($this->fieldDefinitions[$name])) { $return = $this->getField($name); return $return; } @@ -607,6 +606,11 @@ public function __set($name, $value) { $this->getField($name)->setValue($value); } } + // The translations array is unset when cloning the entity object, we just + // need to restore it. + elseif ($name == 'translations') { + $this->translations = $value; + } // Directly write non-field values. else { $this->values[$name] = $value; @@ -636,6 +640,17 @@ public function __unset($name) { } } + /** + * An array of entity translation metadata. + * + * An associative array keyed by translation language code. Every value is an + * array containing the translation status and the translation object, if it has + * already been instantiated. + * + * @var array + */ + private $translations = []; + /** * Gets a non-translatable field. * From 7fe31b79878dcbe5720cc40e0cb8841ff5043110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Thu, 12 Oct 2023 18:51:26 +0200 Subject: [PATCH 3/3] Distinguish apigee baseFields during __get (#948) --- src/Entity/FieldableEdgeEntityBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/FieldableEdgeEntityBase.php b/src/Entity/FieldableEdgeEntityBase.php index 0a0a391a..e1b78e23 100644 --- a/src/Entity/FieldableEdgeEntityBase.php +++ b/src/Entity/FieldableEdgeEntityBase.php @@ -569,7 +569,7 @@ public function &__get($name) { if (!isset($this->fieldDefinitions)) { $this->getFieldDefinitions(); } - if (isset($this->fieldDefinitions[$name])) { + if (isset($this->fieldDefinitions[$name]) && !($this->fieldDefinitions[$name] instanceof BaseFieldDefinition)) { $return = $this->getField($name); return $return; }