Importing Files into Drupal 8 media_entity Bundles

The media_entity module is Drupal 8 (and its eventual inclusion in core) is great for managing these assets so that they can be reused, have fields, and included in nodes with the paragraphs module. For a new project, we’re importing faculty profile photos from a remote database into Drupal and want to be able to track the files locally so that we can take advantage of all these features, plus image styles and responsive images.

I found several examples that covered parts of this, but no single example that did everything I needed it to do, so this is a write-up of what I ended up using to solve this problem.

First, I wanted to create a separate directory inside the files folder to hold the images that this module would manage. I would like to be able to use file_prepare_directory() for this task, but in testing I wasn’t able to get it to create a directory with sufficient permissions. Calling file_prepare_directory() later in my procedural code would return false. So instead I’m using FileSystem::mkdir() which lets me pass the mode. This is based on the install process from the Libraries API module.


<?php
/**
* @file
* Contains install, uninstall and update functions for Middlebury Profile Sync.
*/
/**
* Implements hook_install().
*/
function middlebury_profile_sync_install() {
$directory = file_default_scheme() . '://middlebury_profile_sync';
// This would ideally use file_prepare_directory(), but that doesn't create
// the directory with sufficient write permissions and a check against the
// function in the module code later fails.
if (!is_dir($directory)) {
$file_system = \Drupal::service('file_system');
$file_system->mkdir($directory, 0777);
}
}

Next, I use system_retrieve_file() to import the file from the remote URL, create a File object for it and track it in Drupal’s managed files. There are a lot of examples out there that would have you use the httpClient class manually to fetch the file, or use standard PHP file functions and create the File object by hand, but I found using this function to be much simpler for what I was trying to do.

I then created the Media bundle based on the File I had just created. This code is based on this gist example. Lastly, I assign the Media object’s id to the Featured Image field of my node and then save it. I found this example of assigning media ids to fields to be helpful.


<?php
$photo_dir = file_default_scheme() . '://middlebury_profile_sync';
if ($profile->hasPhoto() && file_prepare_directory($photo_dir)) {
$photo = $profile->getPhoto();
$id = $profile->getId();
$destination = $photo_dir . '/' . $id . '.' . pathinfo($photo, PATHINFO_EXTENSION);
$file = system_retrieve_file($photo, $destination, TRUE, FILE_EXISTS_REPLACE);
$media = Media::create([
'bundle' => 'image',
'uid' => \Drupal::currentUser()->id(),
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
'status' => Media::PUBLISHED,
'field_image' => [
'target_id' => $file->id(),
'alt' => t('Photo of ' . $node->title->value),
'title' => t('Photo of ' . $node->title->value),
],
]);
$media->save();
$node->field_featured_image = ['target_id' => $media->id()];
}
$node->save();

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s