SU-HKKU/cardinal_service_profile

View on GitHub
modules/cardinal_service_profile_helper/src/Form/CsvImporterForm.php

Summary

Maintainability
A
0 mins
Test Coverage
A
98%
<?php

namespace Drupal\cardinal_service_profile_helper\Form;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class CsvImporterForm.
 */
class CsvImporterForm extends FormBase {

  /**
   * Entity Type Manager Service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * File System service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * Cache service.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cache;

  /**
   * Database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('file_system'),
      $container->get('cache.default'),
      $container->get('database')
    );
  }

  /**
   * CsvImporterForm constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Entity Type Manager Service.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File system service.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   Caching service.
   * @param \Drupal\Core\Database\Connection $database
   *   Database connection service.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager, FileSystemInterface $fileSystem, CacheBackendInterface $cache, Connection $database) {
    $this->entityTypeManager = $entityTypeManager;
    $this->fileSystem = $fileSystem;
    $this->cache = $cache;
    $this->database = $database;
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'csv_importer_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['help'] = $this->getHelpText();
    $form['migration'] = [
      '#type' => 'select',
      '#title' => $this->t('Content Type'),
      '#required' => TRUE,
      '#options' => [
        'csv_spotlight' => 'Spotlight',
        'csv_opportunities' => 'Opportunity',
      ],
      '#empty_option' => $this->t('- Select -'),
    ];
    $form['csv'] = [
      '#type' => 'managed_file',
      '#title' => $this->t('CSV File'),
      '#upload_location' => 'temporary://',
      '#upload_validators' => ['file_validate_extensions' => ['csv']],
      '#required' => TRUE,
    ];
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#name' => 'import',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);
    if ($form_state->getTriggeringElement()['#name'] !== 'import') {
      return;
    }

    /** @var \Drupal\file\FileInterface $file */
    $file = $this->entityTypeManager->getStorage('file')
      ->load($form_state->getValue(['csv', 0]));
    if (!$file || !file_exists($file->getFileUri())) {
      $form_state->setError($form['csv'], $this->t('Unable to load file'));
      return;
    }

    $finput = fopen($file->getFileUri(), 'r');
    $header = fgetcsv($finput);
    fclose($finput);
    $migration = $this->getMigration($form_state->getValue('migration'));

    if (!$migration) {
      $form_state->setError($form['migration'], $this->t('No migration by that name. Please check the configuration.'));
      return;
    }

    $migration_fields = $migration->getSourceConfiguration()['fields'];
    array_walk($migration_fields, function (&$field) {
      $field = $field['selector'];
    });

    foreach ($header as $key => $header_value) {
      $header_value = preg_replace('/ .*?$/', '', $header_value);
      if (!isset($migration_fields[$key]) || $migration_fields[$key] != $header_value) {
        $form_state->setError($form['csv'], $this->t('Invalid headers order.'));
        return;
      }
    }

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $before_count = $this->getNodeCount();
    // Invalidate the migrations so that we can alter the plugin after setting
    // the state for the file path.
    Cache::invalidateTags(['migration_plugins']);
    $db_table = 'migrate_map_' . $form_state->getValue('migration');
    if ($this->database->schema()->tableExists($db_table)) {
      $this->database->truncate($db_table)->execute();
    }

    /** @var \Drupal\file\FileInterface $file */
    $file = $this->entityTypeManager->getStorage('file')
      ->load($form_state->getValue(['csv', 0]));
    $file_path = $this->fileSystem->realpath($file->getFileUri());
    $migration_id = $form_state->getValue('migration');

    // Set the cache for the csv file path for only 4 minutes since it will be
    // fast for the importer.
    $this->cache->set('migration:csv_path', [
      'migration' => $migration_id,
      'path' => $file_path,
    ], time() + 240);

    try {
      $migration = $this->getMigration($migration_id);
      stanford_migrate_execute_migration($migration, $migration->id());
      $file->delete();

      $count = $this->getNodeCount() - $before_count;
      $this->messenger()
        ->addStatus($this->t('Imported %count items.', ['%count' => $count]));
      // @codeCoverageIgnoreStart
    }
    catch (\Exception $e) {
      $this->logger('cardinal_service')
        ->error($this->t('CSV Importer failed: @message', ['@message' => $e->getMessage()]));
      $this->messenger()
        ->addError($this->t('Unable to import CSV. Review the logs for more information'));
    }
    // @codeCoverageIgnoreEnd

    $db_table = 'migrate_map_' . $form_state->getValue('migration');
    if ($this->database->schema()->tableExists($db_table)) {
      $this->database->truncate($db_table)->execute();
    }
  }

  /**
   * Get a list of links for the available importers.
   *
   * @return array
   *   Help text markup.
   */
  protected function getHelpText() {
    $replacements = [
      '@opportunities' => Link::createFromRoute($this->t('Opportunities'), 'cardinal_service.csv_template', ['migration' => 'csv_opportunities'])
        ->toString(),
      '@stories' => Link::createFromRoute($this->t('Spotlight'), 'cardinal_service.csv_template', ['migration' => 'csv_spotlight'])
        ->toString(),
    ];
    $help[] = [
      '#markup' => $this->t('Download an empty CSV template for @opportunities or @stories.', $replacements),
      '#prefix' => '<p>',
      '#suffix' => '</p>',
    ];
    $help[] = [
      '#markup' => $this->t('Leave the "existing_id" column empty unless you wish to update an existing piece of content. Be aware, this will overwrite all existing field information. For multiple value fields, separate each value with a semicolon.'),
      '#prefix' => '<p>',
      '#suffix' => '</p>',
    ];
    return $help;
  }

  /**
   * Get a migration object for the given migration id.
   *
   * @param string $migration_id
   *   Migration entity ID.
   *
   * @return \Drupal\migrate\Plugin\MigrationInterface|false
   *   Migration plugin object.
   */
  protected function getMigration($migration_id) {
    try {
      $migrations = stanford_migrate_migration_list();

      foreach ($migrations as $group) {
        if (isset($group[$migration_id])) {
          return $group[$migration_id];
        }
      }
      // @codeCoverageIgnoreStart
    }
    catch (\Exception $e) {
      return FALSE;
    }
    //@codeCoverageIgnoreEnd
  }

  /**
   * Get the number of nodes in the database.
   *
   * @return int
   *   Number of nodes.
   */
  protected function getNodeCount() {
    return (int) $this->database->select('node', 'n')
      ->fields('n')
      ->countQuery()
      ->execute()
      ->fetchField();
  }

}