En este caso vamos a explicar como crear un campo personalizado en Drupal 7, lo que vamos hacer básicamente es combinar varios tipos de campos predefinidos por Drupal en uno solo, podemos ser muy flexibles, por ejemplo esto puede ser una mezcla de select, textbox, checkbox, date, etc. según lo que haga falta, este grupo de valores serán guardados en la base de datos usando la lógica de Drupal, por lo que no debemos preocuparnos por el modelado de la tabla y las consultas. Bien antes de empezar debemos entender un poco lo que conforma cada campo (field) y así podremos orientarnos mejor cuando estamos programando dicho módulo.
Lo primero que debemos saber es que un campo (field) esta compuesto por uno o varios Widget y Formats, ahora ¿Qué es cada uno de ellos?; bien el Widget seria en este caso una forma de mostrar un campo según su tipo de captura, esto lo podemos ver en la pestaña “Manage Fields”:
Luego existen los Formats, que es como queremos presentar los datos cuando no es un formulario, y lo podemos seleccionar en la pestaña “Manage Display”:
Para el ejemplo que se va a desarrollar en este post, se necesita de un campo que se repite varias veces, está compuesto por dos campos de texto y un campo select, uno es el nombre de la propiedad, el valor de la misma y un estado por cada elemento, como lo vemos en la siguiente imagen:
Ahora que tenemos una mejor idea, podemos hablar de los detalles técnicos, el desarrollo de un campo adicional es en base a un módulo, este lo llamaremos “property”:
Primero definimos la información del mismo, luego creamos la estructura del esquema o tabla donde alojamos los datos, por ultimo creamos todas las funciones necesarias (hooks) para darle forma y comportamiento a los campos.
Primero debemos crear un módulo con los siguientes archivos:
  1. property.info: Aquí se define la información del módulo, no hay nada nuevo que explicar.
  2. property.install: Contiene la estructura de la tabla de la base de datos que se va a crear una ves asignado el campo a un tipo de contenido, cada valor del campo será guardado en dicha tabla, el tipo de datos y las propiedades que se le definen deben corresponder a las especificaciones de Drupal o en caso particular al manejador de base de datos, para este caso usamos MySQL.
  3. property.module: Contiene todas las funciones de tipo hook para darle forma y comportamiento a los campos. Si queremos más información de estas funciones, ponemos a tu disposición los siguientes enlaces:
Dentro del archivo property.info agregamos las siguientes líneas:
package = "Demo"
name = "Property Field"
description = "Campo combinado para agregar propiedades, valores y un estado."
version = "7.x-0.1"
core = "7.x"
files[] = property.module

Dentro del archivo property.install agregamos las siguientes líneas:
function property_field_schema($field) {
  switch($field['type']) {
    case 'property':
      $columns = array(
        'name'   => array(
          'type'     => 'varchar',
          'length'   => '32',
          'not null' => TRUE,
        ),
        'value'      => array(
          'type'     => 'varchar',
          'length'   => '32',
          'not null' => TRUE,
        ),
        'status'     => array(
          'type'     => 'int',
          'size'     => 'small',
          'not null' => TRUE,
          'default'  => 1,
        ),
      );
      $indexes = array(
        'value' => array('value'),
      );
      break;
  }
  
  return array(
    'columns' => $columns,
    'indexes' => $indexes,
  );
}
Drupal tiene su propio modelo de datos escalable, aquí mostramos como queda la tabla o esquema creado dentro del manejador de base de datos MySQL, ahora mostramos una imagen de como quedaría el esquema una ves creado:

Dentro del archivo property.module agregamos las siguientes líneas:

function property_field_info() {
  return array(
    'property'    => array(
      'label'             => t('Property'),
      'description'       => t('Permite agregar valores de las Bolsas'),
      'default_widget'    => 'property',
      'default_formatter' => 'property',
    ),
  );
}

function property_field_widget_info() {
  return array(
    'property' => array(
      'label' => t('Text fields'),
      'field types' => array('property'),
    ),
  );
}

function property_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  switch ($instance['widget']['type']) {
    case 'property':
      $element['name']   = array(
        '#type'          => 'textfield',
        '#title'         => 'Nombre',
        '#description'   => $element['#description'],
        '#default_value' => empty($items[$delta]['name'])? '' : $items[$delta]['name'],
        '#required'      => $element['#required'],
        '#delta'         => $delta,
      );
      $element['value'] = array(
        '#type'          => 'textfield',
        '#title'         => 'Valor',
        '#description'   => $element['#description'],
        '#default_value' => empty($items[$delta]['name'])? '' : $items[$delta]['name'],
        '#required'      => $element['#required'],
        '#delta'         => $delta,
      );
      $element['status'] = array(
        '#type'          => 'select',
        '#title'         => 'Estado',
        '#required'      => $element['#required'],
        '#description'   => $element['#description'],
        '#default_value' => empty($items[$delta]['name'])? '' : $items[$delta]['name'],
        '#options'       => array(
                                  1 => t('Enable'),
                                  2 => t('Disable')),
      );
      break;
  }

  return $element;
}

function property_field_is_empty($item, $field) {
  switch ($display['type']) {
    case 'property':
      if(empty($item['value'])) {
        return TRUE;
      }
      break;
  }
  
  return FALSE;
}

function property_field_formatter_info() {
  return array(
    'property' => array(
      'label' => t('Fields values'),
      'field types' => array('property'),
    ),
  );
}
Para entender un poco el código anterior vamos a explicar que hace cada función:
  • hook_field_info: Contiene toda la información básica del campo, como su nombre, descripción y cuales son los widget y formatter definidos por defecto cuando sean invocados.
  • hook_field_widget_info: Contiene la información básica de cada widget que tiene el campo.
  • hook_field_widget_form: Contiene toda la estructura de los diversos campos o cualquier componente que se desa usar como un solo campo en la captura.
  • hook_field_is_empty: Contiene nuestra propia lógica que valida el campo para que no se considere vacío.
  • hook_field_formatter_info: Contiene la información de cada forma de mostrar los datos en la vista.
  • hook_field_formatter_view: Contiene la lógica que manipula los datos para ser mostrados en la vista.
Todo lo que explique aquí no es el todo para crear campos, podemos incluir CSS / JS, atributos del HTML, manipular los datos antes de guardarlos o mostrarlos, agregar más de un widget y formatter, al saber todo esto quien sabe cuantas cosas más podemos hacer.