From 386c8eb8b701a256fd6753effb4db1529d64ebfa Mon Sep 17 00:00:00 2001
From: florenttorregrosa <florenttorregrosa@2388214.no-reply.drupal.org>
Date: Thu, 23 Mar 2017 13:14:53 +0100
Subject: [PATCH] Issue #2860886 by Grimreaper, e0ipso, dawehner, edwardaa, Wim
 Leers, beram: Special fields on entity reference

---
 src/Normalizer/EntityReferenceFieldNormalizer.php  | 27 ++++++++++++++++++----
 src/Normalizer/Relationship.php                    | 10 +++++---
 src/Normalizer/RelationshipItem.php                | 17 ++++++++++++--
 .../Value/RelationshipItemNormalizerValue.php      | 10 ++++++--
 4 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/src/Normalizer/EntityReferenceFieldNormalizer.php b/src/Normalizer/EntityReferenceFieldNormalizer.php
index 247e82c..85ed52a 100644
--- a/src/Normalizer/EntityReferenceFieldNormalizer.php
+++ b/src/Normalizer/EntityReferenceFieldNormalizer.php
@@ -85,17 +85,34 @@ class EntityReferenceFieldNormalizer extends FieldNormalizer implements Denormal
     $cardinality = $definition
       ->getFieldStorageDefinition()
       ->getCardinality();
-    $entity_list = array_filter($field->getIterator()->getArrayCopy(), function ($item) {
+    /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem[] $entity_reference_item_list */
+    $entity_reference_item_list = array_filter($field->getIterator()->getArrayCopy(), function ($item) {
       return (bool) $item->get('entity')->getValue();
     });
-    $entity_list = array_map(function ($item) {
+    $entity_list_metadata = array();
+    $entity_list = array();
+    foreach ($entity_reference_item_list as $item) {
+      // Prepare a list of additional properties stored by the field.
+      $metadata = array();
+      /** @var \Drupal\Core\TypedData\TypedDataInterface[] $properties */
+      // TODO: Add computed values?
+      // TODO: What about entity reference revisions?
+      // TODO: Add access control on those properties?
+      $properties = $item->getProperties();
+      foreach ($properties as $property_key => $property) {
+        if ($property_key != $main_property) {
+          $metadata[$property_key] = $property->getValue();
+        }
+      }
+      $entity_list_metadata[] = $metadata;
+
       // Get the referenced entity.
       $entity = $item->get('entity')->getValue();
       // And get the translation in the requested language.
-      return $this->entityRepository->getTranslationFromContext($entity);
-    }, $entity_list);
+      $entity_list[] = $this->entityRepository->getTranslationFromContext($entity);
+    }
     $entity_collection = new EntityCollection($entity_list);
-    $relationship = new Relationship($this->resourceTypeRepository, $field->getName(), $cardinality, $entity_collection, $field->getEntity(), $main_property);
+    $relationship = new Relationship($this->resourceTypeRepository, $field->getName(), $cardinality, $entity_collection, $field->getEntity(), $main_property, $entity_list_metadata);
     return $this->serializer->normalize($relationship, $format, $context);
   }
 
diff --git a/src/Normalizer/Relationship.php b/src/Normalizer/Relationship.php
index 2483b6d..9abc7cd 100644
--- a/src/Normalizer/Relationship.php
+++ b/src/Normalizer/Relationship.php
@@ -68,19 +68,23 @@ class Relationship implements AccessibleInterface {
    *   The host entity.
    * @param string $target_key
    *   The property name of the relationship id.
+   * @param array $entity_list_metadata
+   *   An array of additional properties stored by the field and that will be
+   *   added to the meta in the relationship.
    */
-  public function __construct(ResourceTypeRepository $resource_type_repository, $field_name, $cardinality = FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, EntityCollection $entities, EntityInterface $host_entity, $target_key = 'target_id') {
+  public function __construct(ResourceTypeRepository $resource_type_repository, $field_name, $cardinality = FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, EntityCollection $entities, EntityInterface $host_entity, $target_key = 'target_id', array $entity_list_metadata = array()) {
     $this->resourceTypeRepository = $resource_type_repository;
     $this->propertyName = $field_name;
     $this->cardinality = $cardinality;
     $this->hostEntity = $host_entity;
     $this->items = [];
-    foreach ($entities as $entity) {
+    foreach ($entities as $key => $entity) {
       $this->items[] = new RelationshipItem(
         $resource_type_repository,
         $entity,
         $this,
-        $target_key
+        $target_key,
+        $entity_list_metadata[$key]
       );
     }
   }
diff --git a/src/Normalizer/RelationshipItem.php b/src/Normalizer/RelationshipItem.php
index b10201e..90d6dab 100644
--- a/src/Normalizer/RelationshipItem.php
+++ b/src/Normalizer/RelationshipItem.php
@@ -39,6 +39,13 @@ class RelationshipItem {
   protected $parent;
 
   /**
+   * The list of metadata associated with this field item value.
+   *
+   * @var array
+   */
+  protected $metadata;
+
+  /**
    * Relationship item constructor.
    *
    * @param \Drupal\jsonapi\ResourceType\ResourceTypeRepository $resource_type_repository
@@ -49,8 +56,10 @@ class RelationshipItem {
    *   The parent of this item.
    * @param string $target_key
    *   The key name of the target relationship.
+   * @param array $metadata
+   *   The list of metadata associated with this field item value.
    */
-  public function __construct(ResourceTypeRepository $resource_type_repository, EntityInterface $target_entity, Relationship $parent, $target_key = 'target_id') {
+  public function __construct(ResourceTypeRepository $resource_type_repository, EntityInterface $target_entity, Relationship $parent, $target_key = 'target_id', array $metadata = array()) {
     $this->targetResourceType = $resource_type_repository->get(
       $target_entity->getEntityTypeId(),
       $target_entity->bundle()
@@ -58,6 +67,7 @@ class RelationshipItem {
     $this->targetKey = $target_key;
     $this->targetEntity = $target_entity;
     $this->parent = $parent;
+    $this->metadata = $metadata;
   }
 
   /**
@@ -86,7 +96,10 @@ class RelationshipItem {
    * @return string
    */
   public function getValue() {
-    return [$this->targetKey => $this->getTargetEntity()->uuid()];
+    return [
+      $this->targetKey => $this->getTargetEntity()->uuid(),
+      'meta' => $this->metadata,
+    ];
   }
 
   /**
diff --git a/src/Normalizer/Value/RelationshipItemNormalizerValue.php b/src/Normalizer/Value/RelationshipItemNormalizerValue.php
index 8955eaf..29d599c 100644
--- a/src/Normalizer/Value/RelationshipItemNormalizerValue.php
+++ b/src/Normalizer/Value/RelationshipItemNormalizerValue.php
@@ -39,10 +39,16 @@ class RelationshipItemNormalizerValue extends FieldItemNormalizerValue implement
     if (!$value = parent::rasterizeValue()) {
       return $value;
     }
-    return [
+    $rasterizedValue = [
       'type' => $this->resource->getTypeName(),
-      'id' => $value,
+      'id' => $value['target_id'],
     ];
+
+    if (!empty($value['meta'])) {
+      $rasterizedValue['meta'] = $value['meta'];
+    }
+
+    return $rasterizedValue;
   }
 
   /**
-- 
1.9.1

