Si queremos que un contenido en Drupal tenga un fichero, es tan fácil como añadir un campo de tipo fichero a la entidad correspondiente, normalmente un nodo aunque podría ser otra entidad como por ejemplo un usuario. De esta forma obtendremos tanto un campo para subir el fichero en el formulario de edición de la entidad como control sobre cómo se mostrará el fichero al visitante.
Pero, ¿cómo hacerlo para un formulario de administración? Estos formularios no son entidades y por tanto no podemos añadirles campos tan fácilmente como lo haríamos con un contenido normal.
La solución pasa por realizar la gestión del fichero mediante código. Podemos simplemente añadir a nuestro formulario un elemento de formulario de tipo managed_file. El problema principal que nos encontramos al enviar el formulario es que el archivo va a ser considerado como temporal por Drupal. Esto significa que aunque el fichero se sube correctamente y estará disponible, pasadas unas horas drupal borrará el fichero durante las tareas rutinarias de limpieza, dejando la funcionalidad que use ese fichero en un estado inconsistente.
Para solucionar el problema, en el submit del formulario cargamos el archivo y mediante la función setPermanent indicamos a Drupal que el fichero debe ser conservado. Básicamente estamos modificando el status a 1 (valor de la constante FILE_STATUS_PERMANENT definida en el fichero file.inc). Si no queremos invocar el Field API de Drupal, tenemos el recurso de la función file_unmanaged_save_data, que no vamos a tratar aquí.
A continuación se muestra un ejemplo de código:
class MyModuleSettingsConfigurationForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'my_module_admin_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'my_module.settings',
];
}
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL) {
$config = $this->config('my_module.settings');
$allowed_ext = 'doc docx pdf jpg';
$max_upload = 25600000;
$form['my_file_fid'] = [
'#type' => 'managed_file',
'#title' => $this->t('Document'),
'#description' => $this->t('Valid extensions: @allowed_ext', ['@allowed_ext' => $allowed_ext]),
// @NOTE: add your folder here!
'#upload_location' => 'public://test/',
'#multiple' => FALSE,
'#required' => TRUE,
// No need array, $config->get already give us an array format.
'#default_value' => $config->get('my_file_fid'),
'#upload_validators' => [
'file_validate_extensions' => [
$allowed_ext,
],
'file_validate_size' => [
$max_upload,
],
],
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$fid = reset($form_state->getValue('my_file_fid'));
$file = File::load($fid);
// We don't want to lose it after 6 hours (default Drupal's value), since it is going to be used
// in some forms (will be downloaded).
$file->setPermanent();
$file->save();
// Save configuration settings.
$this->config('my_module.settings')
->set('my_file_fid', $values['my_file_fid'])
->save();
}
}
El método buildForm construye el formulario añadiendo el campo de subida de fichero (my_file_fid). En el submit del formulario consultando el valor de dicho campo obtenemos el fid del fichero. Solo necesitamos cargarlo y hacerlo permanente como hemos comentado antes.
Tećnicamente, mediante el método:
$file->setPermanent();
estamos indicando que el archivo no debe ser borrado por Drupal de la tabla file_managed, aunque ningún módulo lo esté usando (tabla file_usage). El uso de la tabla file_usage, también queda fuera de este post, pero tenlo en cuenta si el archivo va a ser usado por algún módulo (o el tuyo).
Aunque aquí lo hemos explicado para un formularios de administración, este método lógicamente sirve para cualquier formulario que no sea de una entidad (dado que las entidades sí gestionan automáticamente los ficheros subidos) al que le añadamos un campo de subida de ficheros.