Managed file form element

The other obligatory method we need to implement is getConfigurationForm(), by which we define the form elements needed to configure this particular plugin. Here, we will create the file field allowing users to upload the CSV file:

/** 
 * {@inheritdoc} 
 */ 
public function getConfigurationForm(\Drupal\products\Entity\ImporterInterface $importer) { 
  $form = []; 
  $config = $importer->getPluginConfiguration(); 
  $form['file'] = [ 
    '#type' => 'managed_file', 
    '#default_value' => isset($config['file']) ? $config['file'] : '', 
    '#title' => $this->t('File'), 
    '#description' => $this->t('The CSV file containing the product records.'), 
    '#required' => TRUE, 
  ]; 
 
  return $form; 
}  

The form element type is called managed_file (implemented by the ManagedFile form element class). The rest of the definition is straightforward. However, there are a couple of problems.

First, by default, using this form element, files are uploaded to the temporary:// filesystem of Drupal. Since we don't want that, we need to specify an upload location:

'#upload_location' => 'public://'  

The root of our public files folder will suffice for this example as we assume the file does not contain any sensitive information. If so, we could upload it to the private:// one and control who gets access. We'll talk about how that works later in the chapter.

Second, by default, using this form element, the allowed file extensions for upload are limited to jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp. So if we want to allow CSV files, we need to specify the extension in a list of allowed upload extensions. And we do this by overriding the default upload validators:

'#upload_validators' => [ 
  'file_validate_extensions' => ['csv'], 
],  

This is an array of validator callbacks we want Drupal to run when the file is uploaded. And allowing only CSV files is enough for our purposes. But another handy validator we could use is file_validate_size(). Moreover, we can implement hook_file_validate() ourselves and perform any custom validation to the files being uploaded. So that's also something to keep in mind when dealing with validation files that don't belong to your modules.

With this, our plugin configuration form is in place; it looks something like this:

However, there is still something we need to do in order for the uploaded file to be managed properly. When using this form element, the file gets correctly uploaded and a record is added to the file_managed table. So we get our File entity. However, its status is not permanent because it doesn't have any usages. There are no records for it in the file_usage table. How could there be? So what we need to do is handle that ourselves and basically tell Drupal that the file uploaded in this form is used by the respective Importer configuration entity. And to do this, we need to know when the file is saved onto the entity, changed, and deleted.

With this, we can also learn about something very important that we skipped in Chapter 6, Data Modeling and Storage, and Chapter 7, Your Own Custom Entity and Plugin Types: entity CRUD hooks. But right before we jump into that, let's not forget about the configuration schema of this new configuration item—the file key of the plugin configuration:

products.importer.plugin.csv: 
  type: mapping 
  label: Plugin configuration for the CSV importer plugin 
  mapping: 
    file: 
      type: sequence 
      label: File IDs 
      sequence: 
        type: integer 
        label: CSV File ID  

We are doing the same as we did for the url key of the JSON importer but, in this case, we need to account for the fact that file is actually an array. So we define it as a sequence whose individual items are integers. Feel free to check Chapter 6, Data Modeling and Storage, for more information on configuration schemas whenever you need a reminder.