I haven't been able to find information on how to programmaticly create a node in Drupal 7. Migrate module lookd like it can do it, but it is a bit overkill to learn just for this.

I have a Drupal 6 blog, and want to upgrade it to Drupal 7. Unfortunately, updating is not working, and I refuse to try updating again until the update path is more clearly defined with a beta release of D7.

So, instead I:

1) Set up fresh install of D7
2) Turn on blog module
3) Export node and node_revision SQL tables from D6 blog
4) Clean up node SQL data with some regex
5) Insert the SQL data into array (e.g., $node[0]['title'] = 'Why I like Drupal'; $node[1]['title'] = 'Drupal love';
6) Run the below to import into D7 (imagine about 60 nodes):

<?php
main();
function main() {
$nodes = array();
$nodes[1]['title'] = 'First post';
$nodes[1]['body'] = '<p>A node</p><p>A node which is a blog post with lots of stuff</p>';
$nodes[1]['teaser'] = '<p>A node</p>';
$nodes[1]['timestamp'] = 1281765101;
$nodes[1]['format'] = 2;
// about 65 more nodes that I don't want to paste here
make_nodes($nodes);
}

function make_nodes($nodes) {
	foreach($nodes as $new_node) {
		$node = new stdClass();
                $node->type = 'blog';
		$node->status = 1;
		$node->uid = 1;
		$node->title = $new_node['title'];
		$node->promote = 1;
		$node->created = $new_node['timestamp'];
		$node->timestamp = $new_node['timestamp'];
		$node->sticky = 0;
		$node->format = 3;
		$node->language = 'en';
		$node->teaser = $new_node['teaser'];
		$node->body = $new_node['body'];
		$node->revision = 0;
		node_save($node);
	}
}

The nodes get created, but without the field data from the body. I'm guessing there is some magic I need to do with the new field API, like using field_attach or perhaps setting some field or entity value in the node object, but so far I haven't found any information on this on d.o. Any suggestions or able to point me to the right page on how to programmatically create nodes in D7?

Side note: I can get it to work if I manually insert the data with a script into field_data_body and field_revision_body tables, but this is neither ideal nor the correct way to be doing things.

Comments

lorinpda’s picture

Hi,
You are really close. You just need a couple changes.

Note! I tested this for the Drupal 7 Alpha 6 release. Therefore, the following simply gets you through the work around you've described in your post. This a not a comprehensive migration example (for folks reading this after a Drupal 7 production release).

First, add a "changed" attribute to your node structure:

   $node->changed= $new_node['timestamp'];

Next, "body" is removed from the default node structure/instance in Drupal 7 (as you implied). Thus, change:

   $node->format = 3;
   $node->teaser = $new_node['teaser'];
   $node->body = $new_node['body'];

to

   $node->body['und'][0]['format'] = 3;
   $node->body['und'][0]['summary'] = $new_node['teaser'];
   $node->body['und'][0]['value'] = $new_node['body'];

With those changes I was able to generate blog posts programmatically

    node_save($node);

(i.e. made same call, per your post) .

Hope that helps.

zero cool’s picture

HI. i am working on a project to import xml contents into drupal 7. i have parsed all the data in php. so far i have been succeded in importing node body and its title. There is no documentation for drupal 7 on how to attach image to the node and tags. i really need help i have spend two days finding it tried a lot. i will be very thank you for someone. please just guide me somewhere

function make_nodes($nodes)
{
$new_node = $nodes[0];
$node = new stdClass();
$node->title = $new_node['title'];
$node->body['und'][0]['value'] = $new_node['body'];
$node->type = 'article';
$node->created = $new_node['timestamp'];
$node->changed= $new_node['timestamp'];
$node->status = 1;
$node->promote = 1;
$node->sticky = 0;
$node->body['und'][0]['format'] = 1;
$node->uid = (isset($local_user->uid) && !empty($local_user->uid)?$local_user->uid:1);
$node->language = 'en';
$node->timestamp = $new_node['timestamp'];
$node->revision = 0;
node_submit($node);
node_save($node);
}

dbaonguis’s picture

In D7, when you use node_submit(), you should get the returned value before passing it to node_save(). So in the above code.. it should be like so:

$node = node_submit($node);
node_save($node);

coderintherye’s picture

Thanks much, this did indeed solve the problem. Though I changed 'und' to 'en' since I had English enabled as the language type. I'm working on a writeup of this for anyone else who wants to go through this process before a proper migration path is available.

bloke_zero’s picture

This works with D7 beta 2 - it needs refining though!

I've exported my nodes using views bonus as XML with simpleXML (hence the casting as string) and already got the images in sites/default/files - look out for D7's file stream i.e. public:// & private:// - and re-imported it using the code above plus:

    //File?
    $fpURL = explode('/', (string)$new_node->field_image);
    //create file object
    $file = new stdClass();
    $file->uid = '1';
    //the file is already in sites/default/files - which is D7 public://
    $file->filename = $fpURL[3];
    $file->uri = file_build_uri($fpURL[3]);
    $file->filemime = file_get_mimetype((string)$new_node->field_image);//this is the complete path
    $file->status = '1';   
    $savedFile = file_save($file);
    $savedFile =file_load($savedFile->fid);//do we need to do this?
    //slot into node - do we have to do all this?  Probably not...
    $node->field_image['und'][0] = array(
      'fid' => $savedFile->fid,//file id is key - now registered as file in system
      'alt' => $node->title,
      'title' => $node->title,
      'uid' => '1',
      'filename' => $savedFile->filename,
      'uri' => $savedFile->uri,
      'filemime' => $savedFile->filemime,
      'filesize'=> $savedFile->filesize,
      'status' => '1',
    );

    node_submit($node);
    node_save($node);

Anyone got a more elegant way?

mattjbondi’s picture

Saved me after hours of searching!! Works like a charm.

nzcodarnoc’s picture

Here's my take on it

$existing_filepath = "/home/nzcodarnoc/sites/default/files/imported/picture.jpg"
$new_filepath = "public://picture.jpg"
// Load a node
$node = node_load($nid, NULL, TRUE);
// Create the file object
$drupal_file = file_save_data(file_get_contents($existing_filepath), $new_filepath);
$drupal_file->alt = $node->title;
$drupal_file->title = $node->title;
// Assign the file object to the node, as an array
$node->field_my_file[$node->language][0] = get_object_vars($drupal_file);
// Save the node
$node_save($node);

The downside of this approach is that really big files can cause out of memory errors as they are read entirely into memory during the execution of the above code.

Jonah Ellison’s picture

Here's an example that uses minimal code by prefilling node and image file field values with the help of node_object_prepare() and file_copy(). This is currently used to import nodes with an image.

/**
 * Node save example with image.
 * Requires $filepath to be defined with absolute path to image.
 */ 

// Create node object.
$node = new StdClass();
$node->type = 'image';
$node->language = LANGUAGE_NONE;
node_object_prepare($node);

$node->title = $filepath;    

$node->body[$node->language][0]['value']   = $body_text;
$node->body[$node->language][0]['summary'] = text_summary($body_text);
$node->body[$node->language][0]['format']  = 2;

// Create managed File object and associate with Image field.
$file = new StdClass();
$file->uid = 1;
$file->uri = $filepath;
$file->filemime = file_get_mimetype($file->uri);
$file->status = 1;      

$dest = file_default_scheme() . '://image'; // Subdirectory name within files directory. ($dest is optional.)
$file = file_copy($file, $dest);
      
$node->field_image['und'][0] = (array)$file;

node_save($node);
Alexander Matveev’s picture

// replace
$node->body[$node->language][0]['format']  = 2;
// with 
$node->body[$node->language][0]['format']  = 'filtered_html';
redsd’s picture

In order to save a file to a file field you need to extra variables compared to the above example of saving to the image field
The code will look something like this:

/**
 * Node save example with image.
 * Requires $filepath to be defined with absolute path to image.
 */ 
$filename = 'test.pdf';
$filepath = 'sites/all/my_files/test.pdf';

// Create node object.
$node = new StdClass();
$node->type = 'story';
$node->language = LANGUAGE_NONE;
node_object_prepare($node);

$node->title = $filename;    

$node->body[$node->language][0]['value']   = $body_text;
$node->body[$node->language][0]['summary'] = text_summary($body_text);
$node->body[$node->language][0]['format']  = 'filtered_html';

// Create managed File object and associate with file field.
$file = new StdClass();
$file->uid = 1;
$file->uri = $filepath;
$file->filename = $filename;
$file->filemime = file_get_mimetype($file->uri);
$file->status = 1;      

$dest = file_default_scheme() . '://'.$filename; // Subdirectory name within files directory. ($dest is optional.)
$file = file_copy($file, $dest);
$file->display = 1;
$file->description = "";
      
$node->field_my_files['und'][0] = (array)$file;//the name of the field that requires the files

node_save($node);
dkoukoul’s picture

Hi guys I have a similar problem...
I have managed to add nodes programmaticaly but I can not reference the taxonomy terms that I want selected....
I have tried this
$node->taxonomy[4] = 12;
$node->taxonomy[8] = 30;
$node->taxonomy[5] = 20;
and this
array(4=>array(12),8=>array(30),5=>array(20));
but they don't seem to work...
any ideas?
Thanx

aqua_linksunten’s picture

Hey,

this code works for me:

$node->field_tags[$node->language][]['tid'] = 25;

Source: http://fooninja.net/2011/04/13/guide-to-programmatic-node-creation-in-dr...

What i can't manage so far is adding a date-field. (I want to add events).

Any ideas?

ohnobinki’s picture

Have you finished/posted this writeup, nowarninglabel?

coderintherye’s picture

Well, not exactly, but you can read what I did write here: http://www.coderintherye.com/node-export-import-d6-d7

Cheers.

wrd’s picture

And frankly I'm confused. I'm trying to create four nodes, one for the "page" content type, and one for the "alert" content type.

The Pages are created without any problems:

  for ($i=1; $i<=3; $i++) {
   $node = new stdClass();
   $node->type = 'page';
   $node->title = "Menu Item $i";
   $node->language = LANGUAGE_NONE;
   $node->uid = 1;
   node_object_prepare($node);
   $node->body[$node->language][0]['value'] = "Landing page for item $i.";
   $node->body[$node->language][0]['summary'] = "Landing page for item $i.";
   $node->body[$node->language][0]['format'] = 'raw_html';
   $node = node_submit($node);
   node_save($node);
  }

...but the Alert is created with an empty body field:

  $node = new stdClass();
  $node->type = 'alert';
  $node->title = 'Information about the Lorem Ipsum Alert System';
  $node->language = LANGUAGE_NONE;
  $node->uid = 1;
  node_object_prepare($node);
  $node->status = 1;
  $node->promote = 1;
  $node->body[$node->language][0]['value'] = 'Lorem ipsum dolor sit amet.';
  $node->body[$node->language][0]['summary'] = 'Lorem ipsum dolor sit amet.';
  $node->body[$node->language][0]['format'] = 'raw_html';
  $node = node_submit($node);
  node_save($node);

Both content types are using the default body field, and UID 1 is permitted to use the raw_html text format. Any thoughts?

coderintherye’s picture

Perhaps try also setting the 'safe_value' and you could try directly with the language, e.g.,

$node->body['und'][0]['value'] = 'hello';
$node->body['und'][0]['safe_value'] = check_plain('hello');

micheas’s picture

Make sure that the body field really is body and not something like field_body.

The body field is no longer guaranteed to be there in d7.

veeray’s picture

I cant get the body to save. I've tried all the code from this thread as well as many other threads on node creation.

ed.hollinghurst’s picture

I found that this worked for me:

<?php
  $node = new stdClass();
  $node->type = 'node';
  $node->type = 'page';
  $node->title = 'Title of new node';
  $node->language = LANGUAGE_NONE;
  $node->uid = 1;

  $node_wrapper = entity_metadata_wrapper('node', $node);
  $node_wrapper->body->set(array('value' => '<p>New content</p>', 'format' => 'full_html'));

  node_save($node);
?>

However in order to use Entity Metadata Wrappers you do need to have the Entity API module installed.

danylevskyi’s picture

I'll recommend you to use drupal_form_submit() function for programmatically node creation.
Only this function fires all hooks.

Here you can find an example:
http://d.danylevskyi.com/node/6

lahode’s picture

In theory I agree with you however in practice using files doesn't work and ends up always with a warning: "Managed file: the file used in the field may not be referenced", and no nodes are created.

  // I took this code in (file_entity.pages.inc)
  $source = $uploaded_file['tmppath'];
  $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']);
  $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
  $file = file_uri_to_object($destination);
  $file->status = FILE_STATUS_PERMANENT;
  $file->display = 1;
  $file->description = "";
  file_save($file);
    
  // Create node
  module_load_include('inc', 'node', 'node.pages');    
  $node = (object) array(
    'type' => 'fichier',
    'language' => LANGUAGE_NONE
  );
  node_object_prepare($node);
  
  // This doesn't work with drupal_form_submit()
  $form_state = array();  
  $form_state['values']['title'] = $values['title'];  
  $form_state['values']['name'] = $user->name;
  $form_state['values']['op'] = t('Save');
  $form_state['values']['field_fichier'][$node->language][0] = (array)$file;   
  drupal_form_submit("{$node->type}_node_form", $form_state, $node);   

  /* But this work! with node_save()
  $node->title = $values['title'];
  $node->field_fichier[$node->language][0] = (array)$file;
  node_save($node);
  */

All other examples I found on the web uses node_save(), I would be very happy if you could explain us how to make it work with drupal_form_submit

Thanks in advance

Fredrik Lahode
Hôptaux Universitaires de Genève - Suisse