Importando contenido con drupal_execute()

9 Comentarios
Fecha: 
15 de Junio de 2009

Uno de los primeros artículos que siempre quise escribir en Cuenco Digital fue la explicación de como importar contenido de forma programática. Y hoy que lo estoy haciendo estoy realmente convencido que puede resultarle útil a muchos y evitarles muchos de los dolores de cabeza que este tema me ha hecho padecer.

Advertencia: Este tema no es para novatos en Drupal. Se requieren conocimientos de programación y un profundo entendimiento de como funciona Drupal. Así que antes de preguntar, lea bien todo por lo menos 3 veces :-)

Lo que voy a explicarles en el siguiente artículo es como simular la creación de contenidos tal como si una persona se sentara en frente de una PC y rellenara un formulario.

Por supuesto la utilidad de este tema se ve claramente si usted ya posee muchos datos en algún formato interpretable, por ejemplo una tabla, una planilla de cálculo o un archivo XML.

Herramientas:

Para realizar este trabajo usted va a necesitar los módulos Devel y Form inspect. Si nunca ha usado estos módulos puede ver su funcionamiento en el siguiente video.

Este artículo esta orientado a Drupal 6. En Drupal 5 este procedimiento es muy diferente (y de hecho más complejo), por lo tanto intente utilizar Drupal 6 para esta tarea.

La idea básica:

Como mencioné antes, lo que buscamos es simular la creación de un contenido tal como si lo hiciera un humano.

Para eso usted debe tener creado su tipo de contenido, con sus correspondientes campos. Para comenzar con algo simple veremos el proceso de creación de nodos del tipo Página.

Formulario para crear una página

El proceso conocido por muchos es completar el formulario y presionar enviar. Los campos que tienen un asterisco son obligatorios, mientras que los demás no.

Sin embargo, para programar este funcionamiento, usted debe instalar Devel y activar Form Inspect.

Luego, complete el formulario con datos de prueba y presione Previsualizar.

Cuando haya hecho esto, aparecerán debajo del formulario dos campos desplegables que indican la estructura del array $form y del array $form_state.

Preste mucha atención a $form_state, porque es crucial para crear el contenido de forma programada.

¿Cómo funciona drupal_execute()?.

En lineas generales, drupal_execute() simula el envío de un formulario tal como si se presionara el boton Enviar.

En su sintaxis indica que requiere al menos 2 parámetros. El primero es el nombre del formulario a enviar, el segundo es el array que contiene la información de $form_state.

Opcionalmente se pueden agregar otros parámetros, en nuestro caso, agregaremos un tercer parámetro que representa el nodo a crear.

Para probar entonces un ejemplo básico, copie y pegue este código en un formulario de creación de algún contenido, por ejemplo un story.

Luego seleccione el formato de entrada y presione Previsualizar.

<?php
// El array $node luego se convertirá a un objeto
$node = array();

// Solo es necesario especificar el tipo de nodo
$node['type'] = 'page';

// El array $form_state contiene la información que se envia cuando se presiona Guardar
$form_state = array();

// Definimos el título de la página
$form_state['values']['title'] = 'Página de prueba';

// Definimos el body para el nodo
$form_state['values']['body'] = 'Texto de prueba';

// Definimos el autor del nodo. Recuerde que debe tener permisos de creación de paginas
$form_state['values']['name'] = 'admin';

// Importantisismo, definimos que boton se debe presionar
$form_state['values']['op'] = t('Save');

// El parámetro valor es el nombre del formulario a enviar
// Este valor lo muestra Form Inspect al principio de cada fieldset colapsable
// También convertimos el nodo a un objeto
drupal_execute('page_node_form', $form_state, (object)$node);
?>

Al ejecutar ese código, una nueva página habrá sido creada en el sitio.

Cada uno de los valores de $form_state que se definieron en el código de arriba, pueden obtenerse del análisis de la información que provee Form Inspect.

Información de form inspect

Sin embargo, notará que hay valores que no aparecen en el array de form inspect, por ejemplo 'name', ¿cómo sabía que tienía que poner $form_values['name'] = 'admin';? ¿Recuerda los dolores de cabeza? Ahí tiene uno.

Creando nodos más complejos.

Por supuesto, la complejidad de sus tipos de contenido generalmente superarán a la estructura del tipo de contenido Página.

Si bien la forma de trabajo es la misma, Form Inspect seguramente fallará (a no ser que se repare el problema) a la hora de previsualizar el formulario. Es algún conflicto con jquery.

Es por eso que deberá desactivar el uso de krumo() para nodos más complejos. Simplemente renombre la carpeta devel/krumo a otro nombre diferente

cd devel
mv krumo krumo2

Ahora, la próxima vez que previsualice un nodo con Form Inspect verá dos fieldset colapsables que contienen la misma información que mostraba antes devel usando la biblioteca krumo.

Para este nuevo ejemplo, supongamos un tipo de contenido llama artículo (el texto del ejemplo lo tomé de aquí), con un título, un campo textfield de una sola linea que representa el autor, un campo de fecha y una término que indica la categoría del artículo (Deportes, Actualidad, Sociedad, Tecnología).

Completar el formulario

Ahora presione el boton Guardar.

Luego, en la pestaña de Devel Load observe la información que brinda Devel.

Información de Devel Load

En la imagen anterior solo estoy mostrando las partes más importantes para este script.

Observe que la taxonomía no es un array, sino un objeto (porque dice StdClass, en vez de array), por lo tanto deberá utilizar la sintaxis de objetos de PHP para crear el nodo con la categoría correcta.

Por otro lado, la estructura del array de referencias a usuario tiene varios [1], [2], [3] porque el campo permite múltiples valores.

Por ultimo cree su script para automatizar la creación de contenido. Tenga en cuenta que todos los valores tienen que ir dentro de $form_state['values'], usamos Devel Load simplemente para observar todos los valores finales del objeto $node.

<?php
$node
= array();
$node['type'] = 'articulo';

$form_state = array();

$form_state['values']['title'] = 'Google Wave';
// Body: Recortado simplemente para no ensuciar el código.
$form_state['values']['body'] = 'Explicarlo les confieso, es algo complicado, porque estamos hablando de una...';

// Taxonomia, Observe la sintaxis para el objeto
// No es necesario especificar el título, solo el id del vocabulario y el id del término
$form_state['values']['taxonomy'][2]->tid = array('tid' => 2, 'vid' => 2);

// Campos de CCK

// Resumen: Recortado simplemente para no ensuciar el codigo.
$form_state['values']['field_resumen'][0]['value'] = 'Google Wave fue presentado hoy...';

// Referencia a usuarios
$form_state['values']['field_autor'] = array(
 
0 => array('uid' => 9),
 
1 => array('uid' => 7),
 
2 => array('uid' => 10),
);

// Usuario que creo el nodo
$form_state['values']['name'] = 'admin';
$form_state['values']['op'] = t('Save');

drupal_execute('articulo_node_form', $form_state, (object)$node);
?>

Algunas notas importantes sobre este mecanismo.

Usted debería tener en cuenta que la creación de contenidos de esta forma está muy íntimamente relacionada con el funcionamiento interno de Drupal, por lo tanto deberá contemplar que:

  • El autor del contenido debe tener permisos para crear ese tipo de contenidos.
  • Los campos requeridos del tipo de contenido original deben tener su valor en $form_state.
  • Debe respetar exactamente la estructura que indica Form Inspect para el array $form_state.
  • Debe especificar correctamente el nombre del formulario a ejecutar.
  • Cuando utilice campos con múltiples valores, tenga en cuenta que debe modificar el [0] de $form_state por [1], [2], [3] según corresponda.

Operaciones complejas.

Hasta aquí, es probable que haya entendido el asunto bastante bien y haya logrado replicar los ejemplos.

Sin embargo, seguramente surjan cuestiones más complejas que necesite resolver. Trataré aquí algunas, pueden colaborar con sus experiencias a través de los comentarios.

¿Qué nodo se creó?

Tal vez le interese obtener el nid del nodo recién creado. Debido a que drupal_execute pasa $form_state como una referencia, luego de ejecutar la función, podrá revisar el valor de

<?php
$nid_del_nodo_recien_creado
= $form_state['nid'];
?>

Para obtener el nid del nodo recién creado.

Creando traducciones:

Suponga que tiene que crear un nodo en español y otro en inglés. He buscado soluciones a través de drupal_execute, pero no he logrado que funcione ninguna.

Sin embargo, revisando la tabla node se puede observar que por cada nodo se puede asignar un valor para el campo tnid.

tnid representa el id del nodo en el “idioma original del contenido”.

Supongamos entonces que usted crea un nodo en español que tiene como nid el valor 25.

Luego crea una traducción de ese nodo con el mismo método usando drupal_execute.

Como se indico antes, $form_state contiene el valor del id del nodo recién creado. Por lo tanto solo tiene que hacer un UPDATE a la tabla node de la base de datos definiendo el tnid como 25 de esta forma:

<?php
$id_nodo_original
= 25;
db_query("UPDATE {node} n SET tnid = %d WHERE nid = %d", $id_nodo_original, $form_state['nid']);
?>

En donde $id_nodo_original contiene el nid del nodo en el idioma original del contenido.

Creando usuarios y taxonomías:

drupal_execute() no sólo puede usarse para crear nodos, también se pueden crear vocabularios, término de taxonomías, usuarios y toda entidad que posea un formulario en drupal.

Les dejo a continuación dos ejemplos de como crear un usuario y un vocabulario de taxonomía.

<?php
// Crear un nuevo usuario
$form_state = array();

// Estos tres datos son requeridos
$form_state['values']['name'] = 'nombre del usuario';
$form_state['values']['mail'] = 'nombre@example.com';
$form_state['values']['pass'] = 'constraseña';

// Create new account es el nombre del boton para crear el usuario
$form_state['values']['op'] = t('Create new account');

// Observe como no es necesario un tercer parámetro
drupal_execute('user_register', $form_state);
?>

<?php
//Crear vocabulario
$form_state['values']['name'] = 'Categorias para contenido';
$form_state['values']['description'] = 'La descripcion para esta categoria';
$form_state['values']['help'] = 'Selecione un término para la categoría';
$form_state['values']['nodes'] = array(
   
'page' => 'page',
   
'articulo' => 'articulo',
  );

// Es importante añadir este archivo para que se cargue la funcion taxonomy_form_vocabulary
module_load_include('inc', 'taxonomy', 'taxonomy.admin');

// Realizar el envío
drupal_execute('taxonomy_form_vocabulary', $form_state);
?>

Usando estos códigos fuera de la interfaz Drupal:

Si usted quiere ejecutar estos scripts fuera de la interfaz de drupal deberá tener en cuenta que tal vez deba incluir ciertos archivos.

Para la creación de nodos deberá incluir la siguiente linea:

<?php
module_load_include
('inc', 'node', 'node.pages');
?>

y para la creación de términos de taxonomía o vocabularios:

<?php
module_load_include
('inc', 'taxonomy', 'taxonomy.admin');
?>

Si algún script falla indicando el error

warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'nombre_de_la_funcion' was given in /includes/form.inc on line 366.

Significa que la función 'nombre_de_la_funcion', no pudo ser cargada, con lo que tendrá que incluir antes el archivo .inc donde esté definida.

Resumiendo

Para automatizar la creación de un nodo, complete el formulario del contenido y presione Previsualizar. El array $form_state contendrá casi toda la información que necesita para construir el nodo de forma programática.

En teoría podría utilizar los valores que muestra la pestaña Devel Load una vez creado el nodo para generar el array $form_state['values'], pero como suele pasar en Drupal, esto no siempre está asegurado (recuerde el ejemplo de las traducciones de los nodos).

Luego genere un script y peguelo en un formulario básico de drupal, elija el formato de entrada código en PHP y ejecutelo.

Recuerde que si escribe este codigo en algún módulo, tal vez deba incluir algunos archivos como node.pages.tpl o taxonomy.admin.inc

Es claro entender que muchas de estas operaciones son prueba y error. No hay una regla única para aplicar este tipo de scripts, y mientras más sepa del funcionamiento de Drupal más fácil le será determinar cuales elementos son necesarios de incluir en el script y cuales no.

Buscando más información:

Cuando me tuve que enfrentar a mi primer desafío de importación, allá por mediados de 2007 pocos eran los que conocían bien como hacer esto en Drupal 5. Hoy en día con las facilidades que presenta Drupal 6 hay muchísima más documentación:

Todos los artículos que siguen están escritos en inglés, y sí, los he leído a todos (algunos, varias veces) para escribir esto:-)

Les deseo el mayor de los éxitos en la creación de contenidos de forma automática con Drupal. Y recuerden contar sus experiencias y problemas usando los comentarios.

Su voto: Nada Promedio: 4.7 (6 votos)

Contenido Relacionado

Comentarios

Explicación más que detallada

Hola Mariano realmente una explicación magistral, como apunte para los lectores de este articulo, yo tuve que realizar un trabajo de integración de nodos páginas automáticamente y Mariano cometió el error de agregarme a sus contactos de Gtalk, digo error porque de ese día en adelante no lo deje en paz hasta lograr terminar el scripts para poder integrar nodos paginas y sus traducciones en drupal, la explicación que Mariano está dando en este artículo es realmente magistral, si la hubiera tenido antes me hubiera ahorrado horas y horas de búsqueda en internet, realmente aprovecho este comentario para agradecerte públicamente Mariano, gracias a tu ayuda desinteresada pude salvar el trabajo, personas como Mariano que comparten esta clase de conocimientos son las que necesita la comunidad de Drupal para seguir creciendo, muy buen artículo, gracias por compartir.

Muchas gracias Herson. Es la

Muchas gracias Herson.

Es la idea de todo esto, ayudar para que otros aprendan. Así aprendí yo hace tiempo y sigo aprendiendo de esta forma.

Saludos

Muy interesante, tan solo dos

Muy interesante, tan solo dos apuntes:

Para crear nodos de forma programática es más sencillo usar el node_save por objetos, primero cargas un nodo similar al que quieres crear y luego lo guardas pasandolo a node_save. Para temas de debug y demás seguramente será mejor drupal_execute. Para usuarios se puede usar user_save de la misma forma.

No me gusta la forma en la que desactivas el krumo del Devel, hasta donde yo se, en las settings del módulo (admin/settings/devel) puedes poner el estado de krumo como disabled.

Un saludo!

Muchas gracias Pedro: Ya

Muchas gracias Pedro:

Ya estaba preguntandome quien iba a hacer referencia a node_save() :-)

Lo cierto es que realmente pueden crearse nodos con node_save, sin embargo, usar drupal_execute asegura que el Form API valide los elementos antes de crear el nodo, en última instancia terminará llamando a node_save()

Esto de alguna forma permite que en la base de datos no queden nodos "fallados". Por ejemplo si por alguna razón a node_save se le pasa un nodo sin título, lo creará de todos modos, con drupal_execute no sucederá eso.

De todas formas node_save es una opción valida, y más rápida (en tiempo de ejecución), para crear nodos de forma programática.

Con respecto a lo de desactivar krumo, a mi tampoco me gusta, pero revisando las opciones de devel no hay forma de desactivarlo desde la interfaz.

Saludos y gracias por el aporte.

Eres el mejor

Desde hace tiempo vengo pensandolo pero desde ya, eres el mejor blog de Drupal que he encontrado nunca. Casi todos los temas me gustan, me vienen bien o me parecen buenos para novatos y bien explicados.

No siempre se dice, pero si hace falta para que sigas escribiendo:
Buen trabajo

interesante

nteresante, sin duda lo leeré con más profundidad este fin de semana.

saludos

Luis Elizondo

Conseguir contenido de cualquier web

Hola,soy nueva
Me gustaria saber si puedo exportar todo el contenido de esta web de Jonas Brothers hacia Drupal . Quiero crear un club de fans y no quiero empezar desde 0.

No se mucho mucho de programación, pero todo sea por los Jonas. Sólo respondame Si o No. Please!!

Hola Ana Paula: Este artículo

Hola Ana Paula:

Este artículo trata sobre como importar los datos suponiendo que los mismos ya se encuentran en un formato interpretable. Como por ejemplo una base de datos, una planilla de cálculo o un archivo xml.

Importar el contenido desde una web de forma automática requiere de conocimientos avanzados de programación. Hay empresas que se dedican exclusivamente a eso, por lo que claramente demuestra que no es una tarea sencilla.

Sin embargo estuve revisando la web y a simple vista no contiene tanto material como para que valga la pena programar algo para obtener su contenido programando algo. La automatización se justifica cuando el tiempo que lleva importar todo es extremadamente superior al tiempo que lleva programar las tareas de automatización.

¿Mi humilde recomendación? Armate de paciencia y copia parte por parte.

Saludos y espero que sea de ayuda.

Para importar de una web a otra sin acceso a la primera...

Para importar de una web a otra sin acceso a la primera... yo usaría las funciones curl de PHP que simulan el comportamiento de un usuario al 100% (incluido el uso de cookies).

Enviar un comentario nuevo

¿Dudas, comentarios?. Anímese, y de su opinión sobre material. Procure cuidar su ortografía y publicar algo relacionado con el contenido.

Por consultas generales, utilice los Foros de Drupal Hispano. Los comentarios no relacionados con el contenido seran borrados.

Seguro que usted no se llama anónimo :)
El contenido de este campo se mantiene privado y no se mostrará públicamente.
Si posee un sitio web puede indicarlo en este campo
  • Las direcciones de las páginas web y las de correo se convierten en enlaces automáticamente.
  • Etiquetas HTML permitidas: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd><del>
  • Saltos automáticos de líneas y de párrafos.
  • Usted puede agregar código (genérico) utilizando etiquetas <code>...</code> o <?php ... ?> para código PHP resaltado.

Más información sobre opciones de formato

CAPTCHA
Esta pregunta es para asegurarnos de que usted es humano. Coloque las dos palabras separadas por un espacio.