You are here

class MetatagImport in Metatag Import Export CSV 8

Class for batch process for metatag import.

Hierarchy

Expanded class hierarchy of MetatagImport

1 file declares its use of MetatagImport
MetatagImportTest.php in tests/src/Kernel/MetatagImportTest.php

File

src/MetatagImport.php, line 11

Namespace

Drupal\metatag_import_export_csv
View source
class MetatagImport {

  /**
   * Validate the CSV header row.
   *
   * Helper for validate_csv_header().
   *
   * @param \Drupal\file\FileInterface $file
   *   The uploaded file.
   *
   * @return array
   *   An array of errors.
   */
  public static function validateCsvHeader(FileInterface $file) {
    $url = $file
      ->getFileUri();
    $filepath = file_create_url($url);
    $handle = fopen($filepath, 'r');
    $headers = fgetcsv($handle);

    // Close the CSV file, as the batch setup needs to open it from the top.
    fclose($handle);
    $errors = [];
    if (!in_array('path_alias', $headers)) {
      if (!in_array('entity_type', $headers)) {
        $errors[] = t("The CSV must have an 'entity_type' column if it does not have a 'path_alias' column.");
      }
      if (!in_array('entity_id', $headers)) {
        $errors[] = t("The CSV must have an 'entity_id' column if it does not have a 'path_alias' column.");
      }
    }
    return $errors;
  }

  /**
   * Batch callback for parsing the CSV.
   *
   * @param array $headers
   *   The header row of the CSV file. This must contain an 'entity_type' and
   *   'entity_id' column.
   * @param array $filepath
   *   The filepath to the uploaded CSV file.
   * @param array $context
   *   The batch context.
   */
  public static function importCsvBatchOperation($headers, $filepath, &$context = []) {

    // Initial setup on the first operation.
    if (empty($context['sandbox'])) {
      $context['sandbox']['handle'] = NULL;
      $context['sandbox']['pointer_position'] = 0;
      $context['sandbox']['csv_line'] = 0;
      $context['results']['success'] = [];
      $context['results']['error'] = [];
    }
    if (!is_resource($context['sandbox']['handle'])) {
      $context['sandbox']['handle'] = fopen($filepath, 'r');
    }

    // If we've reached the end of the file, we're done.
    if (feof($context['sandbox']['handle'])) {
      $context['finished'] = 1;
      return;
    }
    if ($context['sandbox']['pointer_position'] == 0) {

      // On the first operation, discard the CSV header row.
      fgetcsv($context['sandbox']['handle']);
      $context['sandbox']['csv_line']++;
    }
    else {

      // On subsequent operations, move the pointer to where it was for the last
      // operation.
      fseek($context['sandbox']['handle'], $context['sandbox']['pointer_position']);
    }

    // Get the data, and update our stored pointer.
    $data = fgetcsv($context['sandbox']['handle']);
    $context['sandbox']['csv_line']++;
    $context['sandbox']['pointer_position'] = ftell($context['sandbox']['handle']);
    try {
      $entity = static::importCsvRow($headers, $data);
      $context['results']['success'][$entity
        ->getEntityTypeId()][] = $entity
        ->id();
    } catch (\Exception $e) {
      $context['results']['error'][] = t("Unable to import line @line of the CSV. Error was: '@message'.", [
        '@line' => $context['sandbox']['csv_line'],
        '@message' => $e
          ->getMessage(),
      ]);
    }

    // We're not finished until we reach the end of the CSV file.
    $context['finished'] = 0;
  }

  /**
   * Imports a single row of data from the CSV.
   *
   * @param array $headers
   *   The header row of the CSV file.
   * @param array $data
   *   A data row of the CSV file.
   *
   * @return
   *   The entity which has been updated by the CSV row.
   *
   * @throws \Exception
   *   Throws an exception if there was a problem finding or saving the entity.
   */
  public static function importCsvRow($headers, $data) {
    $entity_type_index = array_search('entity_type', $headers);
    $entity_id_index = array_search('entity_id', $headers);
    $language_index = array_search('language', $headers);
    $path_alias_index = array_search('path_alias', $headers);
    $entity = [];
    if ($entity_type_index !== FALSE && $entity_id_index !== FALSE) {
      $entity_type = trim($data[$entity_type_index]);
      $entity_id = trim($data[$entity_id_index]);
    }
    if ($path_alias_index !== FALSE) {
      $path_alias = trim($data[$path_alias_index]);
    }
    if (!(!empty($entity_type) && !empty($entity_id) || !empty($path_alias))) {
      throw new \Exception(t("Either both of entity_id and entity_type or path_alias must be specified."));
    }
    if ($language_index !== FALSE) {
      $langcode = trim($data[$language_index]);
    }

    // Resolve the path alias if that is being used.
    if (!empty($path_alias)) {
      $path = \Drupal::service('path_alias.manager')
        ->getPathByAlias($path_alias, $langcode ?? NULL);
      $route_parameters = Url::fromUri("internal:" . $path)
        ->getRouteParameters();
      $entity_type = key($route_parameters);
      if (!\Drupal::service('entity_type.manager')
        ->hasDefinition($entity_type)) {
        throw new \Exception(t("The path alias '@alias' does not correspond to an entity route.", [
          '@alias' => $path_alias,
        ]));
      }
      $entity_id = $route_parameters[$entity_type];
    }
    if (!\Drupal::service('entity_type.manager')
      ->hasDefinition($entity_type)) {
      throw new \Exception(t("The '@entity-type' entity type does not exist.", [
        '@entity-type' => $entity_type,
      ]));
    }

    // Load the entity.
    $entity = \Drupal::service('entity_type.manager')
      ->getStorage($entity_type)
      ->load($entity_id);
    if (!$entity) {
      throw new \Exception(t("No @entity-type with ID @entity-id was found.", [
        '@entity-type' => $entity_type,
        '@entity-id' => $entity_id,
      ]));
    }

    // Get the translation if the language is specified in the CSV data.
    if (!empty($langcode)) {
      if (!$entity
        ->hasTranslation($langcode)) {
        throw new \Exception(t("The @entity-type with ID @entity-id has no @language translation.", [
          '@entity-type' => $entity_type,
          '@entity-id' => $entity_id,
          '@language' => $langcode,
        ]));
      }
      $entity = $entity
        ->getTranslation($langcode);
    }
    $tags = [];
    foreach ($headers as $keys => $values) {
      if (trim($data[$keys]) == '_blank') {
        $tags[$values] = "";
      }
      else {
        $tags[$values] = $data[$keys];
      }
    }

    // Remove non meta tags fields.
    $metatag_machine_name = $tags['field_machine_name'];
    unset($tags['entity_id'], $tags['entity_title'], $tags['entity_type'], $tags['alias'], $tags['field_machine_name']);
    try {
      $entity
        ->set($metatag_machine_name, serialize($tags));
      $entity
        ->save();
    } catch (\Exception $e) {
      throw new \Exception(t("Error saving entity @entity-type @entity-id: '@message'.", [
        '@entity-type' => $entity_type,
        '@entity-id' => $entity_id,
        '@message' => $e
          ->getMessage(),
      ]));
    }
    return $entity;
  }

  /**
   * Batch finished callback for import.
   */
  public static function importFinish($success, $results, $operations) {
    $messenger = \Drupal::messenger();
    if ($success) {
      $message_entity_summary = [];
      foreach ($results['success'] as $entity_type => $entity_ids) {
        $entity_type_definition = \Drupal::service('entity_type.manager')
          ->getDefinition($entity_type);
        $message_entity_summary[] = \Drupal::service('string_translation')
          ->formatPlural(count($entity_ids), '1 ' . $entity_type_definition
          ->getSingularLabel(), '@count ' . $entity_type_definition
          ->getPluralLabel());
      }
      if ($message_entity_summary) {
        $messenger
          ->addMessage(t('Successfully updated entities: @summary.', [
          '@summary' => implode(', ', $message_entity_summary),
        ]));
      }
      foreach ($results['error'] as $error_message) {
        $messenger
          ->addError($error_message);
      }
    }
    else {
      $error_operation = reset($operations);
      $messenger
        ->addMessage(t('An error occurred while processing @operation with arguments : @args', [
        '@operation' => $error_operation[0],
        '@args' => print_r($error_operation[0], TRUE),
      ]), 'error');
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
MetatagImport::importCsvBatchOperation public static function Batch callback for parsing the CSV.
MetatagImport::importCsvRow public static function Imports a single row of data from the CSV.
MetatagImport::importFinish public static function Batch finished callback for import.
MetatagImport::validateCsvHeader public static function Validate the CSV header row.