View source  
  <?php
declare (strict_types=1);
namespace Drupal\entity_share_diff\Service;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\entity_share\EntityShareUtility;
use Drupal\entity_share_client\Entity\RemoteInterface;
use Drupal\entity_share_client\Service\EntityReferenceHelper;
use Drupal\entity_share_client\Service\JsonapiHelperInterface;
use Drupal\entity_share_client\Service\RemoteManagerInterface;
use Drupal\entity_share_diff\DiffGenerator\DiffGeneratorPluginManager;
use Drupal\jsonapi\ResourceType\ResourceType;
use Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface;
class EntityParser implements EntityParserInterface {
  
  protected $diffGeneratorManager;
  
  protected $languageManager;
  
  protected $remoteManager;
  
  protected $jsonapiHelper;
  
  protected $resourceTypeRepository;
  
  protected $entityReferenceHelper;
  
  protected $remote;
  
  private $processedEntities;
  
  public function __construct(DiffGeneratorPluginManager $diff_generator_manager, LanguageManagerInterface $language_manager, RemoteManagerInterface $remote_manager, JsonapiHelperInterface $jsonapi_helper, ResourceTypeRepositoryInterface $resource_type_repository, EntityReferenceHelper $entity_reference_helper) {
    $this->diffGeneratorManager = $diff_generator_manager;
    $this->languageManager = $language_manager;
    $this->remoteManager = $remote_manager;
    $this->jsonapiHelper = $jsonapi_helper;
    $this->resourceTypeRepository = $resource_type_repository;
    $this->entityReferenceHelper = $entity_reference_helper;
    $this->processedEntities = [
      'local' => [],
      'remote' => [],
    ];
  }
  
  public function getRemote() {
    return $this->remote;
  }
  
  public function setRemote($remote) {
    $this->remote = $remote;
  }
  
  public function prepareLocalEntity(ContentEntityInterface $entity) {
    $this
      ->setRemote(FALSE);
    return $this
      ->parseEntity($entity);
  }
  
  public function prepareRemoteEntity(array $remote_data, RemoteInterface $remote) {
    $this
      ->setRemote($remote);
    $remote_entity = $this->jsonapiHelper
      ->extractEntity($remote_data);
    return $this
      ->parseEntity($remote_entity, $remote_data);
  }
  
  public function validateNeedToProcess(string $uuid, bool $remote) {
    $main_key = $remote ? 'remote' : 'local';
    if (!in_array($uuid, $this->processedEntities[$main_key])) {
      $this->processedEntities[$main_key][] = $uuid;
      return TRUE;
    }
    return FALSE;
  }
  
  public function getPublicFieldName(string $field_name, array $entity_json_data) {
    if (empty($entity_json_data['type'])) {
      return '';
    }
    $parsed_type = explode('--', $entity_json_data['type']);
    $entity_type_id = $parsed_type[0];
    $bundle = $parsed_type[1];
    $resource_type = $this->resourceTypeRepository
      ->get($entity_type_id, $bundle);
    if (!$resource_type instanceof ResourceType) {
      return '';
    }
    if (!$resource_type
      ->hasField($field_name)) {
      return '';
    }
    return $resource_type
      ->getPublicName($field_name);
  }
  
  public function getRemoteChangedTime(array $remote_data) {
    $changed_public_name = $this
      ->getPublicFieldName('changed', $remote_data);
    $entity_changed_time = 0;
    if ($changed_public_name && !empty($remote_data['attributes'][$changed_public_name])) {
      $entity_changed_time = EntityShareUtility::convertChangedTime($remote_data['attributes'][$changed_public_name]);
    }
    return $entity_changed_time;
  }
  
  protected function parseEntity(ContentEntityInterface $entity, array $remote_data = NULL) {
    $result = [];
    $langcode = $this->languageManager
      ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
      ->getId();
    
    if ($entity
      ->hasTranslation($langcode)) {
      $entity = $entity
        ->getTranslation($langcode);
    }
    $irrelevant_fields = $this
      ->getFieldsIrrelevantForDiff($entity);
    
    
    foreach ($entity as $item_key => $field_items) {
      
      if ($this
        ->getRemote()) {
        $public_key = $this
          ->getPublicFieldName($item_key, $remote_data);
      }
      else {
        $public_key = $item_key;
      }
      $remote_field_data = [];
      
      switch ($this->entityReferenceHelper
        ->relationshipHandleable($field_items)) {
        case EntityReferenceHelper::RELATIONSHIP_HANDLEABLE:
          $should_parse = TRUE;
          if (isset($remote_data['relationships'][$public_key])) {
            $remote_field_data = $remote_data['relationships'][$public_key];
            if (isset($remote_field_data['data'])) {
              $remote_field_data['data'] = EntityShareUtility::prepareData($remote_field_data['data']);
            }
          }
          break;
        case EntityReferenceHelper::RELATIONSHIP_NOT_HANDLEABLE:
          $should_parse = FALSE;
          break;
        case EntityReferenceHelper::RELATIONSHIP_NOT_ENTITY_REFERENCE:
          $should_parse = !in_array($item_key, $irrelevant_fields);
          break;
      }
      if (!$should_parse) {
        continue;
      }
      $parsed_field = $this
        ->parseField($item_key, $field_items, $remote_field_data);
      if ($parsed_field != NULL) {
        $field_label = (string) $field_items
          ->getFieldDefinition()
          ->getLabel();
        $result[$field_label] = $parsed_field;
      }
    }
    return $result;
  }
  
  protected function parseField(string $item_key, FieldItemListInterface $field_items, array $remote_field_data = []) {
    $build = [];
    $field_type = $field_items
      ->getFieldDefinition()
      ->getType();
    $plugin = $this->diffGeneratorManager
      ->createInstanceForFieldDefinition($field_type);
    if ($plugin) {
      
      $remote = $this
        ->getRemote();
      if ($remote) {
        $plugin
          ->setRemote($remote);
      }
      
      $build = $plugin
        ->build($field_items, $remote_field_data);
      if (!empty($build)) {
        
        $cardinality = $field_items
          ->getFieldDefinition()
          ->getFieldStorageDefinition()
          ->getCardinality();
        if ($cardinality == 1 && is_array($build)) {
          $build = current($build);
        }
      }
    }
    return $build;
  }
  
  public function referenceEmbeddable(string $entity_type_id) {
    $embeddable_types = [
      'paragraph',
      'media',
    ];
    return in_array($entity_type_id, $embeddable_types);
  }
  
  protected function getFieldsIrrelevantForDiff(ContentEntityInterface $entity) {
    
    $entity_keys = $entity
      ->getEntityType()
      ->getKeys();
    
    unset($entity_keys['label']);
    unset($entity_keys['langcode']);
    $field_names = array_values($entity_keys);
    
    $revision_keys = array_keys($entity
      ->getEntityType()
      ->getRevisionMetadataKeys());
    $field_names = array_merge($field_names, $revision_keys);
    
    $other_keys = [
      'changed',
      'created',
      
      'content_translation_source',
      'content_translation_affected',
      'content_translation_outdated',
      'revision_translation_affected',
      
      'parent_id',
      'parent_type',
      'parent_field_name',
      
      'revision_timestamp',
      'revision_log',
    ];
    $field_names = array_merge($field_names, $other_keys);
    return $field_names;
  }
}