Tablas en Drupal: Haciendo una tabla perfecta.
Sobre el artículo
Este artículo, basado en el original "Tables in Drupal: Making a perfect table", escrito por Kevin Bowler, ha sido traducido por Miguel Ángel García al castellano (también conocido como español).
El copyright pertenece a Kevin Bowler, por lo que deberán dirigirse a éste para los agradecimientos y cualquier comentario sobre el código.
Por favor, use el foro en castellano únicamente para comentarios sobre la traducción.
No se permite la reproducción total ni parcial de este documento, salvo del código.
¿Alguna vez has...?
¿Alguna vez ha querido hacer una tabla en Drupal usando theme_table? Bien, ¿qué tal si le añadimos unos links extra, por ejemplo para opciones (Edit/Delete etc)? ¿Y si la ordenamos? ¿Algo de paginación? Bien, este tutorial le mostrará cómo hacer todo esto. ¡Después de esto será el máster de las Tablas en Drupal!
Paso 1: Difinir las cabeceras
¿Cuáles son los nombres de las columnas?
Suena estúpido, pero será útil más adelante durante el proceso.
Puede encontrarlos buscando la tabla correspondiente en su base de datos.
Debería terminar con un array de cabeceras como éste:
Claro que es necesario sustituir el texto entre corchetes con el nombre del campo de la base de datos.
Paso 2: Define los extras
Los extras son las acciones que se quieren realizar sobre los datos, como editar o borrar el dato.
La manera más limpia y clara de hacer esto es crear una función de ayuda separada a la que se pueda llamar
mientras se construye la tabla principal.
* Función de utilidad para construir enlaces para la tabla
*/
function _generate_optons ( $id )
{
$links = l(t('Edit'), "admin/settings/data/edit/$id") .
" " .
l(t('Remove'), "admon/settings/data/remove/$id");
return $links;
}
Realmente no es demasiado código, y se observa que puede ser muy manejable. Simplemente construya un enlace (utilizando la función l() ) con acciones basadas en las elecciones de la cabecera, y añada el id de la columna en el enlace, con el fin de asegurar que se actúa sobre el elemento correcto.
Uso el subrayado al principio del nombre de la función para marcar que es una función 'privada'; sólo se va a utilizar de forma interna en este módulo.
Esta función se llamará en el paso 4, durante el proceso de los datos para construir la tabla actual.
Paso 3: Ejecuta la consulta
Ahora tenemos que extablecer la consulta para obtener todos los datos de nuestra deseada tabla.
Como puede observarse, no es una query demasiado compleja. Puede pensarse que es ineficiente, y así es.
Dejaremos que Drupal se ocupe de esto más adelante (paso 6).
Paso 4: Procesado de datos
Es en este paso cuando transforman los datos de base de datos en un array, preparado para ser introducido con un tema en una tabla.
$result = db_query ( $sql );
if ( $result )
{
// Establecer el array de datos
$data = '';
$i = 1;
// Bucle básico para obtener los datos
while ( $tmp = db_fetch_array ( $result ) )
{
$data[$i] = $tmp;
}
}
Puede ser tan simple o tan complejo como se desee. Aquí sólo se cubrirá un ejemplo de personalización.
Eliminando el ID
A veces no se desea mostrar al usuario el ID de un elemento, pero se necesita para generar la URL o los extras.
De esta manera, después de usarlo en el código anterior se puede 'desestablecer'.
Simplemente inserte la línea siguiente al final del bucle while ( ¡¡¡ pero dentro de él !!! ):
Ahora el ID no requiere una cabecera, por lo que no es necesario seleccionar el ID en la query y no tiene que mostrarse al usuario cómo se ordenan los datos.
Paso 5: Establecer un tema para la salida
De esta forma, ahora vamos a establecer un tema para la salida, y obtendremos una tabla básica:
Lo que se está haciendo es llamar a la función theme, y pasarle el tipo del elemento a renderizar, en nuestro caso una tabla.
Esto llama a la función interna para el tema de la tabla, llamada theme_table(), y se le pasan los dos parámetros; el array de cabeceras, y el array de datos (ambos definidos antes en el código).
Si lo único que busca es una tabla simple, puede dejarlo aquí.
De todas maneras, es muy fácil añadirle algunas características interesantes a la tabla, de forma que impresione a la gente con su asistente de tablas Drupal, ¡por lo que siga leyendo!
Paso 6: El paginador
Ok, esta tabla está bien, pero ¿qué ocurre si tiene 1.000 registros? ¡Eso hace una página muuuuuuuuuy larga, y una consulta masivamente ineficiente! ¡Pobre servidor de bases de datos!
Así, se le puede pedir a Drupal que añada un paginador a nuestra consulta, y sólo devuelva cada vez un número de filas predefinido:
// generar una consulta paginada
$result = pager_query ( $sql, $limit );
¿Parece simple? Bien. Lo es. Lo único que estamos haciendo es reemplazar la llamada a db_query con una llamada a pager_query, y pasándola un límite, o el número de filas a devolver por página.
Si quería ser realmente listo, este valor se podría establecer en una página de administracción, y añadida aquí usando variable_get (Le dejaré éste a usted, ¡no me gustaría fastidiarle toda la diversión!).
Ésta es una de las cosas a hacer antes de que nuestro paginador funcione. Hay que establecer el tema de la salida como una tabla paginada.
No se preocupe, no es difícil, y no requiere cambiar mucho código.
Todo lo que haremos es reemplazar la llamada actual a theme por:
'table',
$headers,
$data
);
$output .= theme ( 'pager', NULL, $limit, 0 );
print theme ( 'page', $output );
¿Qué es lo que estamos haciendo aquí?
- Llamar a theme para establecer el tema de nuestra tabla original, y almacenando el resultado en una variable.
- Añadir el paginador a esa variable llamando a theme_pager, pero esta vez usando 'pager' como tipo, para así llamar a la función con subrayado theme_pager, y concatenarlo con nuestra variable.
- Finalmente, establecemos el tema a la salida como una página a renderizar lista para mostrar.
Simple, ¿no?
Paso 7: Ordenación
El paso final en la ruta de la Tabla Zen es la ordenación de la tabla. ¿No estaría bien poder ordenar por un campo específico distinto del ID, o simplemente cambiar el orden de la tabla, al vuelo?
Bien, con unos pocos cambios en nuestro código, ¡podemos!
¿Recuerda el array de cabeceras que creamos anteriormente? Bien, ahora nos va a venir de perlas.
Para que la ordenación de la tabla funcione, tenemos que indicarle cómo ordenar los datos. Se hace con el array de cabeceras. Simplemente se le da a cada cabecera (que es el nombre del campo en la base de datos) el orden por el que ordenar los datos.
En el siguiente ejemplo, usaremos el campo 'Name'.
Así, nuestro array de cabeceras queda de la forma:
Esto nos permitirá ordenar por un campo llamado 'name', y ordenar en orden descendiente cuando se pulse sobre la cabecera.
Ahora se requiere añadir la ordenación de las tablas SQL a nuestra SQL original:
// añadir el orden mediante la cláusula
$sql .= tablesort_sql ( $headers );
Ahora le pasamos esto a nuestra llamada a pager_query y bingo, tenemos una tabla ordenable, paginada y con opciones extra.
Aún más lejos
La belleza de Drupal es que puede llegar tan lejos como quiera. Una opción obvia es añadirle la funcionalidad de Arrastrar y Soltar, o envolverlo en una función theme, de manera que todo el código se añada a una página con con una llamada a su propia función de tema, como por ejemplo theme ( 'paged_table', $data );.
Por favor, hágame saber si lo encuentra útil o si ha llegado más lejos.
En futuras semanas iré comentando cómo definir sus propios elementos personalizables como mencioné más arriba.
¡Feliz codificación!
N.T.
En el código, los literales se han mantenido en inglés. Así lo recomienda la gente de Drupal, ya que el inglés es el idioma oficial de Drupal. Las traducciones al resto de idiomas se realizarán todas de la misma manera, con el inglés como base.


y para poner enlaces a los registros?
Hola,
primeramente, muy buen tutorial.
En mi caso, tengo tambien una tabla que se construye con una consulta a una tabla creada por mi. El tema es que el módulo que he creado de momento consigo que me liste todos los registros y me los pagine.
Lo que querría es añadir un enlace en cada registro para ampliar la información de ese registro. Me explico. Cada registro es un libro (tengo una tabla que contiene información sobre un libro, como el título, autor, editorial, etc.). Lo que pretendo es que cada título de libro que imprimo con el módulo sea un enlace que lleve a una página donde se acabe de ver toda la información de ese libro.
Eso en php es relativamente fácil. Pero a parte de que me da problemas de indexación en el buscador (es decir, no me indexa las fichas de los libros, y por tanto, cada vez que hago una búsqueda no me encuentra ninguna ficha de los libros) lo querria hacer todo via módulo, como se podria hacer?
Gracias de antemano.
Claro que se puede hacer.
Hola!
Claro que se puede hacer :-D
Antes que nada, quiero aclarar un tema: el tutorial no es mío, sino que lo he traducido (con permiso del autor) por lo bueno que me pareció.
Para que tus fichas aparezcan en el buscador, necesitas que se registren en el CCK. Te recomiendo que investigues este tema. Tengo ganas de hacer un tutorial, pero aún no he llegado a ese punto.
Para poner enlaces a los registros... Bueno, una vez que tus registros sean nodos -es decir, se están registrando en el CCK-, todos los nodos se pueden referenciar de la forma node/id, donde "id" es el identificador del nodo, por lo que no creo que tengas mayor problema.
Espero haber podido aportar un poco de luz sobre el tema...
Hola de nuevo, gracias por
Hola de nuevo,
gracias por responder :).
No lo acabo de ver claro. Es decir, hay alguna manera de poder automatizar y que desde el CCK, habiendo creado un tipo de contenido adecuado a la ficha del libro, pueda coger los registros de una tabla de libros creada por mi y cree una ficha de libro por cada registro de esa tabla?
A parte de eso, si creo un nodo por cada libro con su correspondiente nid (identificador de nodo), si luego, entre medias, se crea un artículo o una notícia, página o lo que sea, que también es un nodo, luego no podria tener problemas? es decir, si del nodo 10 al 15 son libros, el nodo 16 es una noticia, y del 17 al 20 son libros... ese nodo 16 no me podria dar problemas a la hora de imprimir los libros?
Quizás deberia de tener la tabla libros asociada de alguna manera a la de nodos o algo para que supiera que un libro está asociado a un nodo en concreto?
Vaya, no creia que fuera a ser tan engorroso algo que creía tan básico :P. Igual seria mejor modificar de alguna manera del buscador de drupal o crear uno nuevo que busque en las tablas que he creado yo?
De nuevo, las gracias por responder.
Confía en Drupal
Respecto a la primera parte, ya me haces dudar: realmente no sé si vas a poder buscar por ciertos campos que mantienes en una tabla a parte. De todas maneras, ¿no pensabas crear un buscador de libros? Sería interesante el tema, para poder especificar si estás buscando por autor, por título, etc.
En la segunda parte, la mezcla de tipos de nodos, no va a darte ningún tipo de problema. Lo que hace Drupal es llamar a tu módulo preguntándole: "Oye, ¿Cómo renderizo esto?", y será tu módulo el que le explica: "Pues mira, arriba una caja de texto, después un combo y después un cuadro de texto". Por lo tanto tienes que ser tú quien le diga a Drupal cómo pintarlo.
Si no terminas de verlo, te recomiendo que te instales algún plugin "raro", como alguno de gestión de tareas, algún "to-do-list" o algo por el estilo. Verás que es muy similar a lo que quieres hacer. Seguramente hasta puedas aprovechar parte del código.
Y venga.... ¡¡ animate !! Es mucho más fácil de lo que parece. Lo único que ocurre es que requiere un poco de tiempo.
Una pregunta: ¿Va a ser libre o es sólo para ti?
Un saludo.
Pues es para mi, es para un
Pues es para mi, es para un proyecto final de carrera. La intención era publicarlo, pero vaya, ya veremos, porqué ahora mismo después de darle tanto tiempo, lo único que quiero es acabarlo xD.
Si lo tuviera que hacer de zero, el tema de los registros, lo haría via CCK como has dicho, porque es una buena solución. Creando un tipo de contenido y venga. El tema está en que ya tengo registros. He hecho un pequeño screenscraper, muy rudimentario, para no tener que andar metiendo yo los registros manualmente.
Entonces el tema está en buscar una solución que me permita aprovechar la base de datos que tengo ya hecha. Lo del CCK no lo veo claro por eso, porqué no veo yo una manera "fácil" de poder aprovechar los registros que ya tengo.
Además que creo que debería de crear relaciones intermedias entre las tablas Nodes y Libros (la de libros es la que he creado yo).
Lo del buscador no pensaba hacerlo, sólo me ha salido para encontrar una solución al problema que tenia que no me encontraba los libros porque no los tenia en forma de nodo... Podría ser una solución más "asequible" ya que tengo las tablas ya hechas y registros insertados.
Creo que si hago un buscador específico para el tema de los libros (en parte también por lo que dices, para poder hacer una búsqueda sobre autor o título) me complicaria menos, lo que pasa es que querria adaptarlo al que ya hay en Drupal, porqué tendria que buscar tambien el contenido de la web en general (me refiero al resto de nodos creados). Pero claro, me da un poco de reparo meterme en un módulo ya hecho y modificarlo... yo nosé si no hay ya algo para poder ampliar las búsquedas sobre las tablas que he creado yo.
Es que tener dos buscadores es poco usable, y el que tiene Drupal es muy bueno, solo tengo el problemilla de que no puedo especificar que busque en mis tablas :S. Podria tener el buscador de Drupal en la home, y luego si quieres hacer una búsqueda "avanzada" de libros ir al otro, pero vaya, si lo puedo evitar mejor...
Al final te voy a saturar el foro de comentarios xD.
Un detalle
Ah, ya veo... Crees que lo que te digo es que uses CCK para ir creando los nodos...
Se puede utilizar desde código, registrando lo que te dé la gana. A eso es a lo que me refiero: a registrar los nodos desde tu propio módulo, de manera que aproveches toda la estructura que ya tienes. No me refería a usar "el asistente" :-D
Para eso está.
Para eso creé el foro, para llenarlo.
Te recomiendo que sigas mi tutorial de drupal, ya que comienza desde cero (creación de tablas, etc.) con un ejemplo bastante pequeño: un pequeño registro de mensajes. Este tutorial tiene segunda parte, en la que completo las tablas.
Insisto que si utilizas CCK puedes utilizar toda la estructura que tuvieras anteriormente. Es tu clase la que decide cómo renderizar los datos. Probablemente Drupal indexe los datos ya renderizados. La única diferencia entre tu nodo "libro" y un nodo "bitácora" son los campos a rellenar para crearlo, el "formulario", por decirlo así.
Cuando comencé con Drupal -ojo, que tampoco he hecho grandes cosas- utilicé CCK en lo primero que hice, por lo que no tiene que ser demasiado complicado, teniendo en cuenta que no tenía ni idea de PHP.
La estructura de lo que necesitas, en mi opinión, es un nodo que guarda los datos en la estructura que tú has diseñado, solicita los datos mediante un formulario típico y después los muestra usando themes. Me gustaría decirte que también tengo tutoriales de todo eso, pero los dejé en el tintero por otras cosas más "divertidas" :-D
Los nodos son sólo "apuntadores". El contenido lo pones tú. Son una superclase. Por ejemplo: un coche es un vehículo, pero una moto también lo es. Si te creas una lista de "vehículos", podrás acceder a coches y motos, y cada uno tendrá unas características distintas. Pues algo así sucede con los nodos: hay nodos bitácora, página, tarea, proyecto, calendario, evento, libro, .... lo que se te ocurra, pero todos son nodos. En mi caso da la casualidad de que, precisamente, lo que quiero es que no aparezcan en las búsquedas, por lo que lo quité del CCK.
Ánimo y no te rindas. Yo tardé 2 años en acabar el proyecto.
Bueno, he estado mirando el
Bueno, he estado mirando el tema del CCK y creo que sé por donde puede ir la historia, aunque hasta que no lo pruebe no lo verificaré. Veo que cuando creas un tipo de contenido y le das unos campos para rellenar, en la base de datos te crea la tabla "content_type_nombre". Donde "nombre" estaria el nombre del tipo de contenido que has creado.
Al entrar en ésta tabla, he visto los campos que he creado y además sus identificadores "vid" y "nid". Así que, supongo que lo que tendré que hacer, es que en vez de meterle los datos a la tabla de libros que habia creado, lo suyo seria poner esos datos en la tabla de "content_type_nombre".
De todas maneras, hasta que no me ponga, no lo veré del todo. Pero vaya, que creo que ya sé como lo puedo hacer.
Bueno, antes de nada, gracias
Bueno, antes de nada, gracias por la respuesta una vez más.
Me miraré los tutos que me dices de tu web a ver si me sacan de dudas :D.
Muchas gracias.
Ya di con la solución
Hola de nuevo,
pues eso, que ya di con la solución y quería postearla por si alguien se encuentra con el mismo problema.
Bueno, antes de todo comentar que también comenté ésta duda en el foro de Drupal Hispano. Y ahi es donde he puesto al completo la solución. Dejo en enlace directo (http://drupal.org.es/node/8726#comment-34516).
De todas maneras, y a modo de resumen, comentar que la solución la he encontrado con el módulo Node Import. Esa tabla de registros que tenia, la exporté a un archivo CVS con formato UTF 8.
Una vez hecho esto, he instalado el módulo en la web y mediante una serie de pasos que te va indicando, configuras los campos origen (los del archivo CVS) respecto a los campos destino (los campos creados en tu tipo de contenido). Entonces se importa todo y listo, ya tienes cada registro de tu tabla inicial en forma de nodo.
Pues nada, muchas gracias por tu ayuda que ha sido vital para poder dar con la solución.
Un saludo.
Me alegro
Pues nada, me alegro :-D
Ni idea de ese módulo. Leeré la solución completa.
sobre tu explicacion
Hola amigo , he seguido paso a paso tu explicación y traté de usar lo que explicabas pero me da el siguiente error
Fatal error: Cannot use object of type stdClass as array in D:\Clases\Desarrollo Tesis\wamp\www\drupal14\includes\theme.inc on line 1339
aqui te pongo la tabla que hice para ver si le encuentras algun error , mi usuario es acalvarez@estudiantes.uci.cu
function ejemplo_tabla_display()
{
$content = '';
$header = array
(
array('data' => t('Nombre'), 'field' => 'nombre', 'sort' => 'desc'),
array('data' => t('Módulo'), 'field' => 'modulo', 'sort' => 'desc'),
array('data' => t('Submódulo'), 'field' => 'submodulo', 'sort' => 'desc'),
array('data' => t('Estado'), 'field' => 'estado', 'sort' => 'desc'),
array('data' => t('Fecha'), 'field' => 'fecha', 'sort' => 'desc'),
);
$sql = "SELECT * FROM {casodeuso} ";
$sql .= tablesort_sql ( $header );
$limit = 10;
// generar una consulta paginada
$queryResult = pager_query ( $sql, $limit );
$rows = array ();
$i = 1;
while ( $result = db_fetch_object ( $queryResult ) )
{
$rows[$i] = $result;
unset ( $rows[$i]['id'] ); // esta funcion tambien me da error
}
$content .= theme ( 'table', $header, $rows);
$content.= theme ( 'pager', NULL, $limit, 0 );
print theme ( 'pager', $content );
}
por favor si me puedieras ayudar te lo agradecería mucho .. Gracias de antemano
Respuesta
Hola.
Lo primero, vamos a llamar a pager_query con todos sus argumentos:
Así establecemos que es "la tabla 1", para después indicarle al paginador sobre quién está actuando.
Fíjate en el While. Tienes un iterador ($i) sobre el que nunca incrementas.
En el ejemplo de código (función feedback_admin) viene lo siguiente:
while ( $message = db_fetch_object ( $queryResult ) )
{
$row = array ();
$row['indate'] = $message->indate;
$row['name'] = $message->name;
$row['message'] = $message->message;
$row['read'] = $message->read;
$row['edit'] = l ( t('edit'), "feedback/adminmessages/" . $message->id );
$rows[] = $row;
}
Prueba a usar algo así.
Y evita usar el unset. Estás destruyendo algo que no sé si has construido.
Finalmente, trata de devolver $content como resultado, no lo imprimas. Es decir, en vez de:
$content.= theme ( 'pager', NULL, $limit, 0 );
print theme ( 'page', $content );
Trata de usar:
$content .= theme ( 'pager', NULL, $limit, 1 );
return $content
(El 1 es el número de tabla que dimos al principio).
Debería funcionarte. No tiene nada.
¿Has probado el ejemplo, el tutorial o tan sólo te has leído algo ?
Un saludo!!