You are here

EntityShareCronService.php in Entity Share Cron 8.2

Same filename and directory in other branches
  1. 8 src/EntityShareCronService.php
  2. 3.0.x src/EntityShareCronService.php

File

src/EntityShareCronService.php
View source
<?php

declare (strict_types=1);
namespace Drupal\entity_share_cron;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Component\Serialization\Json;
use Drupal\entity_share\EntityShareUtility;
use Drupal\entity_share_client\Entity\Remote;
use Drupal\entity_share_client\Service\JsonapiHelperInterface;
use Drupal\entity_share_client\Service\RemoteManagerInterface;

/**
 * Entity Share Cron service.
 */
class EntityShareCronService implements EntityShareCronServiceInterface {

  /**
   * Module configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * Remote manager service.
   *
   * @var \Drupal\entity_share_client\Service\RemoteManagerInterface
   */
  protected $remoteManager;

  /**
   * JSON API helper service.
   *
   * @var \Drupal\entity_share_client\Service\JsonapiHelperInterface
   */
  protected $jsonapiHelper;

  /**
   * Queue service.
   *
   * @var \Drupal\Core\Queue\QueueFactory
   */
  protected $queueFactory;

  /**
   * Entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory.
   * @param \Drupal\entity_share_client\Service\RemoteManagerInterface $remote_manager
   *   Remote manager to get channels infos from.
   * @param \Drupal\entity_share_client\Service\JsonapiHelperInterface $jsonapi_helper
   *   Helps to prepare JSON responses.
   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
   *   Queue service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager.
   */
  public function __construct(ConfigFactoryInterface $config_factory, RemoteManagerInterface $remote_manager, JsonapiHelperInterface $jsonapi_helper, QueueFactory $queue_factory, EntityTypeManagerInterface $entity_type_manager) {
    $this->config = $config_factory
      ->get('entity_share_cron.settings');
    $this->remoteManager = $remote_manager;
    $this->jsonapiHelper = $jsonapi_helper;
    $this->queueFactory = $queue_factory;
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function enqueue($remote_id, $channel_id, array $channel_info) {
    $queue_name = EntityShareCronServiceInterface::PENDING_QUEUE_NAME;
    $queue = $this->queueFactory
      ->get($queue_name);
    $item = [
      'remote_id' => $remote_id,
      'channel_id' => $channel_id,
      'channel_info' => $channel_info,
    ];
    $queue
      ->createItem($item);
  }

  /**
   * {@inheritdoc}
   */
  public function sync($remote_id, $channel_id, array $channel_info, $page_limit = 0) {

    // Collects entities to import from each page.
    $data_to_import = [];
    $next_page = 1;
    $url = $channel_info['url'];
    while ($url) {

      // Performs request to get the list of entities.
      $page_data = $this
        ->getPage($remote_id, $channel_id, $url);
      $data_to_import = array_merge($data_to_import, $page_data['data']);
      $next_page++;
      $url = $page_data['next'];
      if ($url) {
        if ($page_limit != 0 && $next_page > $page_limit) {

          // Enqueues the next page.
          $next_info = $channel_info;
          $next_info['url'] = $url;
          $this
            ->enqueue($remote_id, $channel_id, $next_info);
          $url = FALSE;
        }
      }
    }

    // Removes data to import according to enabled operations.
    $channel_config = $this
      ->getChannelConfig($remote_id, $channel_id);
    if (empty($channel_config['operations']['create'])) {
      $this
        ->filterDataToImport($data_to_import);
    }
    if (empty($channel_config['operations']['update'])) {
      $this
        ->filterDataToImport($data_to_import, FALSE);
    }

    // Imports the data.
    return $this->jsonapiHelper
      ->importEntityListData($data_to_import);
  }

  /**
   * Returns the page data to import entities.
   *
   * @param string $remote_id
   *   The ID of the remote the channel belongs to.
   * @param string $channel_id
   *   The ID of the channel to be synchronized.
   * @param string $url
   *   The URL of the page.
   *
   * @return array
   *   An associative array with the following keys:
   *   - data => parsed JSON of entities to import.
   *   - next => URL of the next page.
   */
  protected function getPage($remote_id, $channel_id, $url) {
    $data = [
      'data' => [],
      'next' => FALSE,
    ];
    $remote = Remote::load($remote_id);
    if ($remote) {

      // Makes request to get profiles.
      $this->jsonapiHelper
        ->setRemote($remote);
      $http_client = $this->remoteManager
        ->prepareJsonApiClient($remote);
      $json_response = $http_client
        ->get($url)
        ->getBody()
        ->getContents();

      // Parses the JSON of entities to import.
      $json = Json::decode($json_response);
      $data['data'] = EntityShareUtility::prepareData($json['data']);

      // Gets the URL of the next page.
      $data['next'] = !empty($json['links']['next']['href']) ? $json['links']['next']['href'] : FALSE;
    }
    return $data;
  }

  /**
   * Filters the data to import by existing or non existing entities.
   *
   * @param array $data
   *   The data to be filtered.
   * @param bool $keep_existing
   *   Keeps only existing entities if TRUE. Otherwise, only non existing
   *   entities.
   */
  protected function filterDataToImport(array &$data, $keep_existing = TRUE) {

    // Groups entities by entity type before checking.
    $uuid_by_type = [];
    foreach ($data as $entity_data) {
      $parsed_type = explode('--', $entity_data['type']);
      $entity_type = $parsed_type[0];
      $uuid = $entity_data['id'];
      $uuid_by_type[$entity_type][] = $uuid;
    }

    // Filters entities.
    $existing_uuids = [];
    foreach ($uuid_by_type as $entity_type => $uuids) {
      $definition = $this->entityTypeManager
        ->getDefinition($entity_type);
      $uuid_property = $definition
        ->getKey('uuid');

      // Gets existing entities from the list.
      $storage = $this->entityTypeManager
        ->getStorage($entity_type);
      $existing_entities = $storage
        ->loadByProperties([
        $uuid_property => $uuids,
      ]);

      // Adds existing UUIDs to list.
      foreach ($existing_entities as $entity) {
        $uuid = $entity
          ->uuid();
        $existing_uuids[$uuid] = $uuid;
      }
    }

    // Filters data to be imported.
    $data_updated = [];
    foreach ($data as $entity_data) {
      $uuid = $entity_data['id'];
      if ($keep_existing && !empty($existing_uuids[$uuid])) {
        $data_updated[] = $entity_data;
      }
      elseif (!$keep_existing && empty($existing_uuids[$uuid])) {
        $data_updated[] = $entity_data;
      }
    }
    $data = $data_updated;
  }

  /**
   * Returns the settings of a channel.
   *
   * @param string $remote_id
   *   The ID of the remote the channel belongs to.
   * @param string $channel_id
   *   The ID of the channel.
   *
   * @return array
   *   Channel settings.
   */
  protected function getChannelConfig($remote_id, $channel_id) {
    $settings = [];
    $remotes = $this->config
      ->get('remotes');
    if (!empty($remotes[$remote_id]['channels'][$channel_id])) {
      $settings = $remotes[$remote_id]['channels'][$channel_id];
    }
    return $settings;
  }

}

Classes

Namesort descending Description
EntityShareCronService Entity Share Cron service.