I do this all the time, making use of migration configuration entities (provided by the migrate_plus module). Define a migration plugin in your migration module's config/install directory, using the CSV source plugin from the migrate_source_csv module - omitting the 'path' source configuration, which will be filled in from the form. Let's say the ID of this migration is example_csv. Create a form with a file upload element (named 'csv_file' in this case), and in the submitForm() method:
public function submitForm(array &$form, FormStateInterface $form_state) {
$all_files = $this->getRequest()->files->get('files', []);
if (!empty($all_files['csv_file'])) {
$validators = ['file_validate_extensions' => ['csv']];
if ($file = file_save_upload('csv_file', $validators, 'public://', 0)) {
$csv_migration = Migration::load('example_csv');
$source = $csv_migration->get('source');
$source['path'] = $file->getFileUri();
$csv_migration->set('source', $source);
$csv_migration->save();
drupal_set_message($this->t('File uploaded as @uri.', ['@uri' => $file->getFileUri()]));
}
else {
drupal_set_message($this->t('File upload failed.'));
}
}
}
This updates the migration settings with the new file. You still have to run the migration using drush mi example_csv
to actually import the content.
Or add some code to the function to actually execute the import:
$migration_instance = \Drupal::service('plugin.manager.migration')->createInstance('example_csv');
$executable = new MigrateExecutable($migration_instance, new MigrateMessage());
try {
$migration_status = $executable->import();
}
catch (\Exception $e) {
\Drupal::logger('migrate_drupal_ui')->error($e->getMessage());
$migration_status = MigrationInterface::RESULT_FAILED;
}
if ($migration_status) {
drupal_set_message($this->t('Import Successful'));
}
else {
drupal_set_message($migration_status, 'error');
}