7

What’s the best solution for importing CSV files into nodes or entities in Drupal 8 that offers a UI so content editors can import regularly?

I’ve heard D8 Migrate works well but I understand there’s currently no UI for the import process.

CSV import via Feeds doesn’t appear to be ready yet.

2
  • 3
    I tried a basic test using the latest Feeds D8 release and applying the patch at #4 from this thread drupal.org/node/2443471#comment-9723715 and it appears to work correctly. See how it goes for more complex fields. Commented Jan 8, 2017 at 4:34
  • I should think that Drupal is saving wordpress all the time or Drupal is still developing. Commented Oct 14, 2017 at 23:41

5 Answers 5

10

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');
      }
0
1

It is probably better and faster to use Feeds, but since the D8 version is still under development; alternatively, you could use Excel + VBA (Visual Basic for Applications, comes with Excel) + Internet Explorer 11.

Here an example how you can import your CSV content using VBA.

For example, lets assume you want to import this and create new nodes with the info from your CSV:

enter image description here

Here is a sample VBA code:

Sub Drupal_Import()

Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")

Dim x As Integer

For x = 2 To 4 'this controls which rows get added, so only 2 to 4

myURL = "https://rgr79.ply.st/node/add/article"

With IE
.Visible = True 'makes your Internet Explorer window visible.
.navigate myURL
End With

Do
el = vbNullString
On Error Resume Next
Set HTML = IE.document
el = HTML.getElementsByClassName("visually-hidden")(1).innerText
DoEvents
Loop While el = vbNullString

'the above loops until the visualy-hidden class is detected, which means the edit form has been loaded

Application.Wait (Now + TimeValue("00:00:03")) 'tells the program to wait 3 secs.

Set HTML = IE.document

HTML.getElementById("edit-title-0-value").Value = Cells(x, 1).Value 'here the 1 is the Y (so 1 is Column A)

HTML.getElementById("edit-body-0-value").Value = Cells(x, 2).Value 'here the 2 is the Y (so 2 is Column B)

Cells(x, 3).Value = "Done" 'here we use the 3rd column (Column C) and mark it as Done to keep track.

HTML.getElementsByClassName("button js-form-submit form-submit")(1).Click 'clicks the submit button

Application.Wait (Now + TimeValue("00:00:00")) 'here I have a wait for 0, increase it to 2 or 3 if you see your VBA get stuck after submitting a node.

Do
el = vbNullString
On Error Resume Next
Set HTML = IE.document
el = HTML.getElementsByClassName("messages messages--status")(0).innerText
DoEvents
Loop While el = vbNullString

'all the above does is loops the code until the drupal message is detected, which means the node was loaded, after the submit.

Next x

End Sub

Make sure you change the domain name in myURL = "https://rgr79.ply.st/node/add/article" line to your domain. I am using a simplytest.me domain, if you can't already tell.

How to add the VBA code?

Click on the Developer tab and then on Visual Basic icon (or ALT + F11)

enter image description here

and Paste the code inside of Sheet1(Sheet1)

enter image description here

Now in the toolbar, click on tool and then References

enter image description here

You will need to scroll, find and checkmark

  • Microsoft HTML Object Library
  • Microsoft Internet Controls

enter image description here

Note: I know it works with Internet Explorer 11, not sure if it works with the new Microsoft Edge browser.

Now you are ready to run the script. You can do so by clicking the Play button

enter image description here

You could also run it by clicking the Macros Icon (see image2), but I prefer to do it from the VBA window.

So you hit the play button and an IE window automatically opens and you see this:

enter image description here

Oh yes, you forgot to log in, lol.

So you proceed to login to Drupal and then you close explorer (since cookies history saves your login) and plan on hitting the play button again. But you are not able to... you see the play button greyed out and not able to make any changes to the VBA code... What is going on?

Well your code is still running, so you need to hit the Stop (reset) button.

enter image description here

Now you can click the play button again and joy the world of automatization.

Important

If you plan to insert stuff to the Body field (like we are doing in this example), since Drupal 8 uses CKEditor for this field and CKEditor is JS, we cannot target a div class or ID; thus, we cannot add content inside CKEditor.

Fortunately, there is a work around. Make sure your IE 11 Security settings are set to High, this automatically will block all JS. Therefore, CKeditor will not load, and the body field will be just like the other fields.

enter image description here


If you need to edit nodes example:

enter image description here

Sub Drupal_Edit()

Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")

Dim x As Integer

For x = 2 To 4 'this controls which rows get added, so only 2 to 4

myURL = "https://rgr79.ply.st/node/" & Cells(x, 3) & "/edit"

With IE
.Visible = True 'makes your Internet Explorer window visible.
.navigate myURL
End With

Do
el = vbNullString
On Error Resume Next
Set HTML = IE.document
el = HTML.getElementsByClassName("visually-hidden")(1).innerText
DoEvents
Loop While el = vbNullString

'the above loops until the visualy-hidden class is detected, which means the edit form has been loaded

Application.Wait (Now + TimeValue("00:00:04")) 'tells the program to wait 3 secs.

Set HTML = IE.document

HTML.getElementById("edit-title-0-value").Value = Cells(x, 1).Value 'here the 1 is the Y (so 1 is Column A)

HTML.getElementById("edit-body-0-value").Value = Cells(x, 2).Value 'here the 2 is the Y (so 2 is Column B)

Cells(x, 4).Value = "Done" 'here we use the 4th column (Column D) and mark it as Done to keep track.

HTML.getElementsByClassName("button js-form-submit form-submit")(1).Click 'clicks the submit button

Application.Wait (Now + TimeValue("00:00:00")) 'here I have a wait for 0, increase it to 2 or 3 if you see your VBA get stuck after submitting a node.

Do
el = vbNullString
On Error Resume Next
Set HTML = IE.document
el = HTML.getElementsByClassName("messages messages--status")(0).innerText
DoEvents
Loop While el = vbNullString

'all the above does is loops the code until the drupal message is detected, which means the node was loaded, after the submit.

Next x

End Sub
3
  • Thanks... that's an interesting option. I think I'll need a simpler solution from a UI perspective though. Commented Jan 8, 2017 at 11:11
  • 2
    Won't work if users don't have IE and seems unnecessarily complex. I think the way to go would be a custom admin confirm form, batch op, and migrate process... @mikeryan would be the person to ask.
    – Kevin
    Commented Jan 8, 2017 at 16:36
  • You are killer. Commented Oct 14, 2017 at 23:41
1

If you don't need to store CSV values in entity fields take a look at https://www.drupal.org/project/viewer

0

The above is working fine for me but Migratin::Load() and save() method is not available in Drupal 8 3.x. I have done some changes in above @Mike Ryan suggested code. Here is the working code on form sumbit handler.

public function submitForm(array &$form, FormStateInterface $form_state) {
$all_files = $form_state->getValue('csv_file');
if (!empty($all_files)) {
  $file = file_load($all_files[0]);
  if (!empty($file)) {
    $csv_migration = \Drupal::service('plugin.manager.migration')->createInstance('panalist_migration');
    $source = $csv_migration->get('source');
    $source['path'] = $file->getFileUri();
    $csv_migration->set('source', $source);
    drupal_set_message($this->t('File uploaded as @uri.', ['@uri' => $file->getFileUri()]));
    $executable = new MigrateExecutable($csv_migration, 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');
    }
  }
  else {
    drupal_set_message($this->t('File upload failed.'));
  }
}
0

Now you can use Content Import module (https://www.drupal.org/project/contentimport). Heres a post on how to use it: https://www.ostraining.com/blog/drupal/content-import-module/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.