Cómo crear un Field Formatter en Drupal 8

Author Top
enzo

Uno de los mayores cambios en la Drupal 8 es su integración con RESTful.

Ahora el modulo de Views estará integrado en el Core de Drupal 8, usted puede crear una vista que retorne una  respuesta JSON en pocos minutos, sólo necesita habilitar los módulos Views, Views UI and RESTful Web Services.

El problema que me he entrado se relaciona con los campos tipo imagen  y su formateador de imagen, el cual compatible con respuestas RESTful por que la salida del formateador es HTML requiriendo que el que consuma el servicio deba parsear los datos restando la utilidad del servicio.

Permítanme mostrarles una solución a este problema.

1. Crear Vista

Si se crea una vista con un display de tipo REST Export al igual que en cualquier vista se pueden seleccionar qué campos de su entidad desea procesar.

Ahora bien, si usted elige un campo de tipo imagen el formato por defecto es Imagen como se puede ver en la siguiente image.

Incluso si no se establece la opción de enlazar a la imagen original, obtendrá la imagen en formato HTML como podéis ver en el siguiente ejemplo de la respuesta JSON.

[
  {
    title: "Image sample # 3",
    field_image: " <img src="http://example.com/sites/default/files/styles/thumbnail/public/field/image/globe.jpg?itok=wmu3VCr6" width="100" height="75" alt="" typeof="foaf:Image" class="image-style-thumbnail" /> "
  },
  {
    title: "Image sample # 2",
    field_image: " <img src="http://example.com/sites/default/files/styles/thumbnail/public/field/image/sample_08.jpg?itok=X9N005N1" width="100" height="75" alt="" typeof="foaf:Image" class="image-style-thumbnail" /> "
  },
  {
    title: "Image sample # 1",
    field_image: " <img src="http://d$/sites/default/files/styles/thumbnail/public/field/image/sample_01.jpg?itok=UD1-QXTj" width="100" height="75" alt="" typeof="foaf:Image" class="image-style-thumbnail" /> "
  }
]

El resultado anterior se ha generado por una vista seleccionando artículos con imágenes, si desea importar esta vista en su sistema sólo tiene que descargar el archivo views_list.yml e importar el archivos mediante el Configuration Management  de Drupal 8 accediendo a la URL http://example.com/admin/config/development/configuration/single/import en su instalación de Drupal como se puede ver en la siguiente imagen.

Si quiere saber mas sobre el Configuration Management de Drupal 8 lo invito a ver el screencast Entendiendo el modulo Configuration Management de Drupal 8

Para resolver este problema y te mostraré cómo crear su propio Field Formatter y así satisfacer nuestras necesidades.

2. Crear un modulo

Voy a omitir la explicación acerca de cómo crear un módulo en Drupal 8 porque se podría generar con el proyecto Drupal console ejecutando el siguiente comando.

$ php console.phar generate:module

3. Crear Field Formatter

Si desea crear un nuevo Field Formatter se requiere añadir un nuevo archivo de clase situado en el módulo en la siguiente ruta

YOUR_MODULE/src/Plugin/Field/FieldFormatter/

Dentro de esta carpeta debe crear un archivo de clase por cada Field Fomatter que desee crear.

Voy a crear un nuevo archivo llamado ImageRawFormatter.php, voy a explicar cada parte de este archivo

3.1 Field Formatter Metadata

Antes de comenzar con el código que debemos definir algunas cosas requerido para un correcto funcionamiento.

3.2 Namespace

Tenemos que definir el namespace para el nuevo Field Formatter como se nuestra a continuación.

namespace Drupal\image_raw_formatter\Plugin\Field\FieldFormatter;

Drupal 8 implementa PSR-4 parte del  PHP Framework Interop Group

Esta especificación sigue el siguiente patrón

\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>

En nuestro caso la división significa:

  • NamespaceName: Drupal
  • SubNamespaceNames: \image_raw_formatter\Plugin\Field\FieldFormatter
  • NombreClase: ImageRawFormatter

3.2 Librerías requeridas

En este caso específico, tenemos que definir dónde estas clases que necesitamos para crear nuestro Field Fotmatter.

Drupal 8 utiliza Autoloader para cargas las clases pero tenemos que informar dónde están utilizando la instrucción use.

Veamos las librerías que necesitamos incluir.

use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatterBase;
use Drupal\image\Entity\ImageStyle;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldItemListInterface;
use \InvalidArgumentException;

3.3 Anotaciones

Drupal 8 cuanta con  varios Discovers la implementación de alguna piezas especificas de código en nuestro proyecto, por ejemplo, tenemos un Discover para detectar los Field Formatters declarados en nuestra aplicación y la forma de declarar un nuevo Field Formatter está utilizando Anotaciones.

A continuación puede encontrar un ejemplo de anotación para Field Formatter

/**
 * Plugin implementation of the 'image_raw_formatter' formatter.
 *
 * @FieldFormatter(
 *   id = "image_raw_formatter",
 *   label = @Translation("Image Raw"),
 *   field_types = {
 *     "image"
 *   }
 * )
 */

Como se puede ver hay que definir un id único con un label con traducción y se debe definir para que tipo de campos nuestro nuevo Field Formatter estará disponible.

3.4 Implementar Clase Field Formatter

Ahora tenemos que crear una clase que extienda la clase ImageFormatterBase como se puede ver en el siguiente fragmento de código.

class ImageRawFormatter extends ImageFormatterBase
{
}

3.5 Definir preferencias del Field Formatter 

Ahora debemos  añadir un método llamado settingsForm para definir las opciones de configuración de nuestro Field Formatter como puede apreciar a continuación.

/**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $image_styles = image_style_options(FALSE);
    $element['image_style'] = array(
      '#title' => t('Image style'),
      '#type' => 'select',
      '#default_value' => $this->getSetting('image_style'),
      '#empty_option' => t('None (original image)'),
      '#options' => $image_styles,
    );
    return $element;
  }

La implementación anterior permite al usuario seleccionar un Image Style específico  o simplemente devolver la imagen original.

3.6 Reporte sumario

Es importante informar a los usuarios finales la configuración actual del Formatter, para esto debemos implementar el método settingsSummary como se puede ver en el siguiente extracto de código.

/**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    $summary = array();
    $image_styles = image_style_options(FALSE);
    // Unset possible 'No defined styles' option.
    unset($image_styles['']);
    // Styles could be lost because of enabled/disabled modules that defines
    // their styles in code.
    $image_style_setting = $this->getSetting('image_style');
    if (isset($image_styles[$image_style_setting])) {
      $summary[] = t('Image style: @style', array('@style' => $image_styles[$image_style_setting]));
    }
    else {
      $summary[] = t('Original image');
    }
    return $summary;
  }

Como se puede ver el método anterior únicamente lee la configuración actual y la retorna para ser desplegada por la interfaz de usuario.

3.7 Renderizar campo usando el Field Formatter

Para finalizar debemos definir la forma en que queremos presentar el campo al que se le aplico nuestro Field Formatter basado en la configuración actual, para esto debemos implementar el método viewElements como se puede ver en el siguiente fragmento.

/**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items) {
    $elements = array();
    $image_style_setting = $this->getSetting('image_style');

    // Determine if Image style is required.
    $image_style = NULL;
    if (!empty($image_style_setting)) {
      $image_style = entity_load('image_style', $image_style_setting);
    }
    foreach ($items as $delta => $item) {
      if ($item->entity) {
        $image_uri = $item->entity->getFileUri();
        // Get image style URL
        if ($image_style) {
          $image_uri = ImageStyle::load($image_style->getName())->buildUrl($image_uri);
        } else {
          // Get absolute path for original image
          $image_uri = $item->entity->url();
        }
        $elements[$delta] = array(
          '#markup' => $image_uri,
        );
      }
    }
    return $elements;
  }

Si usted lee el código, la lógica sólo determina si la configuración requiere usar algún Image Style o si sólo se requiere la imagen original, en ambos escenarios la url es retornada

No necesitamos ninguna transformación a JSON porque eso está en manos del Display del View.

3.8 Probando el nuevo Field Formatter

Después de activar nuestro módulo personalizado sólo necesitamos modificar nuestro la Vista y editar el campo para utilizar el nuevo Raw Field Formatter como se puede ver en la siguiente imagen.

Después de guardar la vista y ejecutar de nuevo, la salida será totalmente diferente y perfecta para usar en los servicios REST para evitar análisis de HTML para extraer la ruta de la imagen. veamos una la salida de ejemplo.

[
  {
    title: "Image sample # 3",
    field_image: "http://drupal8b3.dev/sites/default/files/styles/thumbnail/public/field/image/globe.jpg?itok=wmu3VCr6"
  },
  {
    title: "Image sample # 2",
    field_image: "http://drupal8b3.dev/sites/default/files/styles/thumbnail/public/field/image/sample_08.jpg?itok=X9N005N1"
  },
  {
    title: "Image sample # 1",
    field_image: "http://drupal8b3.dev/sites/default/files/styles/thumbnail/public/field/image/sample_01.jpg?itok=UD1-QXTj"
  }
]

Puede descargar una implementación completa del Format Formatter en  https://github.com/enzolutions/image_raw_formatter.

Espero que esta entrada de blog haya sido útil.

Recursos: