Curso de desarrollo de módulos con Drupal 7 (IX): Creación y gestión de Entidades (Parte I)

Como siempre crearemos un nuevo módulo pero esta vez definiremos una entidad que manejaremos desde Drupal. Este caso es un poco más complejo que el de la creación de un nuevo tipo de contenido, debido a que deberemos realizar todas las funcionalidades de gestión de información para esta nueva entidad.
Crearemos un carpeta para el módulo llamada “entity_example” y un nuevo fichero .info con el siguiente contenido:

; $Id: ajax_example.info,v 1.3 2010/08/11 23:16:27 rfay Exp $

name = Ejemplo de Entidad
description = Módulo de ejemplo que muestra como usar el API de Entidades de Drupal
core = 7.x
package = Modulos de ejemplo
files[] = entity_example.module
files[] = entity_example.install

Definiendo el Modelo de la BBDD

Como se vió en la tercera entrega es posible definir un esquema de bbdd para que nuestro módulo disponga de un conjunto de tablas y campos sobre los que manejar la información que necesita gestionar. Generamos un nuevo fichero entity_example.install, pero esta vez sólo tendremos la implementación de hook_squema():

<?php
// $Id$

/**
 * @file
 * Install/Update/Uninstall functions for entity_example module.
 */

/**
 * Implements hook_schema().
 *
 * @see hook_schema()
 * @link schemaapi Schema API @endlink
 */
function entity_example_schema() {
  $schema[‘example_entity’] = array(
    ‘description’ => ‘The base table for example entity’,
    ‘fields’ => array(
      ‘eeid’ => array(
        ‘description’ => ‘The primary identifier for a entity.’,
        ‘type’ => ‘serial’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
      ),
      ‘title’ => array(
        ‘description’ => ‘The title of this entity, always treated as non-markup plain text.’,
        ‘type’ => ‘varchar’,
        ‘length’ => 255,
        ‘not null’ => TRUE,
        ‘default’ => ”,
      ),
      ‘perm’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘size’ => ‘tiny’,
        ‘description’ => ‘Whether the example entity is permanent(1) or not(0).’,
      ),
      ‘dstart’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘Start date of example entity. Format: Ymd’,
      ),
      ‘dend’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘End date of example entity. Format: Ymd’,
      ),
      ‘weight’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘The weight of this example entity.’,
      ),
    ),
    ‘primary key’ => array(‘eeid’),
    ‘indexes’ => array(
      ‘perm’ => array(‘perm’),
      ‘dstart’ => array(‘dstart’),
      ‘dend’ => array(‘dend’),
    ),
  );
  return $schema;
}

Como podemos ver en modelo es sencillo:

  • Campos:
    • eeid: numero entero, sin signo, no puede ser null y clave primaria de la tabla
    • title: varchar de 255 caracteres que almacenará el título
    • perm: entero pequeño (tiny) que almacena valores 0 y 1
    • dstart y dend: fecha inicio y fin 
    • weigth: peso de la entidad.
  • Clave primaria: campo eeid
  • indices:
    • perm
    • dstart
    • dend

Hasta aquí el modelo está definido, cuando se active el módulo se generará la tabla ‘example_entity’ con las especificaciones que le hemos definido.

Añadir Permisos, hook_permission()

Ahora añadiremos el fichero .module donde colocaremos el resto del código del módulo.
Debido a que estamos manejando una nueva entidad será necesario aplicar nuevos permisos con el módulo para así poder controlar mejor que operaciones pueden realizar los usuarios. Para ello utilizaremos el hook_permission().  aquí tenemos un ejemplo de uso para el módulo:
/**
 * Implements hook_permission().
 */
function entity_example_permission() {
  return array(
    ‘administer example entity’ =>  array(
      ‘title’ => t(‘Administer example entity’),
      ‘restrict access’ => TRUE,
    ),
  );
}
Como puede verse el hook debe devolver un array asociativo con un elemento para cada nuevo permiso. En este caso es “administer example entity”, donde configuramos que su valor es otro array con el título (title) y si restrigiremos el acceso a la funcionalidad (restrict access), que en este caso lo haremos.

Definición de la entidad: hook_entity_info()

La definición de la entidad se realiza a través del hook_entity_info(), veamos un ejemplo de implementación para este módulo:
/**
 * Implementa hook_entity_info().
 */
function entity_example_entity_info() {
  $return = array(
    ‘entity_example’ => array(
      ‘label’ => t(‘Example entity’),
      ‘base table’ => ‘example_entity’,
      ‘uri callback’ => ‘entity_example_uri’,
      ‘fieldable’ => TRUE,
      ‘entity keys’ => array(
        ‘id’ => ‘eeid’,
      ),
      ‘bundles’ => array(
        ‘entity_example’ => array(
          ‘label’ => t(‘Example entity’),
          ‘admin’ => array(
            ‘path’ => ‘admin/structure/entity_example’,
            ‘access arguments’ => array(‘administer example entity’),
          ),
        ),
      ),
    ),
  );
  return $return;
}
Como puede verse se devuelve un array asociativo son los datos principales de las entidades que queremos definir. En este caso se está definiendo la entidad ‘entity_example’ como índice del array principal. Dentro del array que define cada entidad nos encontramos con los siguientes campos:

  • label: etiqueta principal
  • base_table: nombre de la tabla que gestiona la entidad
  • uri callback: nombre dela función que sabe manejar las URL’s de la entidad, nos permitirá pasar por URL la entidad que manejamos.
  • fieldable: booleano que define si la entidad es manejable mediante campos (Field API)
  • entity keys: array de claves primarias de la entidad, en este caso es el campo eeid
  • bundles: conjuntos de entidades que queremos gestionar, en este caso solo daremos una de alta correspondiendo a la entidad que hemos dado de alta ‘entity_example’
    • nombre_entidad: aqui referenciamos el bundle con la entidad
      • label: etiqueta de la entidad para el bundle
      • admin: array con los datos de la administración del bundle para la entidad
        • path: rura de acceso a la administración
        • access arguments: array de permisos necesarios para la gestión de la entidad

A continuación debemos programar la función que permite manejar las rutas de la entidad para Drupal:
/**
 * Entity uri callback.
 */
function entity_example_uri($entity_example) {
  return array(
    ‘path’ => ‘entity_example/’ . $entity_example->eeid,
  );
}
Como puede verse nos pasan la entidad entera y devolvemos el path con el ID de la entidad que nos pasan en el campo “path” concatenado con la rura principal “entity_example/”.
Tambiñen deberemos sobreescribir el hook_admin_paths(), para que funcionen correctamente las rutas de edición de entidades:
/**
 * Implements hook_admin_paths().
 */
function entity_example_admin_paths() {
  $paths = array(
    ‘entity_example/*/edit’ => TRUE,
  );
  return $paths;
}
 De esta manera cambiaremos algunos paths administrativos del módulo, en concreto el que tiene que ver con la edición de elementos de la entidad.
Esto se entenderá mucho mejor cuando veamos los enlaces generados con el hook_menu().

Entradas del menú para hacer el CRUD

Como se ha mencionado anteriormente será necesario dar de alta todas las funcionalidades de la gestión de la nueva entidad y sus enlaces en el menú, como siempre, lo haremos a través del hook_menu():

/**
 * Implements hook_menu().
 */
function entity_example_menu() {
  $items[‘entity_example/%entity_example’] = array(
    ‘title’ => ‘Example entity’,
    ‘title callback’ => ‘entity_example_page_title’,
    ‘title arguments’ => array(1),
    ‘page callback’ => ‘entity_example_page’,
    ‘page arguments’ => array(1),
    ‘access arguments’ => array(‘access content’),
    ‘type’ => MENU_CALLBACK,
  );
  $items[‘entity_example/%entity_example/view’] = array(
    ‘title’ => ‘View’,
    ‘type’ => MENU_DEFAULT_LOCAL_TASK,
  );
  $items[‘entity_example/%entity_example/edit’] = array(
    ‘title’ => ‘Edit’,
    ‘page callback’ => ‘drupal_get_form’,
    ‘page arguments’ => array(‘entity_example_form_edit’, 1),
    ‘access arguments’ => array(‘administer example entity’),
    ‘type’ => MENU_LOCAL_TASK,
    ‘weight’ => 10,
  );

  $items[‘admin/structure/entity_example’] = array(
    ‘title’ => ‘Example entities’,
    ‘description’ => ‘Manage example entities on your site.’,
    ‘access arguments’ => array(‘administer example entity’),
    ‘page callback’ => ‘entity_example_page_admin’,
    ‘page arguments’ => array(‘list’),
    ‘weight’ => -10,
  );

  $items[‘admin/structure/entity_example/list’] = array(
    ‘title’ => ‘List’,
    ‘type’ => MENU_DEFAULT_LOCAL_TASK,
    ‘weight’ => -10,
  );

  $items[‘admin/structure/entity_example/create’] = array(
    ‘title’ => ‘Add example entity’,
    ‘page arguments’ => array(‘create’),
    ‘access arguments’ => array(‘administer example entity’),
    ‘type’ => MENU_LOCAL_ACTION,
  );

  return $items;
}

Como podemos ver estamos gestionando muchos enlaces, así que explicaremos uno a uno su significado para entender mejor su funcionamiento, en la siguiente entrega.

Referencias:

Licencia Creative Commons

Curso de Desarrollo de Módulos con Drupal 7 (VIII): Features

El módulo Features es una ayuda a los desarrolladores e implantadores de sitios web hechos con Drupal. Una vez instalado y activado podremos interactuar con él a través del path “admin/structure/features”…

como puede verse en la captura, disponemos del listado principal de Features disponibles en nuestro Drupal. A continuación pulsaremos en la pestaña “Create Feature”…

De esta manera podemos configurar la nueva Feature, introduciendo los datos en el formulario:

  • Nombre.
  • Descripción.
  • Package: nombre del paquete donde encontraremos el módulo que contiene la Feature.
  • Versión.
  • URL of Update XML: url de actualización de la feature
  • Edit components: aquí seleccionaremos los distintos componentes de nuestro Drupal que queremos añadir a la feature.

Según vayamos seleccionando los componentes, se nos irán desplegando distintas opciones desde los distintos módulos instalados en nuestro drupal, por lo que podremos seleccionar, desde Tipos de Contenidos a Views para irlas añadiendo a nuestra feature…

Una vez hayamos terminado de añadir componentes a la feature, pulsaremos en el botón “Download Feature”, esto nos permitirá descargar aquellos componentes como un fichero desplegable en otros Drupal instalados. El fichero generado por Features es un fichero comprimido que incluye aquellos componentes seleccionado en forma de código.

Despliegue de la Feature por primera vez

Para instalar y configurar la feature se realiza, inicialmente de la misma manera que se instla un módulo, subiendolo a través del interfaz, o simplemente, descomprimiendo el fichero descargado en el directorio “sites/all/modules”. 
Para poder realizar correctamente la activación será necesario seguir una serie de pasos:

  • Realizar un backup completo de la instalación
  • Verificar que los módulos de los que depende la feature han sido correctamente instalados y activados.

Una vez instalado deberemos activar el nuevo módulo…

Una vez activado, nos moveremos de nuevo a “admin/structure/features” y nos encontremos con la nueva feature…

Procederemos a activarla, mrcando la checkbox al lado de la feature y pulsando en el botón “Guardar la Configuración”.Debería aparecer como “Predeterminado” por lo que ya deberíamos disponer de todas las funcionalidades y Componentes desplegados en el sitio web.

Exportando modificaciones de la feature

Una vez desplegada la feature en el sitio en producción será necesario poder realizar actualizaciones de la funcionalidad y de los componentes integrados en la misma, para ello será necesario reempaquetar la feature con los cambios y realizar el despliegue de nuevo de la feature.
Para ello, el primer paso es modificar la feature, desde el listado de features, pulsamos en el enlace “Recreate” al lado de la feature que queremos modificar.
Añadiremos los nuevos componentes y volveremos a descargar la feature.
Posteriormente volveremos a subir la feature a la instancia de drupal en producción. Y el administrador podrá elegir que cambios quiere Actualizar “Update” o revertir “Revert”.

Referencias: 

Licencia Creative Commons

Curso de Desarrollo de Módulos con Drupal 7 (V): Creación de un Tipo de Contenido

Una de las tareas básicas a la hora de generar un nuevo módulo es la generación de nuevos tipos de contenido. Para ello tendremos que utilizar el API de Drupal de los módulos Node, Field e Image.

Primeros pasos

Como siempre generaremos un nuevo directorio en el directorio “sites/all/modules/custom” con el nombre del módulo y generaremos el fichero .info con una información similar a la siguiente:
name = Ejemplo de Nodo
description = Ejemplo de módulo que crea un tipo de contenido y usa el api de Field
package = Módulos de ejemplo
core = 7.x
dependencies[] = image

El fichero .install

Ahora crearemos el fichero .install para crear y destruir el nuevo tipo de contenido en la instalación y la desinstalación respectivamente. 
Para ello deberemos realizar las implementaciones de los hook_install y hook_uninstall.
Nota: no deberemos realizar la implementación del hook_schema porque utilizaremos los propios API’s de Drupal para almacenar y consultar los datos, incluidos los intefaces de usuario (UI), porque los tipos de contenido ya tienen unos flujos de manejo de la información bien definidos en Drupal.

El hook_install()

Dentro del fichero .install introducimos la implementación de la instalación:
/**
 * Implementa el hook_install().
*
 * Este hook es llamado cuando el usuario activael módulo por primera vez
 *
 * que hace?
 * – Añade el campo body
 * – Configura el campo body
 * – Crea campos para el color la cantidad y una imagen
 * – Crea las instancias para el campo color, cantidad e imageninstances.
 *
 * @see node_type_set_defaults()
 * @see field_info_instance()
 * @see field_update_instance()
 * @see field_create_field()
 * @see field_create_instance()
 * @ingroup node_example
 */
function node_example_install() {
  // Durante la instalación, la función de traducción t() no esta disponible, así que usamos get_t()
  // para almacenar el nombre de la función de traducción
  $t = get_t();

  // Definimos el tipo de nodo (tipo de contenido) mediante un array asociativo.
  $node_example = array(
    ‘type’ => ‘node_example’,
    ‘name’ => $t(‘Ejemplo de nodo’),
    // el parámetro ‘base’ le dice a Drupal el nombre que debe anteponer a las funciones hook
    ‘base’ => ‘node_content’,
    ‘description’ => $t(‘Este es un ejemplo de nodo usando Field’),
    ‘title_label’ => $t(‘Título del ejemplo’),
    ‘custom’ => TRUE,
  );

  // Una vez definido el tipo de contenido tenemos que completarlo con aquellas configuraciones
  // por defecto para los tipos de contenido
  // http://api.drupal.org/api/function/node_type_set_defaults/7
  $content_type = node_type_set_defaults($node_example);

  //Añadimos el campo body y le añadimos una etiqueta
  node_add_body_field($content_type, $t(‘Example Description’));

  // Guardamos el tipo de contenido
  node_type_save($content_type);

  // Cargamos la definición de la instancia de body para nuestro tipo de contenido
  // http://api.drupal.org/api/function/field_info_instance/7
  $body_instance = field_info_instance(‘node’, ‘body’, ‘node_example’);

  // Añadimos el modo de visualización para el campo body de nuestro tipo de contenido para que
  // se muestre un resumen del campo
  $body_instance[‘display’][‘example_node_list’] = array(
    ‘label’ => ‘hidden’,
    ‘type’ => ‘text_summary_or_trimmed’,
  );

  // Guardamos los cambios sobre la instancia de body
  // http://api.drupal.org/api/function/field_update_instance/7
  field_update_instance($body_instance);

  // Creamos los campos que añadiremos a nuestro tipo de contenido, mediante un array creado
  // con una función propia
  // http://api.drupal.org/api/function/field_create_field/7
  foreach (_node_example_installed_fields() as $field) {
    field_create_field($field);
  }

  // Creamos las instancias para los nuevos campos, mediante un array creado con una función propia
  // http://api.drupal.org/api/function/field_create_instance/7
  foreach (_node_example_installed_instances() as $instance) {
    $instance[‘entity_type’] = ‘node’;
    $instance[‘bundle’] = $node_example[‘type’];
    field_create_instance($instance);
  }
}

Como puede verse en el ejemplo, vemos como creamos primero el tipo de contenido con el título, le agregamos el campo body, modificamos su visualización en los listados,  añadimos los nuevos campos y los referenciamos con el tipo del contenido. Ahora incluiremos la función de definición de los campos:
/**
 * Devuelve un array asociativo con los campos del tipo de contenido
 *
 * Esta función se utilizará en los hook de instalación y desinstalación
 *
 * @return
 *  An associative array specifying the fields we wish to add to our
 *  new node type.
 *
 * @ingroup node_example
 */
function _node_example_installed_fields() {
//cogemos el nombre de la función de traducción
  $t = get_t();
//devolvemos el array de los campos
  return array(
    ‘node_example_color’ => array(
      ‘field_name’ => ‘node_example_color’,
      ‘cardinality’ => 3,
      ‘type’        => ‘text’,
      ‘settings’    => array(
        ‘max_length’ => 60,
      ),
    ),
    ‘node_example_quantity’ => array(
      ‘field_name’  => ‘node_example_quantity’,
      ‘cardinality’ => 1,
      ‘type’        => ‘text’,
    ),
    ‘node_example_image’ => array(
      ‘field_name’ => ‘node_example_image’,
      ‘type’       => ‘image’,
      ‘cardinality’ => 1,
    ),
  );
}
Ahora introducimos la función que devuelve el array de instancias:
/**
 * Devuelve un array asociativo con las instancias del tipo de contenido
 *
 * La instancia permite a Drupal saber que widget debe usar para permitir al usuario introducir
 * indormación y saber como reaccionar en las diferentes vistas. Presentaremos una página que
 * use el tipo de contenido. Estableceremos la cardinalidad de 3 para establecer 3 campos para
 * definir colores.
 *
 * Esat función se utilizará tanto en la instalación como en la desinstalación del módulo
 * node_example_install() and node_example_uninstall().
 *
 * @return
 *  An associative array specifying the instances we wish to add to our new
 *  node type.
 *
 * @ingroup node_example
 */
function _node_example_installed_instances() {
  $t = get_t();
  return array(
    ‘node_example_color’ => array(
      ‘field_name’ => ‘node_example_color’,
      ‘label’       => $t(‘The colors available for this object.’),
      ‘widget’      => array(
        ‘type’    => ‘text_textfield’,
      ),
      ‘display’ => array(
        ‘example_node_list’ => array(
          ‘label’ => ‘hidden’,
          ‘type’ => ‘node_example_colors’,
        ),
      ),
    ),
    ‘node_example_quantity’ => array(
      ‘field_name’  => ‘node_example_quantity’,
      ‘label’ => $t(‘Quantity required’),
      ‘type’        => ‘text’,
      ‘widget’      => array(
        ‘type’    => ‘text_textfield’,
      ),
      ‘display’ => array(
        ‘example_node_list’ => array(
          ‘label’ => ‘hidden’,
          ‘type’ => ‘hidden’,
        ),
      ),
    ),
    ‘node_example_image’ => array(
      ‘field_name’  => ‘node_example_image’,
      ‘label’       => $t(‘Upload an image:’),
      ‘required’    => FALSE,
      ‘widget’ => array(
        ‘type’    => ‘image_image’,
        ‘weight’  => 2.10,
      ),
      ‘display’ => array(
        ‘example_node_list’ => array(
          ‘label’ => ‘hidden’,
          ‘type’ => ‘image_link_content__thumbnail’,
        ),
      ),
    ),
  );
}

hook_uninstall()

Una vez tenemos definidas las dos últimas funciones la instalación del módulo debería poder realizarse sin problemas, pero antes de activar el módulo, implementaremos el hook_uninstall, donde desharemos todo lo que hemos hecho anteriormente:

  • Borrando todos los contenidos de dicho tipo.
  • Borrando los campos
  • Borrando las instancias
  • Borrando el tipo de contenido

dichos pasos los daremos con el siguiente código:
/**
 * Implementación del hook_uninstall().
 *
 * Este hook es llamado cuando el usuario, no sólo ha desactivado el módulo, sino además
* lo desintala desde la pestaña “Desinstalar” en la página de módulos
 *
 *
 * @ingroup node_example
 */
function node_example_uninstall() {
  // Recoge todos los nodos dados de alta para nuestro tipo de contenido, mientras que el módulo
  // ha estado activado.  Utilizando el API de consulta de bbdd de Drupal, mediante db_query().
  // http://api.drupal.org/api/function/db_query/7
  $sql = ‘SELECT nid FROM {node} n WHERE n.type = :type’;
  $result = db_query($sql, array(‘:type’ => ‘node_example’));
  $nids = array();
  foreach ($result as $row) {
    $nids[] = $row->nid;
  }

  // Borra todos los nodos de una tacada
  // http://api.drupal.org/api/function/node_delete_multiple/7
  node_delete_multiple($nids);

  // Realizamos un bucle que borra todos las instancias, los campos y los datos creados en la
  // instalación
  // http://api.drupal.org/api/function/field_delete_field/7
  foreach (array_keys(_node_example_installed_fields()) as $field) {
    field_delete_field($field);
  }

  // Realizamos un bucle para quitar todas aquellas instancias pendientes de borrado del tipo de
  // contenido (como el campo body), borrandolos individualmente.
  // http://api.drupal.org/api/function/field_delete_field/7
  $instances = field_info_instances(‘node’, ‘node_example’);
  foreach ($instances as $instance_name => $instance) {
    field_delete_instance($instance);
  }

  // Borramos nuestro tipo de contenido
  // http://api.drupal.org/api/function/node_type_delete/7
  node_type_delete(‘node_example’);

  // Purgamos toda la información correspondiente a los campos que acabamos de borrar
  // http://api.drupal.org/api/function/field_purge_batch/7
  field_purge_batch(1000);
}

Pruebas

Una vez activado el módulo, probaremos manuelmente las funcionalidades básicas que hemos añadido. Para ellos como administrador iremos a la configuración a Estructura->Tipos de Contenido y veremos que nuestro tipo de contenido se ha añadido correctamente…

Podremos configurarlo tal como hacemos con cualquier otro tipo de contenido. Y por supuesto, podremos añadir contenidos de dicho tipo. Si vamos a Contenido->Agregar contenido, veremos que nos deja seleccionar el nuevo tipo de contenido…

y al selccionarlo, iremos al formulario de alta de dicho tipo de contenido con los campos que hemos definido durante la instalación…

y al pulsar en el botón “Guardar” nos llevará a la página del nodo que acabamos de dar de alta…

Definiendo una nueva vista para el tipo de contenido

Como ha podido verse hasta ahora tenemos disponible todo lo necesario para poder funcionar con el nuevo tipo de contenido, pero demos un paso más allá. Generaremos un nuevo enlace en el menú por el que dispondremos de una nueva vista personalizada de los contenidos del tipo de contenido que hayamos dado de alta. Para lo cual deberemos generar un conjunto de implementaciones de hook’s nuevas.

El fichero .module

Para albergar todas aquellas nuevas funciones del módulo necesitaremos generar un nuevo fichero .module dentro del módulo, tal como hemos hecho ya en anteriores entregas.

Nueva ruta a página: hook_menu()

Una vez generado el fichero introduciremos la implementación de hook_menu() para incorporar una nueva página disponible en nuestro Drupal para presentar un listado de contenidos personalizado. Introduciremos el siguiente código:
/**
 * Implementa hook_menu().
 *
 * Proveemos una nueva página para ilustrar el uso de nuestro tipo de contenido personalizado
 * para acceder utilizaremos una URL similar a la siguiente http://example.com/?q=examples/node_example
 */
function node_example_menu() {
  $items[‘examples/node_example’] = array(
    ‘page callback’ => ‘node_example_page’,
    ‘access arguments’ => array(‘access content’),
    ‘title’ => ‘Node Example’,
  );
  return $items;
}
Como puede verse la ruta será ‘examples/node_examples’ y utilizaremos la función ‘node_example_page’ para gestionarla y con tener permisos para ver el contenido, nos debería dejar pasar a verla.

Recolipando y presentando Datos

Pasemos a la función node_example_page():
/**
 * Función Callback que construye el contenido y lo devuelve al navegador
 *
 * Viene desde el hook_menu().
 *
 * @return
 *   un array
 *   un array renderizable con los datos de los nodos
 *
 * @see node_load()
 * @see node_view()
 * @see node_example_field_formatter_view()
 */
function node_example_page() {
  // Declaramos el array a devolver
  $renderable_array = array();
  // Consultamos a la bbdd en busqueda de los nodos publicados del tipo de contenido que queremos
  $sql = ‘SELECT nid FROM {node} n WHERE n.type = :type AND n.status = :status’;
  $result = db_query($sql,
    array(
      ‘:type’ => ‘node_example’,
      ‘:status’ => 1,
    )
  );
  $renderable_array[‘explanation’] = array(
    ‘#markup’ => t(“Los nodos declarados en el módulo y que hayas creado se presentarán aquí. Tenga en cuenta que los campos de color se presentarán de una manera distinta en el listado a como normalmente pude verlos. Haga click en el título del nodo para ver la diferencia. Ésta es una vista personalizada de los tipos de contenido llamada ‘example_node_list’.”),
    );
  // Bucle que  para cada nodo intenta aplicar la vista “example_node_list” mediante node_view().
  // http://api.drupal.org/api/function/node_load/7
  // http://api.drupal.org/api/function/node_view/7
  foreach ($result as $row) {
    $node = node_load($row->nid);
    $renderable_array[‘node_list’][]= node_view($node, ‘example_node_list’);
  }
  return $renderable_array;
}
Como puede verse en los comentarios del mismo código recogemos de la bbdd los nodos y los presentamos de una maner distinta a la habitual en la vista del listado.

Añadiendo una vista personalizada a una Entidad

Ahora necesitamos modificar la información de la entidad relacionada con el nuevo tipo de contenido para añadir una nueva vista llamada “example_node_list”. Esto deberemos realizarlo a través del hook_entity_info_alter(). Por lo que incluiremos una implementación del hook similar a la siguiente:
/**
 * Implementa el hook_entity_info_alter().
 *
 * Necesitamos modificar la información de la entidad relacionada con el nodo añadiendo una nueva  * vista que será usada por las funciones node_view() o node_build_content().
 */
function node_example_entity_info_alter(&$entity_info) {
  // Como vemos nos pasa por referecnia la información de la entidad y añadimos la vista
  $entity_info[‘node’][‘view modes’][‘example_node_list’] = array(
    ‘label’ => t(‘Example Node List’),
    ‘custom settings’ => TRUE,
  );
}
El resto de las funciones sirven para poner a funcionar el cambio de presentación de los colores en la vista modificando la presentación del tema para los campos que almacenan los colores:
/**
 * Implements hook_field_formatter_info().
 */
function node_example_field_formatter_info() {
  return array(
    ‘node_example_colors’ => array(
      ‘label’ => t(‘Node Example Color Handle’),
      ‘field types’ => array(‘text’),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 *
 * @todo: We need to provide a formatter for the colors that a user is allowed
 * to enter during node creation.
 */
function node_example_field_formatter_view($object_type, $object, $field,
                                            $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display[‘type’]) {
    case ‘node_example_colors’:
      foreach ($items as $delta => $item) {
        $element[$delta][‘#type’] = ‘markup’;
        $color = $item[‘safe_value’];
        $element[$delta][‘#markup’] = theme(‘example_node_color’, array(‘color’ => $color));
      }
      break;
  }

  return $element;
}

/**
 * Implements hook_theme().
 *
 * This lets us tell Drupal about our theme functions and their arguments.
 */
function node_example_theme($existing, $type, $theme, $path) {
  return array(
    ‘example_node_color’ => array(
      ‘variables’ => array(‘color’ => NULL),
    ),
  );
}
/**
 * A custom theme function.
 *
 * By using this function to format our node-specific information, themes
 * can override this presentation if they wish.  This is a simplifed theme
 * function purely for illustrative purposes.
 */
function theme_example_node_color($variables) {
  $output = ‘<span style=”background-color: #ccc; padding: 1em; margin-bottom: 1em; float: left; color: ‘ . $variables[‘color’] . ‘”>’ . $variables[‘color’] . ‘</span>’;
  return $output;
}
/**
 * @} End of “defgroup node_example”.
 */
Si cargamos esa dirección URL veremos una página similar a la siguiente…

Presentación de Tipos de Contenido

Para la gestión de la presentación de los tipos de contenido, se recomienda el uso de Vistas.

Referencia

Licencia Creative Commons

Curso de desarrollo de módulos para Drupal 7(III): hook_schema explicado con el módulo User

Para entender bien esta entrega será necesario tener conocimientos avanzados de uso de arrays en PHP.
Como explicar el hook_schema requiere de un modelo de datos en concreto vamos a apoyarnos sobre el módulo User, que forma parte del nucleo de Drupal.
Abrimos el fichero “modules/user/user.install” desde Aptana para ver su contenido, pulsando doble clic sobre él. Y nos encontramos con el siguiente código en la función user_schema():
/**
 * Implements hook_schema().
 */
function user_schema() {
  $schema[‘authmap’] = array(
    ‘description’ => ‘Stores distributed authentication mapping.’,
    ‘fields’ => array(
      ‘aid’ => array(
        ‘description’ => ‘Primary Key: Unique authmap ID.’,
        ‘type’ => ‘serial’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
      ),
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => “User’s {users}.uid.”,
      ),
      ‘authname’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 128,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => ‘Unique authentication name.’,
      ),
      ‘module’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 128,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => ‘Module which is controlling the authentication.’,
      ),
    ),
    ‘unique keys’ => array(
      ‘authname’ => array(‘authname’),
    ),
    ‘primary key’ => array(‘aid’),
    ‘foreign keys’ => array(
      ‘user’ => array(
        ‘table’ => ‘users’,
        ‘columns’ => array(‘uid’ => ‘uid’),
      ),
    ),
  );

  $schema[‘role_permission’] = array(
    ‘description’ => ‘Stores the permissions assigned to user roles.’,
    ‘fields’ => array(
      ‘rid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Foreign Key: {role}.rid.’,
      ),
      ‘permission’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 128,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => ‘A single permission granted to the role identified by rid.’,
      ),
      ‘module’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 255,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => “The module declaring the permission.”,
      ),
    ),
    ‘primary key’ => array(‘rid’, ‘permission’),
    ‘indexes’ => array(
      ‘permission’ => array(‘permission’),
    ),
    ‘foreign keys’ => array(
      ‘role’ => array(
        ‘table’ => ‘roles’,
        ‘columns’ => array(‘rid’ => ‘rid’),
      ),
    ),
  );

  $schema[‘role’] = array(
    ‘description’ => ‘Stores user roles.’,
    ‘fields’ => array(
      ‘rid’ => array(
        ‘type’ => ‘serial’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Primary Key: Unique role ID.’,
      ),
      ‘name’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 64,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => ‘Unique role name.’,
        ‘translatable’ => TRUE,
      ),
      ‘weight’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘The weight of this role in listings and the user interface.’,
      ),
    ),
    ‘unique keys’ => array(
      ‘name’ => array(‘name’),
    ),
    ‘primary key’ => array(‘rid’),
    ‘indexes’ => array(
      ‘name_weight’ => array(‘name’, ‘weight’),
    ),
  );

  $schema[‘users’] = array(
    ‘description’ => ‘Stores user data.’,
    ‘fields’ => array(
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Primary Key: Unique user ID.’,
        ‘default’ => 0,
      ),
      ‘name’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 60,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => ‘Unique user name.’,
      ),
      ‘pass’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 128,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => “User’s password (hashed).”,
      ),
      ‘mail’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 254,
        ‘not null’ => FALSE,
        ‘default’ => ”,
        ‘description’ => “User’s e-mail address.”,
      ),
      ‘theme’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 255,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => “User’s default theme.”,
      ),
      ‘signature’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 255,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => “User’s signature.”,
      ),
      ‘signature_format’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 255,
        ‘not null’ => FALSE,
        ‘description’ => ‘The {filter_format}.format of the signature.’,
      ),
      ‘created’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘Timestamp for when user was created.’,
      ),
      ‘access’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘Timestamp for previous time user accessed the site.’,
      ),
      ‘login’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => “Timestamp for user’s last login.”,
      ),
      ‘status’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘size’ => ‘tiny’,
        ‘description’ => ‘Whether the user is active(1) or blocked(0).’,
      ),
      ‘timezone’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 32,
        ‘not null’ => FALSE,
        ‘description’ => “User’s time zone.”,
      ),
      ‘language’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 12,
        ‘not null’ => TRUE,
        ‘default’ => ”,
        ‘description’ => “User’s default language.”,
      ),
      ‘picture’ => array(
        ‘type’ => ‘int’,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => “Foreign key: {file_managed}.fid of user’s picture.”,
      ),
      ‘init’ => array(
        ‘type’ => ‘varchar’,
        ‘length’ => 254,
        ‘not null’ => FALSE,
        ‘default’ => ”,
        ‘description’ => ‘E-mail address used for initial account creation.’,
      ),
      ‘data’ => array(
        ‘type’ => ‘blob’,
        ‘not null’ => FALSE,
        ‘size’ => ‘big’,
        ‘serialize’ => TRUE,
        ‘description’ => ‘A serialized array of name value pairs that are related to the user. Any form values posted during user edit are stored and are loaded into the $user object during user_load(). Use of this field is discouraged and it will likely disappear in a future version of Drupal.’,
      ),
    ),
    ‘indexes’ => array(
      ‘access’ => array(‘access’),
      ‘created’ => array(‘created’),
      ‘mail’ => array(‘mail’),
    ),
    ‘unique keys’ => array(
      ‘name’ => array(‘name’),
    ),
    ‘primary key’ => array(‘uid’),
    ‘foreign keys’ => array(
      ‘signature_format’ => array(
        ‘table’ => ‘filter_format’,
        ‘columns’ => array(‘signature_format’ => ‘format’),
      ),
    ),
  );

  $schema[‘users_roles’] = array(
    ‘description’ => ‘Maps users to roles.’,
    ‘fields’ => array(
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘Primary Key: {users}.uid for user.’,
      ),
      ‘rid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘default’ => 0,
        ‘description’ => ‘Primary Key: {role}.rid for role.’,
      ),
    ),
    ‘primary key’ => array(‘uid’, ‘rid’),
    ‘indexes’ => array(
      ‘rid’ => array(‘rid’),
    ),
    ‘foreign keys’ => array(
      ‘user’ => array(
        ‘table’ => ‘users’,
        ‘columns’ => array(‘uid’ => ‘uid’),
      ),
      ‘role’ => array(
        ‘table’ => ‘roles’,
        ‘columns’ => array(‘rid’ => ‘rid’),
      ),
    ),
  );

  return $schema;
}

Como puede verse en el ejemplo, básicamente está utilizando una variable llamada “$schema” que al final de la función devuelve. Dicha variable es un array que contiene un índice por cada tabla que necesita generar.
Por ejemplo si queremos generar la tabla ‘users’, indicamos que sobre el array $schema metemos un nuevo elemento con el índice ‘users’ y como valor metemos otro array.
El primer elemento del array valor de la tabla ‘users’ será la descripción de la tabla, cuyo índice será ‘description’ y cuyo valor será una cadena de caracteres con la descripción de la tabla.
El segundo elemento tendrá como índice el nombre de ‘fields’ y contiene un array con las defniciones de los campos de la tabla.
Así tendremos un array $schema definido de esta manera:
$schema[‘users’] = array(
    ‘description’ => ‘Stores user data.’,
    ‘fields’ => array()
);
En dicho array de campos (‘fields’), vamos introducciendo cada campo como un elemento cuyo índice es el nombre del campo y su valor es otro array con los metadatos del campo que queremos meter en la tabla, como el caso del campo ‘uid’…
$schema[‘users’] = array(
    ‘description’ => ‘Stores user data.’,
    ‘fields’ => array(
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Primary Key: Unique user ID.’,
        ‘default’ => 0,
      )
     )
);
En este caso, el campo uid, tiene los siguientes elementos definidos:

  • type: tipo de campo definido por drupal
  • unasigned: valor booleano que permite definir si el campo es con o sin signo. Sólo disponible para campos numéricos.
  • not_null: valor booleano que nos indica si es campo puede ser null o no
  • description: descripción del campo
  • default: valor por defecto del campo

Como puede verse dicha definición de campo es una estructuración en forma de array de las típias configuraciones que solemos hacer en la propia base de datos cuando creamos un campo nuevo.
De esta manera ñadiríamos todos los campos que queremos que tenga la tabla, como distintos elementos del valor del array ‘fields’.
A continuación seguramente será necesario indicar de todos los campos cual (o cuales) es la clave primaria, en el caso de la tabla de users el campo ‘uid’ es el campo de clave primaria de la tabla, por lo que tenemos que insertar un nuevo elemento en el array principal $schema[‘users’] llamado ‘primary_key’ cuyo valor es un array con el nombre de los campos que son clave primaria. Por lo que el código quedaría así:
$schema[‘users’] = array(
    ‘description’ => ‘Stores user data.’,
    ‘fields’ => array(
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Primary Key: Unique user ID.’,
        ‘default’ => 0,
      )
     ),
    ‘primary key’ => array(‘uid’)
);
 En el caso de la tabla users, tambiñen necesita hacer uso de índices sobre campos, para ello introduce también dentro del array $schema[‘users’] un elemento cuyo índice es ‘indexes’ cuyo valor debería ser un array con el nombre del índice y su valor un array con los nombres de los campos que queremos introducir en el índice. Así quedaría el array de la siguiente manera…
$schema[‘users’] = array(
    ‘description’ => ‘Stores user data.’,
    ‘fields’ => array(
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Primary Key: Unique user ID.’,
        ‘default’ => 0,
      )//aquí introduciríamos el resto de elementos para definir el resto de campos de la tabla
     ),
    ‘primary key’ => array(‘uid’) ,
    ‘indexes’ => array(
      ‘access’ => array(‘access’),
      ‘created’ => array(‘created’),
      ‘mail’ => array(‘mail’),
    )
);
En este ejemplo, define 3 índices, sobre los campos access, created y mail. Por otra parte podemos definir índices únicos sobre campos, para ello debemos introducir un elemento más con índice ‘unique keys’ cuyo valor es un array con los nombres del índice y el valor de ese elemento es otro array con los campos que pertenecen a dicho array, en el ejemplo…
$schema[‘users’] = array(
    ‘description’ => ‘Stores user data.’,
    ‘fields’ => array(
      ‘uid’ => array(
        ‘type’ => ‘int’,
        ‘unsigned’ => TRUE,
        ‘not null’ => TRUE,
        ‘description’ => ‘Primary Key: Unique user ID.’,
        ‘default’ => 0,
      )//aquí introduciríamos el resto de elementos para definir el resto de campos de la tabla

     ),
    ‘primary key’ => array(‘uid’) ,
    ‘indexes’ => array(
      ‘access’ => array(‘access’),
      ‘created’ => array(‘created’),
      ‘mail’ => array(‘mail’),
    ),
    ‘unique keys’ => array(
      ‘name’ => array(‘name’),
    )
);
En el ejemplo crea un índice único llamado ‘name’  sobre el campo ‘name’. Dejamos para otro momento el índice ‘foreing keys’ por que tendríamos que explicar el modelo entero de users.

Debemos pensar que Drupal utilizará este hook para crear las tablas necesarias en la base de datos para el módulo, pero seguiríamos necesitando modificar el hook_install para introducir los datos iniciales en las tablas definidas en el hook_schema, como en el ejemplo…
// Insert a row for the anonymous user.
  db_insert(‘users’)
    ->fields(array(
      ‘uid’ => 0,
      ‘name’ => ”,
      ‘mail’ => ”,
    ))
    ->execute();
  Debido que al realizar la desinstalación del módulo user no hay que realizar más acciones que el borrado de las tablas generadas con la definición del hook_schema, el módulo user no necesita un hook_uninstall().

Práctica

  1. Genera un nuevo módulo en base a unos requisitos de base de datos dados.
  2. Define el hook_schema definiendo el modelo de base de datos
  3. Define el hook_install para introducir los datos iniciales en las tablas
  4. Prueba a realizar la instalación e activación del módulo, comprueba si las tablas se han generado correctamente.
  5. prueba a desactivar y desinstalar el módulo y mira a ver si ha borrado correctamente las tablas en la BBDD.

Referencias

Licencia Creative Commons

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. ACEPTAR

Aviso de cookies