staged_content-8.x-1.0-alpha1/src/LayeredImporter.php

src/LayeredImporter.php
<?php

namespace Drupal\staged_content;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\staged_content\DataProxy\DataProxyInterface;
use Drupal\staged_content\Storage\StorageHandlerInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Serializer\Serializer;

/**
 * A service for handling import of default content.
 *
 * @todo throw useful exceptions
 */
class LayeredImporter {

  /**
   * List of all the redefined id's.
   *
   * In case of duplication of id's a new id will be assigned.
   * If the id of an item was already set we'll reevaluate to preven errors
   * later. Note that all the references in the content storage are based on
   * the uuid, so this should not pose any issues. Except maybe in the rare
   * case of the 403, 404 and front page. So we'll emit a warning later for the
   * user to check the config manually.
   *
   * @var array
   *   List of all the id's that have been redefined.
   */
  protected $redefinedIds = [];

  /**
   * Defines relation domain URI for entity links.
   *
   * @var string
   */
  protected $linkDomain;

  /**
   * The serializer service.
   *
   * @var \Symfony\Component\Serializer\Serializer
   */
  protected $serializer;

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

  /**
   * A list of vertex objects keyed by their link.
   *
   * @var array
   */
  protected $vertexes = [];

  /**
   * The graph entries.
   *
   * @var array
   */
  protected $graph = [];

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The account switcher.
   *
   * @var \Drupal\Core\Session\AccountSwitcherInterface
   */
  protected $accountSwitcher;

  /**
   * The storage handler with all the content.
   *
   * @var \Drupal\staged_content\Storage\StorageHandlerInterface
   *   The storage handler with all the content.
   */
  protected $storageHandler;

  /**
   * The output handler for the run.
   *
   * @var \Symfony\Component\Console\Output\OutputInterface
   *   Output handler.
   */
  protected $output;

  /**
   * Constructs the default content manager.
   *
   * @param \Symfony\Component\Serializer\Serializer $serializer
   *   The serializer service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager service.
   * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
   *   The account switcher.
   */
  public function __construct(Serializer $serializer, EntityTypeManagerInterface $entity_type_manager, AccountSwitcherInterface $account_switcher) {
    $this->serializer = $serializer;
    $this->entityTypeManager = $entity_type_manager;
    $this->accountSwitcher = $account_switcher;
  }

  /**
   * Import all the data based on a given set of files.
   *
   * @param \Drupal\staged_content\Storage\StorageHandlerInterface $storageHandler
   *   The handler that holds all the information about the stored files.
   */
  public function importContent(StorageHandlerInterface $storageHandler) {
    $this->storageHandler = $storageHandler;
    $dataItems = $storageHandler->listDataItems();

    // Start by cleaning up the list from the straight keyed type to a
    // more structured one keyed by EntityType.
    $dataItems = $this->keyDataByEntityType($dataItems);

    // @TODO Improve output.
    foreach ($dataItems as $detectedTypeId => $info) {
      $this->getOutput()->writeln(sprintf("%s - count: %s", $detectedTypeId, count($info)));
    }

    // Load in all the items that should saved first.
    // E.g the items that should have their id's preserved.
    $this->precreateEntitiesWithPreservedId($dataItems);

    // Add the actual entities.
    $this->updateReferences($dataItems);

    // Print out the final report if needed.
    // @TODO Improve logging.
    if (!empty($this->redefinedIds)) {
      $this->getOutput()->writeln(" ======================== WARNING ======================== ");
      $this->getOutput()->writeln(" Duplicate id's were detected ");
      $this->getOutput()->writeln(" following content has been assigned a new id: ");

      foreach ($this->redefinedIds as $uuid => $entityInfo) {
        $this->getOutput()->writeln(sprintf("%s: %s", $uuid, $entityInfo));
      }
      $this->getOutput()->writeln(" ======================== ======= ======================== ");
    }
  }

  /**
   * Ensures all the items that have their id's preserved are added.
   *
   * This ensures both their id and their revision id are kept in tune.
   *
   * @param array $types
   *   Array of all the types that will be imported.
   */
  public function precreateEntitiesWithPreservedId(array $types) {
    // When preserving the keys an entity will be added a first time without
    // any of it's references (to prevent interference when generating
    // the correct id's).
    $context['ignore_references'] = TRUE;

    foreach ($types as $entityTypeId => $info) {

      if (count($info) == 0) {
        continue;
      }
      $this->getOutput()->writeLn('');
      $this->getOutput()->writeLn(sprintf('Handling %s: ', $entityTypeId));

      $dataList = $this->prepareList($entityTypeId, $info);

      if (count($dataList['preserved']) > 0) {
        // Import the "preserved" items first, and the "new" items second.
        $this->getOutput()->write('Preserved:');
        // @var \Drupal\staged_content\DataProxy\DataProxyInterface $dataProxy
        foreach ($dataList['preserved'] as $preservedId => $dataProxy) {
          // @TOOD Improve output.
          if ($this->getOutput()->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL) {
            $this->getOutput()->write('.');
          }
          $this->getOutput()->writeln(
            sprintf(" Precreating preserved item: %s: %s => %s",
              $dataProxy->getEntityType(),
              $preservedId,
              $dataProxy->getUuid()
            ),
            OutputInterface::VERBOSITY_VERBOSE
          );
          $this->importEntity($dataProxy, $context);
        }
        $this->getOutput()->writeLn('');
      }

      if (count($dataList['shifted']) > 0) {
        $this->getOutput()->write('Shifted: ');
        // Import the "new" items second since their id is not set in stone.
        foreach ($dataList['shifted'] as $dataProxy) {
          // @TOOD Improve output.
          if ($this->getOutput()
            ->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL
          ) {
            $this->getOutput()->write('.');
          }
          $this->getOutput()->writeln(
            sprintf("Precreating shifted item: %s => %s",
              $dataProxy->getEntityType(),
              $dataProxy->getUuid()
            ),
            OutputInterface::VERBOSITY_VERBOSE
          );
          $this->importEntity($dataProxy, $context, TRUE);
        }
        $this->getOutput()->writeLn('');
      }

      if (count($dataList['new']) > 0) {
        $this->getOutput()->write('New:');
        // Import the "new" items second since their id is not set in stone.
        foreach ($dataList['new'] as $dataProxy) {
          // @TOOD Improve output.
          if ($this->getOutput()
            ->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL
          ) {
            $this->getOutput()->write('.');
          }
          $this->getOutput()->writeln(
            sprintf("Precreating new item: %s => %s",
              $dataProxy->getEntityType(),
              $dataProxy->getUuid()
            ),
            OutputInterface::VERBOSITY_VERBOSE
          );
          $this->importEntity($dataProxy, $context);
        }
        $this->getOutput()->writeLn('');
      }
    }
  }

  /**
   * Add all the entities, since all the needed straight items have been added.
   *
   * @param array $types
   *   Array of all the types that will be imported.
   */
  public function updateReferences(array $types) {
    // Second pass, with auto importing of the references.
    $this->getOutput()->writeLn('');
    $this->getOutput()->writeLn('Completing all reference fields:');

    foreach ($types as $entityTypeId => $dataProxies) {
      /** @var \Drupal\staged_content\DataProxy\DataProxyInterface $dataProxy */
      foreach ($dataProxies as $dataProxy) {
        // @TODO Improve output.
        if ($this->getOutput()->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL) {
          $this->getOutput()->write('.');
        }
        $this->getOutput()->writeln(
          sprintf("Completing references for %s:%s",
            $dataProxy->getEntityType(),
            $dataProxy->getUuid()
          ),
          OutputInterface::VERBOSITY_VERBOSE
        );
        $this->importEntity($dataProxy);
      }
    }
    $this->getOutput()->writeLn('');
  }

  /**
   * Prepare/sort the list of items.
   *
   * This will ensure that any items that should preserve their id's are added
   * first and in the correct order. Afterwards any items that should not
   * preserve their id's are added.
   *
   * @param string $entityType
   *   The type of the entity.
   * @param \Drupal\staged_content\DataProxy\DataProxyInterface[] $info
   *   All the extra info connected to the entity type.
   *
   * @return array
   *   All the data to import, in the correct order.
   */
  public function prepareList(string $entityType, array $info) {
    $itemList = [
      'preserved' => [],
      'shifted' => [],
      'new' => [],
    ];

    foreach ($info as $uuid => $dataProxy) {
      // @TODO Probably cleaner to use the serializer here for the decoding.
      // But this is faster and easier to test for now.
      $data = $dataProxy->getData();

      if ($data['meta']['preserve_original_id']) {
        // If the id of an item was already set we'll reevaluate to prevent
        // errors later. Note that all the references in the content storage
        // are based on the uuid, so this should not pose any issues. Except
        // maybe in the rare case of the 403, 404 and front page.
        // So we'll emit a warning later for the user to check the config
        // manually.
        if (isset($itemList['preserved'][$data['meta']['original_id']])) {
          $this->getOutput()->writeln(sprintf("Preserved id altered!"));
          $this->getOutput()->writeln(sprintf("%s with id: %s was already defined for uuid: %s", $entityType, $data['meta']['original_id'], $itemList['preserved'][$data['meta']['original_id']]->getUuid()));

          // Place this item to the "new" queue to enforce generating a new id.
          $itemList['shifted'][] = $dataProxy;

          // Mark this item, so we can display the information later.
          $this->redefinedIds[$uuid] = $entityType . ':' . $data['meta']['original_id'] . ' --> ';
        }
        else {
          $itemList['preserved'][$data['meta']['original_id']] = $dataProxy;
        }
      }
      else {
        $itemList['new'][] = $dataProxy;
      }
    }

    ksort($itemList['preserved']);

    return $itemList;
  }

  /**
   * Key all the data items by their entity type.
   *
   * @param \Drupal\staged_content\DataProxy\DataProxyInterface[] $dataItems
   *   The data items in the set.
   *
   * @return array
   *   All the data items structured by entity type.
   */
  public function keyDataByEntityType(array $dataItems) {
    $return = [];

    foreach ($dataItems as $dataItem) {
      $return[$dataItem->getEntityType()][$dataItem->getUuid()] = $dataItem;
    }

    return $return;
  }

  /**
   * Gets the output interface.
   *
   * @return \Symfony\Component\Console\Output\OutputInterface
   *   Get the output interface.
   */
  public function getOutput() {

    if (!isset($this->output)) {
      $this->output = new ConsoleOutput();
    }

    return $this->output;
  }

  /**
   * Set the output interface.
   *
   * @param \Symfony\Component\Console\Output\OutputInterface $output
   *   Output interface to set.
   */
  public function setOutput(OutputInterface $output) {
    $this->output = $output;
  }

  /**
   * Import a single entity.
   *
   * @param \Drupal\staged_content\DataProxy\DataProxyInterface $dataProxy
   *   The data proxy containing all the data.
   * @param array $context
   *   Extra context for the import/.
   * @param bool $stripPreservedId
   *   In some cases the preserved id has to be stripped from the data.
   *   This is most common in case of duplicate entity id's. Where the preserved
   *   Id is automatically shifted.
   */
  protected function importEntity(DataProxyInterface $dataProxy, array $context = [], bool $stripPreservedId = FALSE) {
    $context += [
      'ignore_references' => FALSE,
    ];

    $decoded = $this->serializer->decode($dataProxy->getRawData(), 'storage_json');

    // Hard strip the original id if relevant.
    if ($stripPreservedId) {
      $decoded['meta']['preserve_original_id'] = FALSE;
      unset($decoded['meta']['original_id']);
    }

    $class = $this->entityTypeManager->getDefinition($dataProxy->getEntityType())->getClass();
    $denormalized = $this->serializer->denormalize($decoded, $class, 'storage_json', $context);
    $denormalized->save();

    // If this item was marked as one of the items with it's id redefined,
    // Mark the new id here.
    if ($stripPreservedId && isset($this->redefinedIds[$denormalized->uuid()])) {
      $this->redefinedIds[$denormalized->uuid()] .= $denormalized->id();
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc