Multiple nodes from single node submission in Drupal

At Advomatic we recently had a use case to create multiple nodes from a single node submission.

This is something that Drupal can handle relatively easily but, it is just not that intuitive so here is a tutorial on how I accomplished this.

This tutorial will cover how to create two nodes, an image node and a text node, from a single submitted node.

So you will be able to have this

And turn it into this



Requirements for this tutorial

** This should work with other versions but, I cannot guarantee that it will work exactly the same.
*** Please install these modules by following their individual install guides.

  • Drupal 6.x
  • CCK 6.x-2.x
  • ImageFild 6.x-3.0
  • FileFild 6.x-3.0
  • ImageAPI 6.x-1.6

Step 1

The fist thing that we have to do is create the content types that we are going to use.

The submitted node

This node will contain the fields that should be associated with the submitted node along with the fields that are associated with the two created nodes.
The reason that we want to add the fields that will be associated with the created nodes is for several reason.

  • To allow the field(s) be validated natively by the cck widget, for security reasons.
  • It’s just easier that way!

The image node

The text node

Step 2

Now that thats done we get to write some code YAY!

So for this demonstration I created a module called “multisubmit”.

So create a module with the following layout.

  • multisubmit
    • multisubmit.info
    • multisubmit.module

Now just incase this is your first module be sure to create your info file, it should look something like this.

; $Id:$
name = Multisubmit
description = Create 'created' nodes from 'submitted' node.
dependencies[] = content
dependencies[] = imagefield
core = 6.x

Now thats done we get to start the fun stuff.

For this tutorial we will be using a couple key Drupal API hooks hook_nodeapi and hook_form_alter.
We will be using hook_nodeapi for a lot of our heaving lifting. The hook_nodeapi will be used for actually creating our two additional nodes along with removing the data from our original submitted node.
The hook_form_alter function will be used to remove the two fields from the submitted node’s edit form, since the content is no longer part of that node.

So the first thing we want to do is create our multisubmit_nodeapi function inside of module.

type == 'submitted') {
switch ($op) {
case 'presave':
// If the node has an image then save it for later use and unset it on the submission node.
multisubmit_get_image_field($node);
// If the node has text then save it for later use and unset it on the submission node.
multisubmit_get_text_field($node);
break;
case 'insert':
// If the node object had an image then create our content image node.
if ($node->multisubmit_image) {
$form_state = array();
$tmp_node = new stdClass();
$tmp_node->type = 'image';
$form_state['values']['type'] = $tmp_node->type;
$form_state['values']['status'] = 1;
$form_state['values']['title'] = $node->title;
$form_state['values']['uid'] = $node->uid;
$form_state['values']['name'] = $node->name;
$form_state['values']['op'] = t('Save'); // This seems to be a required value
// Get the image that we saved from earlier.
$tmp_node->field_image = multisubmit_get_image_field();
// Create the content.
drupal_execute($tmp_node->type . '_node_form', $form_state, &$tmp_node);
}
if ($node->multisubmit_text) {
// Create text node
$form_state = array();
$tmp_node = new stdClass();
$tmp_node->type = 'text';
$form_state['values']['type'] = $tmp_node->type;
$form_state['values']['status'] = 1;
$form_state['values']['title'] = $node->title;
$form_state['values']['uid'] = $node->uid;
$form_state['values']['name'] = $node->name;
$form_state['values']['op'] = t('Save'); // This seems to be a required value.
// Set the text field on the text node.
$form_state['values']['field_text'] = multisubmit_get_text_field();
// Create the content.
drupal_execute($tmp_node->type . '_node_form', $form_state, &$tmp_node);
}
break;
case 'load':
break;
}
}
}
?>

Now you will notice there are two functions inside of our multisubmit_nodeapi that do not exist yet.

One thing that I know will come up is “Why didn’t you use node submit?” well for some of the more complicated form elements such as the imagefield it does not work with just a regular node save :( and I have found that a lot of CCK fields are like that so just to bypass that I used the form submission handlers.
Now node_save might work on some CCK fields but, not all.

  • multisubmit_get_image_field
  • multisubmit_get_text_field

These two functions are used to remove the data from the submitted node and create a static variable that we can use later in the node submission. The reason that these are here is because once the node makes it to the $op insert it has probably already been inserted therefore all of the content has already ben associated with the submitted node, which we don’t want. So before it gets to insert we remove the data in presave. This is a pretty safe place to remove the content because it has already ben validated at this point.

field_image) {
if (!empty($node->field_image[0]['fid'])) {
$image_field = $node->field_image; // Set the image field for later use.
unset($node->field_image); // Unset the image field on the submitted node.
// This is token place holder so that we know that we need to create the image node later.
$node->multisubmit_image = TRUE;
}
return;
}
return $image_field;
}

/**
* Sets/Gets the text field from the submitted node.
*
* @param $node object
* The node object
*
* @return array or NULL
* If $node is passed in then it returns nothing.
* If you are getting the text field that was set then it returns the text field.
*/
function multisubmit_get_text_field(&$node = NULL) {
static $text_field;
if ($node->field_text) {
if (!empty($node->field_text[0]['value'])) {
$text_field = $node->field_text; // Set the text field for later use.
unset($node->field_text); // Unset the text field on the submitted node.
// This is token place holder so that we know that we need to create the text node later.
$node->multisubmit_text = TRUE;
}
return;
}
return $text_field;
}
?>

Step 3

Enjoy the fruits of your labor

Now once you have the multisubmit module built you should be able to do the following.

From the following

Step 4

Now at this point we have already created our two nodes from our single node submission but, we still have the two fields still associated with the node on edit :(.

So in order to get rid of these we need to remove them from the form in hook_form_alter on edit.

nid)) {
unset($form['field_text']);
unset($form['field_image']);
}
}
}
?>

Now thats much better