Last updated 10 June 2011. Created on 8 April 2010.
Edited by batigolix, Karlheinz. Log in to edit this page.

If you're making a custom node type, chances are that your node type will involve images in some way. Though it is possible to write your own custom image handler, it is usually much better to integrate it with another module.

The Image module is a good solution, particularly with its image_attach component. This allows you to add images to any node type.

This tutorial will show you how to integrate image_attach with your custom node type.

It will also show you how to programatically create that node type. For the purposes of this tutorial, we're going to use our old friend, node_example.module.

As of this writing, the Image module is up to version 6.x-1.0-beta5. Prior to version 6.x-1.0-alpha6, the image_attach module could only attach one image per node; I'll explain the differences when they come up.

Enabling attached images

Enabling attached images for a node type is a simple matter of setting one variable to 1 or 0. That variable is named "image_attach_NODETYPE," where "NODETYPE" is your node type (probably also your module name). Note that you should use 1 and 0 rather than TRUE or FALSE; variable_set() serializes these values, so they are not exactly the same. (It will still function, but with one minor issue: the default value for "Attach images" on admin/content/node-type/NODETYPE will be empty.)

By default, you're allowed to attach any number of images to one node. If you'd like to limit that (or override any limit), the variable to set is "image_attach_maximum_NODETYPE." 0 (zero) means "unlimited images," not "zero images." Obviously, this variable has no effect prior to version 6.x-1.0-alpha6.

Since these variables will be meaningless without the image_attach module, you should do a call to module_exists() before you set them. In fact, this check is a good idea any time you want to work with another module.

Here is the finished code for node_example.module:

if (module_exists('image_attach')) {
  variable_set('image_attach_node_example', 1);
  variable_set('image_attach_maximum_node_example', 0);
}

If you know you're going to be using image_attach, you could include this code in hook_install() or hook_enable().

A more common situation is that you want to allow users to choose whether they should use image_attach or not. Users normally do this by editing your content type. But in certain circumstances you might want to make it part of your module's administration page.

One option is to have form fields named "image_attach_NODETYPE" and "image_attach_maximum_NODETYPE" in the settings form itself, thus setting the variables directly. This will simply duplicate the functionality you get when editing your content type.

Another option is to have a single field that checks whether the user wants to enable image_attach on your node type. Your custom submit function would then check the value of this field, and set the variables accordingly. This may not be necessary in most circumstances, but it does have its uses. For example, if you are changing between different image handling modules, you would want the user to just choose one from a select field, and have all of the variables for each module be set automatically. You could also use the custom submit function to add an image size, change the image directory, or whatever. Essentially, you could set whatever variables you like, all without any more user input.

For the details on setting up a module administration page, read the handbook page on Administration settings forms.

Programatically adding images to a custom node type

Now, let's say you're in a situation where you want to create your node from start to finish without any user input whatsoever. Perhaps you're pulling REST data from a third party; perhaps you're creating custom nodes from a data file.

There are essentially two methods to create nodes in Drupal. The first is to create a $node object, and call node_save() on that node object. However, in this case none of the data will be validated.

The other method is to create a $form_state array that mimics your custom node's form, and an empty $node object, and then call drupal_execute() on them. This is the method I prefer, so I'll use it here.

To attach images to your node, you must first copy the image files to your server (likely into Drupal's "temp" directory). Then, you will create image nodes from each file. Finally, you will create an array of image node nid's to put into $form_state.

The image files must be local for the image module to create nodes out of them. This is because it uses Drupal's file_copy() function, which only works with local files. Transferring files to your server is a bit beyond this tutorial, but you can start by looking at Example #3 in the fread() manual on PHP.net.

So, for this example, I'll assume you have an array called $urls, which contains local URL's to image files. Since we're creating the node_example nodetype, let's also assume you have variables called $color and $quantity, and of course $title and $body.

To mimic a user-created form, these values will need to be put into $form_state['values']. You will also need to set $form_state['values']['op'] to "Save."

The image fields in $form_state are different depending on what version of image_attach you are using. Prior to version 6.x-1.0-alpha6, the (single) image node's nid was put into a field called $form_state['values']['iid'], and this field could be unset without any problems.

After 6.x-1.0-alpha6, image_attach could store more than one image, so the field was changed to $form_state['values']['iids'], which contains an array of image node nid's. In addition, during validation the first field is always unset (in the form, it always contains the value "- None -"). This means that the field must always be present, even if there are no images attached to your node. In this case, it should be set to an empty array.

To avoid validation errors, you should always make sure you're not attaching more images than is allowed. To do that, you will need to create an iterator, and compare it with the image_attach_maximum_discogs variable. (Remember, zero is unlimited images.)

To get the image node nid's for each image, naturally we must first create the image nodes. This is done through image.module's image_create_node_from() function. That function takes the URL (and optionally a title, body, and taxonomy), and returns either an image node object, or FALSE on failure.

Your node's form ID is "yournode_node_form." Note that it is NOT the same form that you set up in your implementation of hook_form(). In our example, it will be "node_example_node_form."

Now, let's put it all together.

// Variables to insert into $form_state
$title    = "Don't panic!";
$body     = 'Always know where your towel is';
$color    = 'blue';
$quantity = '42';
$urls     = array('path/to/file/arthur.jpg', 'path/to/file/ford.jpg', 'etc',);

// Insert node variables
$form_state['values']['title']     = $title;
$form_state['values']['body']      = $body;
$form_state['values']['color']     = $color;
$form_state['values']['quantity']  = $quantity;

if (module_exists('image_attach') 
&& (variable_get('image_attach_node_example', 0) == 1)) {

  // Insert single image (pre-6.x-1.0-alpha6)
  // We'll set the image title, though it's not strictly necessary
  $img_title = t('@title image', array(
    '@title' => $form_state['values']['title'],
  ));
  $node = image_create_node_from($urls[0], $img_title);
  $form_state['values']['iid'] = $node->nid;
  
  // Insert multiple images (post-6.x-1.0-alpha6)
  $i   = 1;
  $max = variable_get('image_attach_maximum_node_example', 0);
  $form_state['values']['iids'] = array();
  foreach ($urls as $img_url) {
    if (($i < $max) && ($max != 0)) {
      $img_title = t('@title image @i', array(
        '@title' => $form_state['values']['title'],
        '@i'     => $i,
      ));
      $node = image_create_node_from($img_url, $img_title);
      if ($node) {
        $form_state['values']['iids'][] = $node->nid;
      }
      else {
        $error = t("Could not create image node from %url", array('%url' => $img_url));
        drupal_set_message($error, 'error');
      }
    }
    $i++;
  }
}

// Remember to set the 'op' value on $form_state
$form_state['values']['op'] = t('Save');

// Create a new node object and save node
$node = new stdClass();
$node->type = 'node_example';
drupal_execute('node_example_node_form', $form_state, $node);

Note: If you need to do something with this node after you create it, the $node object is passed by reference, so it will be fully populated.

Happy coding!

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.