View source
<?php
declare (strict_types=1);
namespace Drupal\entity_share_client\Service;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\entity_share\EntityShareUtility;
use Drupal\entity_share_client\ImportContext;
use Drupal\entity_share_client\ImportProcessor\ImportProcessorInterface;
use Drupal\entity_share_client\RuntimeImportContext;
use Psr\Log\LoggerInterface;
class ImportService implements ImportServiceInterface {
use StringTranslationTrait;
protected $entityTypeManager;
protected $logger;
protected $messenger;
protected $remoteManager;
protected $importConfigManipulator;
protected $jsonapiHelper;
protected $runtimeImportContext;
protected $importProcessors;
public function __construct(EntityTypeManagerInterface $entity_type_manager, LoggerInterface $logger, MessengerInterface $messenger, RemoteManagerInterface $remote_manager, ImportConfigManipulatorInterface $import_config_manipulator, JsonapiHelperInterface $jsonapi_helper) {
$this->entityTypeManager = $entity_type_manager;
$this->logger = $logger;
$this->messenger = $messenger;
$this->remoteManager = $remote_manager;
$this->importConfigManipulator = $import_config_manipulator;
$this->jsonapiHelper = $jsonapi_helper;
$this->runtimeImportContext = new RuntimeImportContext();
}
public function importEntities(ImportContext $context, array $uuids, bool $is_batched = TRUE) {
if (!$this
->prepareImport($context)) {
return [];
}
$url = $this->runtimeImportContext
->getChannelUrl();
$prepared_url = EntityShareUtility::prepareUuidsFilteredUrl($url, $uuids);
if ($is_batched) {
$batch = [
'title' => $this
->t('Import entities'),
'operations' => [
[
'\\Drupal\\entity_share_client\\ImportBatchHelper::importUrlBatch',
[
$context,
$prepared_url,
],
],
],
'finished' => '\\Drupal\\entity_share_client\\ImportBatchHelper::importUrlBatchFinished',
];
batch_set($batch);
}
else {
return $this
->importFromUrl($prepared_url);
}
}
public function importChannel(ImportContext $context) {
if (!$this
->prepareImport($context)) {
return;
}
$log_variables = [
'@remote_id' => $context
->getRemoteId(),
'@channel_id' => $context
->getChannelId(),
];
$channel_count = $context
->getRemoteChannelCount();
if (empty($channel_count)) {
$url_uuid = $this->runtimeImportContext
->getChannelUrlUuid();
$response = $this->remoteManager
->jsonApiRequest($this->runtimeImportContext
->getRemote(), 'GET', $url_uuid);
$json = Json::decode((string) $response
->getBody());
if (isset($json['errors'])) {
$this->logger
->error('An error occurred while requesting the UUID URL for the remote website @remote_id and channel @channel_id', $log_variables);
$this->messenger
->addError($this
->t('An error occurred while requesting the UUID URL for the remote website @remote_id and channel @channel_id', $log_variables));
return;
}
elseif (!isset($json['meta']['count'])) {
$this->logger
->error('There is no count of the number of entities to import for the remote website @remote_id and channel @channel_id', $log_variables);
$this->messenger
->addError($this
->t('There is no count of the number of entities to import for the remote website @remote_id and channel @channel_id', $log_variables));
return;
}
$channel_count = $json['meta']['count'];
}
if ($channel_count == 0) {
$this->logger
->info('Nothing to import for the remote website @remote_id and channel @channel_id', $log_variables);
$this->messenger
->addMessage($this
->t('Nothing to import for the remote website @remote_id and channel @channel_id', $log_variables));
return;
}
$step = 50;
$url = $this->runtimeImportContext
->getChannelUrl();
$parsed_url = UrlHelper::parse($url);
$parsed_url['query']['page']['limit'] = $step;
$operations = [];
if ($channel_count >= $step) {
$offsets = range(0, $channel_count - 1, $step);
foreach ($offsets as $offset) {
$parsed_url['query']['page']['offset'] = $offset;
$query = UrlHelper::buildQuery($parsed_url['query']);
$prepared_url = $parsed_url['path'] . '?' . $query;
$operations[] = [
'\\Drupal\\entity_share_client\\ImportBatchHelper::importUrlBatch',
[
$context,
$prepared_url,
],
];
}
}
else {
$operations[] = [
'\\Drupal\\entity_share_client\\ImportBatchHelper::importUrlBatch',
[
$context,
$url,
],
];
}
$batch = [
'title' => $this
->t('Import channel'),
'operations' => $operations,
'progress_message' => $this
->t('Imported pages: @current of @total.'),
'finished' => '\\Drupal\\entity_share_client\\ImportBatchHelper::importUrlBatchFinished',
];
batch_set($batch);
}
public function importFromUrl(string $url) {
$response = $this
->jsonApiRequest('GET', $url);
$json = Json::decode((string) $response
->getBody());
if (!isset($json['data'])) {
return [];
}
$entity_list_data = EntityShareUtility::prepareData($json['data']);
return $this
->importEntityListData($entity_list_data);
}
public function importEntityListData(array $entity_list_data) {
$imported_entity_ids = [];
foreach (EntityShareUtility::prepareData($entity_list_data) as $entity_data) {
foreach ($this->importProcessors[ImportProcessorInterface::STAGE_PREPARE_ENTITY_DATA] as $import_processor) {
$import_processor
->prepareEntityData($this->runtimeImportContext, $entity_data);
}
foreach ($this->importProcessors[ImportProcessorInterface::STAGE_IS_ENTITY_IMPORTABLE] as $import_processor) {
if (!$import_processor
->isEntityImportable($this->runtimeImportContext, $entity_data)) {
continue 2;
}
}
foreach ($this->importProcessors[ImportProcessorInterface::STAGE_PREPARE_IMPORTABLE_ENTITY_DATA] as $import_processor) {
$import_processor
->prepareImportableEntityData($this->runtimeImportContext, $entity_data);
}
$processed_entity = $this
->getProcessedEntity($entity_data);
$imported_entity_ids[$processed_entity
->uuid()] = $processed_entity
->id();
$processed_entity_langcode = $processed_entity
->language()
->getId();
$processed_entity_uuid = $processed_entity
->uuid();
if ($this->runtimeImportContext
->isEntityTranslationImported($processed_entity_langcode, $processed_entity_uuid)) {
continue;
}
else {
$this->runtimeImportContext
->addImportedEntity($processed_entity_langcode, $processed_entity_uuid);
}
foreach ($this->importProcessors[ImportProcessorInterface::STAGE_PROCESS_ENTITY] as $import_processor) {
$import_processor
->processEntity($this->runtimeImportContext, $processed_entity, $entity_data);
}
$processed_entity
->save();
foreach ($this->importProcessors[ImportProcessorInterface::STAGE_POST_ENTITY_SAVE] as $import_processor) {
$import_processor
->postEntitySave($this->runtimeImportContext, $processed_entity);
}
}
return $imported_entity_ids;
}
public function prepareImport(ImportContext $context) {
$remote_id = $context
->getRemoteId();
$channel_id = $context
->getChannelId();
$import_config_id = $context
->getImportConfigId();
$remote = NULL;
$import_config = NULL;
$log_variables = [];
$log_variables['@remote_id'] = $remote_id;
$log_variables['@channel_id'] = $channel_id;
$log_variables['@import_config_id'] = $import_config_id;
if (is_null($import_config_id)) {
$this->logger
->error('No import config ID provided.');
$this->messenger
->addError($this
->t('No import config ID provided.'));
return FALSE;
}
try {
$import_config = $this->entityTypeManager
->getStorage('import_config')
->load($import_config_id);
} catch (\Exception $exception) {
$this->logger
->error('Impossible to load the import config with the ID: @import_config_id', $log_variables);
$this->messenger
->addError($this
->t('Impossible to load the import config with the ID: @import_config_id', $log_variables));
}
if (is_null($import_config)) {
$this->logger
->error('Impossible to load the import config with the ID: @import_config_id', $log_variables);
$this->messenger
->addError($this
->t('Impossible to load the import config with the ID: @import_config_id', $log_variables));
return FALSE;
}
$this->importProcessors = $this->importConfigManipulator
->getImportProcessorsByStages($import_config);
try {
$remote = $this->entityTypeManager
->getStorage('remote')
->load($remote_id);
} catch (\Exception $exception) {
$this->logger
->error('Impossible to load the remote website with the ID: @remote_id', $log_variables);
$this->messenger
->addError($this
->t('Impossible to load the remote website with the ID: @remote_id', $log_variables));
}
if (is_null($remote)) {
return FALSE;
}
$this->runtimeImportContext
->setRemote($remote);
$channels_info = $this->remoteManager
->getChannelsInfos($remote);
if (!isset($channels_info[$channel_id])) {
$this->logger
->error('Impossible to obtain the channel @channel_id on the remote website with the ID: @remote_id', $log_variables);
$this->messenger
->addError($this
->t('Impossible to obtain the channel @channel_id on the remote website with the ID: @remote_id', $log_variables));
return FALSE;
}
$this->runtimeImportContext
->setChannelId($channel_id);
$this->runtimeImportContext
->setChannelLabel($channels_info[$channel_id]['label']);
$this->runtimeImportContext
->setChannelUrl($channels_info[$channel_id]['url']);
$this->runtimeImportContext
->setChannelUrlUuid($channels_info[$channel_id]['url_uuid']);
$this->runtimeImportContext
->setChannelEntityType($channels_info[$channel_id]['channel_entity_type']);
$this->runtimeImportContext
->setChannelBundle($channels_info[$channel_id]['channel_bundle']);
$this->runtimeImportContext
->setChannelSearchConfiguration($channels_info[$channel_id]['search_configuration']);
$this->runtimeImportContext
->setFieldMappings($this->remoteManager
->getfieldMappings($remote));
$this->runtimeImportContext
->setImportService($this);
return TRUE;
}
public function getRuntimeImportContext() {
return $this->runtimeImportContext;
}
public function request($method, $url) {
return $this->remoteManager
->request($this->runtimeImportContext
->getRemote(), $method, $url);
}
public function jsonApiRequest($method, $url) {
return $this->remoteManager
->jsonApiRequest($this->runtimeImportContext
->getRemote(), $method, $url);
}
protected function getProcessedEntity(array $entity_data) {
$field_mappings = $this->runtimeImportContext
->getFieldMappings();
$parsed_type = explode('--', $entity_data['type']);
$entity_type_id = $parsed_type[0];
$entity_bundle = $parsed_type[1];
$entity_storage = $this->entityTypeManager
->getStorage($entity_type_id);
$entity_keys = $entity_storage
->getEntityType()
->getKeys();
$data_uuid = $entity_data['id'];
$langcode_public_name = FALSE;
if (!empty($entity_keys['langcode']) && isset($field_mappings[$entity_type_id][$entity_bundle][$entity_keys['langcode']])) {
$langcode_public_name = $field_mappings[$entity_type_id][$entity_bundle][$entity_keys['langcode']];
}
$data_langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
if ($langcode_public_name && !empty($entity_data['attributes'][$langcode_public_name])) {
$data_langcode = $entity_data['attributes'][$langcode_public_name];
}
$existing_entities = $entity_storage
->loadByProperties([
'uuid' => $data_uuid,
]);
$remote_entity = $this->jsonapiHelper
->extractEntity($entity_data);
if (empty($existing_entities)) {
$remote_entity
->save();
$processed_entity = $remote_entity;
}
else {
$existing_entity = array_shift($existing_entities);
$has_translation = $existing_entity
->hasTranslation($data_langcode);
if ($has_translation) {
$existing_translation = $existing_entity
->getTranslation($data_langcode);
foreach (array_keys($entity_data['attributes']) as $field_public_name) {
$field_internal_name = array_search($field_public_name, $field_mappings[$entity_type_id][$entity_bundle]);
if ($field_internal_name && $existing_translation
->hasField($field_internal_name)) {
$existing_translation
->set($field_internal_name, $remote_entity
->get($field_internal_name)
->getValue());
}
else {
$this->logger
->notice('Error during import. The field @field does not exist.', [
'@field' => $field_internal_name,
]);
}
}
$processed_entity = $existing_translation;
}
else {
$remote_entity_as_array = $remote_entity
->toArray();
$existing_entity
->addTranslation($data_langcode, $remote_entity_as_array);
$processed_entity = $existing_entity
->getTranslation($data_langcode);
}
}
return $processed_entity;
}
}