You are here

class ProcessedContentLoader in YAML Content 8.2

A ContentLoader supporting processing of content through plugins.

@todo Extend this class as an EntityLoader to support later support options.

Hierarchy

Expanded class hierarchy of ProcessedContentLoader

File

src/ContentLoader/ProcessedContentLoader.php, line 19

Namespace

Drupal\yaml_content\ContentLoader
View source
class ProcessedContentLoader extends ContentLoaderBase {

  /**
   * A plugin manager to load processor plugins from.
   *
   * @var \Drupal\yaml_content\ContentProcessorPluginManager
   */
  protected $processorPluginManager;

  /**
   * ProcessedContentLoader constructor.
   *
   * The parent constructor is overridden to pull in the content processor
   * plugin manager.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager service to manage dynamic entity operations.
   * @param \Drupal\Component\Serialization\SerializationInterface $parser
   *   The content parser to use for loading content from content files.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
   *   An event dispatcher service to publish events throughout the process.
   * @param \Drupal\Component\Plugin\PluginManagerInterface $processorPluginManager
   *   The plugin manager to use for loading content processor plugins.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager, SerializationInterface $parser, EventDispatcherInterface $dispatcher, PluginManagerInterface $processorPluginManager) {
    parent::__construct($entityTypeManager, $parser, $dispatcher);
    $this->processorPluginManager = $processorPluginManager;
  }

  /**
   * {@inheritdoc}
   */
  public function loadContent($save = TRUE) {
    $content_context = [];
    $this
      ->preprocessData($this->parsedContent, $content_context);
    $loaded = parent::loadContent($save);
    $this
      ->postprocessData($this->parsedContent, $loaded, $content_context);
    return $loaded;
  }

  /**
   * {@inheritdoc}
   */
  public function importEntity(array $content_data) {

    // Preprocess the content data.
    $entity_context = [];
    $this
      ->preprocessData($content_data, $entity_context);
    $entity = parent::importEntity($content_data);

    // Postprocess loaded entity object.
    $this
      ->postprocessData($content_data, $entity, $entity_context);
    return $entity;
  }

  /**
   * {@inheritdoc}
   */
  public function importEntityField(array $field_data, EntityInterface $entity, FieldDefinitionInterface $field_definition) {

    // Preprocess field data.
    $field_context['entity'] = $entity;
    $field_context['field'] = $field_definition;
    $this
      ->preprocessData($field_data, $field_context);
    parent::importEntityField($field_data, $entity, $field_definition);

    // Postprocess loaded field data.
    $this
      ->postprocessData($field_data, $entity, $field_context);
  }

  /**
   * {@inheritdoc}
   */
  public function importFieldItem($field_item_data, EntityInterface $entity, FieldDefinitionInterface $field_definition) {

    // Preprocess field data.
    $field_context['entity'] = $entity;
    $field_context['field'] = $field_definition;
    if (is_array($field_item_data)) {
      $this
        ->preprocessData($field_item_data, $field_context);
    }
    $item_value = parent::importFieldItem($field_item_data, $entity, $field_definition);

    // Postprocess loaded field data.
    if (is_array($field_item_data)) {
      $this
        ->postprocessData($field_item_data, $item_value, $field_context);
    }
    return $item_value;
  }

  /**
   * Load the processor plugin for use on the import content.
   *
   * Load the processor plugin and configure any relevant context available in
   * the provided `$context` parameter.
   *
   * @param string $processor_id
   *   The machine name identifier of the processor to be loaded.
   * @param array $context
   *   Contextual data to configure the loaded processor plugin.
   *
   * @return \Drupal\yaml_content\ImportProcessorInterface
   *   The loaded import processor object.
   *
   * @throws \Exception
   *
   * @todo Handle PluginNotFoundException.
   */
  public function loadProcessor($processor_id, array &$context) {
    $processor_definition = $this->processorPluginManager
      ->getDefinition($processor_id);

    // @todo Implement exception class for invalid processors.
    if (!$processor_definition['import']) {
      throw new \Exception(sprintf('The %s processor does not support import operations.', $processor_id));
    }

    // Instantiate the processor plugin with default config values.
    $processor = $this->processorPluginManager
      ->createInstance($processor_id, $context);

    // Set and validate context values.
    if (isset($processor_definition['context'])) {
      foreach ($processor_definition['context'] as $name => $definition) {
        if (isset($context[$name])) {

          // @todo Validate context types and values.
          $processor
            ->setContextValue($name, $context[$name]);
        }
        else {

          // Handle missing required contexts.
          if ($definition
            ->isRequired()) {

            // @todo Handle this exception.
          }
        }
      }
    }
    return $processor;
  }

  /**
   * Evaluate the current import data array and run any preprocessing needed.
   *
   * Any data keys starting with '#' indicate preprocessing instructions that
   * should be executed on the data prior to import. The data array is altered
   * directly and fully prepared for import.
   *
   * @param array $import_data
   *   The current content data being evaluated for import. This array is
   *   altered directly and returned without the processing key.
   * @param array $context
   *   Contextual data passed by reference to preprocessing plugins.
   *
   * @throws Exception
   */
  public function preprocessData(array &$import_data, array &$context) {

    // Abort if there are no preprocessing instructions.
    if (!isset($import_data['#preprocess'])) {
      return;
    }
    if (!is_array($import_data['#preprocess'])) {
      throw new Exception('Preprocessing instructions must be provided as an array.');
    }

    // Execute all processing actions.
    foreach ($import_data['#preprocess'] as $key => $data) {

      // Execute preprocess actions.
      if (isset($data['#plugin'])) {

        // Expose preprocess configuration into context for the plugin.
        $processor_context = array_merge($context, $data);

        // Load the plugin.
        $processor = $this
          ->loadProcessor($data['#plugin'], $processor_context);
        assert($processor instanceof ImportProcessorInterface, 'Preprocess plugin [' . $data['#plugin'] . '] failed to load a valid ImportProcessor plugin.');

        // Execute plugin on $import_data.
        $processor
          ->preprocess($import_data);
      }
      else {
        throw new Exception('Preprocessing instructions require a defined "#plugin" identifier.');
      }
    }

    // Remove executed preprocess data.
    unset($import_data['#preprocess']);
  }

  /**
   * Recursively navigate the content hierarchy to apply processors.
   *
   * @param mixed $import_data
   *   Array or string data extracted from the loaded content file.
   */
  public function recursivelyPreprocessData(&$import_data) {
    if (!is_array($import_data)) {
      return;
    }
    foreach ($import_data as $key => &$value) {

      // Preprocess each array value recursively.
      if (is_array($value)) {
        $context = [];

        // Preprocess this branch.
        $this
          ->preprocessData($value, $context);

        // Recurse further for additional preprocessing.
        if (is_array($value)) {
          array_walk($value, [
            $this,
            'recursivelyPreprocessData',
          ]);
        }
      }
    }
  }

  /**
   * Evaluate the current import data array and run any preprocessing needed.
   *
   * Any data keys starting with '#' indicate preprocessing instructions that
   * should be executed on the data prior to import. The data array is altered
   * directly and fully prepared for import.
   *
   * @param array $import_data
   *   The current content data being evaluated for import. This array is
   *   altered directly and returned without the processing key.
   * @param mixed $loaded_content
   *   The loaded content object generated from the given import data.
   * @param array $context
   *   Contextual data passed by reference to preprocessing plugins.
   *
   * @throws Exception
   */
  public function postprocessData(array &$import_data, &$loaded_content, array &$context) {

    // Abort if there are no postprocessing instructions.
    if (!isset($import_data['#postprocess'])) {
      return;
    }
    if (!is_array($import_data['#postprocess'])) {
      throw new Exception('Postprocessing instructions must be provided as an array.');
    }

    // Execute all processing actions.
    foreach ($import_data['#postprocess'] as $key => $data) {

      // @todo Execute postprocess actions.
      if (isset($data['#plugin'])) {

        // Load the plugin.
        $processor = $this
          ->loadProcessor($data['#plugin'], $context);
        assert($processor instanceof ImportProcessorInterface, 'Postprocess plugin [' . $data['#plugin'] . '] failed to load a valid ImportProcessor plugin.');

        // @todo Provide required context as defined by plugin definition.
        // @todo Execute plugin on $loaded_content.
      }
      else {
        throw new Exception('Postprocessing instructions require a defined "#plugin" identifier.');
      }
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ContentLoaderBase::$contentFile protected property The file path for the content file currently being loaded.
ContentLoaderBase::$dispatcher protected property Event dispatcher service to report events throughout the loading process.
ContentLoaderBase::$entityTypeManager protected property Entity type manager service to dynamically handle entities.
ContentLoaderBase::$parsedContent protected property The array of content parsed from the content file being loaded.
ContentLoaderBase::$parser protected property Content file parser utility.
ContentLoaderBase::$path protected property The file path where content files should be loaded from.
ContentLoaderBase::assignFieldValue public function Set or assign a field value based on field cardinality.
ContentLoaderBase::buildEntity public function Build an entity from the provided content data. Overrides ContentLoaderInterface::buildEntity
ContentLoaderBase::loadContentBatch public function Load a batch of content files. Overrides ContentLoaderInterface::loadContentBatch
ContentLoaderBase::parseContent public function Parse the given yaml content file into an array. Overrides ContentLoaderInterface::parseContent
ContentLoaderBase::setContentPath public function Set a path prefix for all content files to be loaded from.
ProcessedContentLoader::$processorPluginManager protected property A plugin manager to load processor plugins from.
ProcessedContentLoader::importEntity public function Load an entity from a loaded import data outline. Overrides ContentLoaderBase::importEntity
ProcessedContentLoader::importEntityField public function Process import data into an appropriate field value and assign it. Overrides ContentLoaderBase::importEntityField
ProcessedContentLoader::importFieldItem public function Process import data for an individual field list item value. Overrides ContentLoaderBase::importFieldItem
ProcessedContentLoader::loadContent public function Load all demo content for a set of parsed data. Overrides ContentLoaderBase::loadContent
ProcessedContentLoader::loadProcessor public function Load the processor plugin for use on the import content.
ProcessedContentLoader::postprocessData public function Evaluate the current import data array and run any preprocessing needed.
ProcessedContentLoader::preprocessData public function Evaluate the current import data array and run any preprocessing needed.
ProcessedContentLoader::recursivelyPreprocessData public function Recursively navigate the content hierarchy to apply processors.
ProcessedContentLoader::__construct public function ProcessedContentLoader constructor. Overrides ContentLoaderBase::__construct