I found that creating nodes with CCK-fields programmatically in D6 is somewhat a pain in the ass. I've had a problem with this issue for several weeks and I've done alot of debugging and
looking for documentation to find a good way of handling this. However, most documentation was incomplete, outdated, only compatible with D5 or so well hidden in other posts that it
was overlooked easily. That's why I decided to post my findings and personal way of working here, in the hope that it will save some other people, which come across the same issue,
alot of time.

There are alot of discussions out there regarding the use of node_save() Vs. drupal_execute(). I don't want to go much deeper into that matter right now. I myself prefer drupal_execute(),
despite it's complexity.

The following code presents a couple examples of various field-types. This is the minimum required for this method to work. There's probably a bunch of other stuff that you might want to
define, but I'm just focusing on the CCK-fields here.

function addSomeStuff() {
	module_load_include('inc', 'node', 'node.pages'); // required
	$node = array('type' => 'mytype');
    $form_state = array();
    $form_state['values']['title'] = 'someText'; // node's title
	
    $form_state['values']['field_user']['uid']['uid'] = someUid; // user reference. (Example: 1) 
	$form_state['values']['field_node']['nid']['nid'] = someNid; // node reference. (Example: 143)
	$form_state['values']['field_text'][0]['value'] = 'someText'; // regular text field
	$form_state['values']['field_number'][0]['value'] = 10; // regular integer field
	$form_state['values']['field_checkbox']['value'] = 1; // on/off checkbox field
	
    $form_state['values']['op'] = t('Save'); // required value
    drupal_execute('mytype_node_form', $form_state, (object)$node);
    
	// you can probably configure the node-author in $form_state or $node, 
	// but i'm doing it this way to demonstrate the use of $form_state['nid'].
	// the following lines aren't required, but otherwise the node-author will be "Anonymous" in this case.
	$node = node_load($form_state['nid']); // nid from the node that gets created is set in $form_state['nid'] after drupal_execute()
    $node->uid = 1; // set author to admin
    node_save($node);
}

Please feel free to leave additions or comments. I may have overlooked some field-types or possibilities.

This method should work for Drupal 6.10 and CCK 6.x-2.2. Earlier or later versions may not be compatible with this solution.

Other useful links: http://drupal.org/node/178506

Kind regards,

mertens

Comments

pobster’s picture

This will time out if you try using it on multiple saves at once, I prefer node_save as you can implement it into the batch_api.

Pobster

yfreeman’s picture

There was a bug in the D6 which is now fixed in D7
see http://drupal.org/node/297972
it is supposedly backported to D6, but not sure on the status.

There is a workaround to get drupal_execute() to run in the BatchAPI
http://drupal.org/node/283594

@mertens
Greate work, will probably save me hours later

freestylegary@yahoo.com’s picture

I am trying to do something like this-
1. Create a new node A
2. On submit automatically create new node B
a. Get submit values from node A
b. Process those values
c. Submit values to node B

The issue i am facing is, I can't figure out how to get the values from form A and pass them along.

John Carbone’s picture

Thanks for posting a complete example. I was struggling with this one for a while. Very much appreciated!

IncrediblyKenzi’s picture

In response to the question about referencing values from one form in another, after calling drupal_execute on a node form, you can inspect the $form_state['nid'] to fetch the id of the saved node and reference that. (I think that's mentioned in the example above, but just so you know). Just do a node_load($form_state['nid']) and then grab values from that.

freestylegary@yahoo.com’s picture

Thanks for the tip. After racking my brains, I had decided to go with CCK computed field instead which works great for my requirement.

hutch120’s picture

I must have read a couple of hundred blog entries and tried dozens of different variations which didn't work before I saw this. Thank You!

The examples you provided were a good start, but some fields I'm using are a bit more complicated and below is how I figure out the parameters to use to UPDATE any field in any node (Again after reading a lot of crap to figure this out), not sure if this is the right place for it, but I think others who find this useful will also want to be able to update nodes. So here it is:

This example is for a datestamp field but will apply to any field type.

$node = node_load($nid);
$node->field_my_datetime[0][value] = time(); // ** LINE 2 **  See below.
$node->field_my_datetime[0][timezone] = 'Australia/Perth';
$node = node_submit($node);
node_save($node);

So the thing that took the longest to figure out was, how do you figure out what LINE 2 or 3 should be? Well I output the node as an array using node_build_content before updating to see the original value, before submitting to see the changed value and before saving to see if submitting had altered the node.

$node = node_load($nid);
print("<BR>Before Change<BR><pre>"); print_r(node_build_content($node)); print("</pre>");
$node->field_my_datetime[0][value] = time(); // ** LINE 2 **  See below.
$node->field_my_datetime[0][timezone] = 'Australia/Perth';
print("<BR>After change before submit<BR><pre>"); print_r(node_build_content($node)); print("</pre>");
$node = node_submit($node);
print("<BR>After submit before save<BR><pre>"); print_r(node_build_content($node)); print("</pre>");
node_save($node);

Examine the output and you will see elements like this:

[field_my_datetime] => Array
        (
            [0] => Array
                (
                    [value] => 1258687683
                    [timezone] => Australia/Sydney
                    [timezone_db] => UTC
                    [date_type] => datestamp
                )
        )

Basically if you take the elements in the brackets [] that translates to Line 2 and 3 above.

It might be worth noting that this requires you to be able to save a node in the first place and you might be having trouble working out the mandatory fields. So if you are trying to figure that out, save a node using the form GUI and then hard code that node ID into the above code to see the array.

MorningCoffee’s picture

Anyone come across the node being created without a type?!

I'm specifying the type as the example does in the beginning.

When I query the db to see if the node was created, I can see that it indeed WAS created but it has no 'type' associated with it.

pobster’s picture

If you're using node_save you need to specify like this;

$form->type = 'page';

Bear in mind that the way you think your content type is formatted may not actually be correct? (i.e. hyphens/ underscores)

Pobster

wideawake’s picture

that you are seeing a different effect of the 0 uid problem detailed below?

wideawake’s picture

Firstly, many thanks for posting this example of creating nodes programmatically, it was a really helpful step along the way to creating a file import routine that creates a new node for each line imported.

It worked beautifully until the last 3 lines!

Even though the $form_state['nid'] was definitely being set to the correct value, tne node_load would only return a blank node. I have no idea why this should have happened, and it is probably something I have done, even though I have checked the code many times, but I thought I would make this post in case others have a similar problem.

The end result was that 2 nodes were created - one with all the data and uid = 0, and another with no data and the correct uid! The former doesnt show in the content list because uid=0, the latter does but is blank. It is only by looking at the database that I could see what had happened.

After trying all sorts of things to remedy this, the solution I finally came up with was to remove the last 3 lines, create a nodeapi hook for op='presave', and force $node->uid = $user->uid.

It works, but I dont really understand why I have had to do it when on the face of it the last 3 lines should have done it in the first place. Can anyone explain it?

dooug’s picture

I found that I had to set

<?php
  $form_state['values']['name'] = $user->name;
?>

I also found the following to be very helpful. You can edit and preview a node of NODETYPE and see what the $form_state['values'] array should be.

function MYMODULE_form_alter(&$form, $form_state, $form_id) {
  // ensure we are modifying the right node type
  if ($form_id == 'NODETYPE_node_form') {
    // add an additional validation handler
    $form['#validate'][] = '_MYMODULE_node_form_validate';
  }
}

function _MYMODULE_node_form_validate($form, &$form_state) {
  // display the submitted data and then die
  echo "<pre>";
  print_r($form_state['values']);
  echo "</pre>";
  die;
}

Credit to: http://thedrupalblog.com/programmatically-create-any-node-type-using-dru...

kwesistewart’s picture

I'm creating nodes from hook_cron and drupal_execute is not altering the 'nid' in $form_state. Anyone know about this?

John Carbone’s picture

I needed to create nodes with cron too. In the end I never used $form at all. I just created a new node object and passed in my variables then did a node_save. Here's a bit of the relevant function.

/**
* Imports an individual property into the system
*
* @param $property
*       array of property values to be updated
*/
function update_property_import_property($property){
    $node = (object) NULL;

    $node->type = 'property';
    $node->title = $property['prop_id'];
    $node->field_num[0]['value'] = $property['address_num'];
    $node->field_ext[0]['value'] = $property['address_ext'];
    $node->field_street[0]['value'] = $property['street'];
    $node->field_owner[0]['value'] = $property['owner'];
    $node->uid = 1;

    // Save the record
    node_save($node);
}

Hope this helps.

pobster’s picture

You might want to consider passing $node through node _validate and _submit like this;

node_object_prepare($node); // prepopulate with defaults and publishing options...
// Then populate with your code above...
// ... ...
// ...
node_validate($node);
$node = node_submit($node);
// Save the record
node_save($node);

It'll populate the teaser, uid, creation time, etc in a more Drupally way.

Pobster

kekkis’s picture

My Drupal 6.16 says 'fatal error' when I try to run the node_object_prepare function. Any pointers?

pobster’s picture

Yeah read your php error log and see what the fatal error is... Sounds like a red herring to me, sounds like your script is flawed. Look to the API to see what you need; http://api.drupal.org/api/function/node_object_prepare/6

Pobster

kekkis’s picture

It says 'call to undefined function' which, I take, is because I fail to include the node.pages.inc file where the function resides. The node.module is somehow automagically included into my custom module but not the inc file. I should probably include it myself then.

pobster’s picture

Ah yes, apologies that should have been more obvious to me... My code was based against the snippet at the top of the page which already has;

function addSomeStuff() {
    module_load_include('inc', 'node', 'node.pages'); // required

Pobster

kekkis’s picture

Thanks, that is indeed a handy function, won't have to resort to drupal_get_path. :)

tmrhymer’s picture

I'm looking to do something similar to what you did in hook_cron to create a node, but it's a little complicated. I'm going to need to create hundreds of nodes every day based on a couple csv files that i will parse. In one of the csv's im going to need to create 2 separate nodes, and create a node reference between the two. Is your method good for creating multiple nodes in bulk or is there a more efficient preferred way?

greggles’s picture

If you want to set the text input format for a bit of text:

    $form_state['values']['field_text'][0]['value'] = 'someText'; // regular text field
    $form_state['values']['field_text'][0]['format'] = 1; // regular text field

dragonwize’s picture

I found the easiest way to determine the $form_state array hierarchy is to look at the "name" attribute of the form element in the HTML source code.

It gives the exact values to use in the array.

deg’s picture

Your code works great for assigning one uid to a user reference field. I'd like to add a uid to all of the uids already in the field. Any tips on assigning multiple uids to a user reference field?

tfranz’s picture

Thank you for your Code!
I tried to import data to CCK FileField (referring to http://drupal.org/node/330421), but unfortunately it doesn't work ...

addSomeStuff();
function addSomeStuff() {

	$mime = 'image/jpeg'; 
	$file_drupal_path =  'sites/default/files/myImage.jpg'; // Image in 
	$file = new stdClass();
	$file->filename = basename($file_drupal_path);
	$file->filepath = $file_drupal_path;
	$file->filemime = $mime;
	$file->filesize = filesize($file_drupal_path);
	
	$file->uid = $uid;
	$file->status = FILE_STATUS_PERMANENT;
	$file->timestamp = time();
	drupal_write_record('files', $file);

    module_load_include('inc', 'node', 'node.pages'); // required
    $node = array('type' => 'story');
    $form_state = array();
    $form_state['values']['title'] = 'My new Node'; // node's title

    $form_state['values']['field_image']  = array(
      '0' => array(
      'fid' => $file->fid,
      'title' => basename($file->filename),
      'filename' => $file->filename,
      'filepath' => $file->filepath,
      'filesize' => $file->filesize,
      'mimetype' => $mime,
      'data' => array(
        'description' => basename($file->filename),
      ),
      'list' => 1,
   ),
  );
    
    $form_state['values']['op'] = t('Save'); // required value
    drupal_execute('story_node_form', $form_state, (object)$node);
    
    $node = node_load($form_state['nid']); // nid from the node that gets created is set in $form_state['nid'] after drupal_execute()
    $node->uid = 1; // set author to admin
    node_save($node);
}

If i execute this script, i receive a broke node and the following error:

Referencing to the file used in the Image field is not allowed.
warning: Invalid argument supplied for foreach() in /is/htdocs/wp1135826_X3HMP62N7A/www/dwb/sites/all/modules/cck/content.module on line 1244.
warning: Invalid argument supplied for foreach() in /is/htdocs/wp1135826_X3HMP62N7A/www/dwb/sites/all/modules/cck/content.module on line 1284.
warning: Invalid argument supplied for foreach() in /is/htdocs/wp1135826_X3HMP62N7A/www/dwb/sites/all/modules/cck/content.module on line 1284.
warning: Invalid argument supplied for foreach() in /is/htdocs/wp1135826_X3HMP62N7A/www/dwb/sites/all/modules/cck/includes/content.token.inc on line 42.

Any help for importing an image into CCK Filefield?
Thank you very much!

Tobias

vomitHatSteve’s picture

We're probably well past the time when this can help you, but in case some other poor sap ends up googling this as I did, here's the solution that I found:

Before it will let you attach an imagefield to a node like this, filefield_widget_validate checks to make sure that something somewhere in the system references the file. Presumably, the built-in checks are able to find newly-uploaded files, but they can't find existing references to manually-saved files like this.

It invokes hook_file_references to count the number of references to the file, and if it finds 0 references, it throws out your error.

You can fix this by implementing hook_file_references in your module.
e.g.

function MYMODULE_file_references($file) {
  // find a way to pass your $file->fid to this hook after drupal_write_record
  //   variable_set/variable_get would probably work
  $my_fid = variable_get('MYMODULE_my_fid', 0);
  if ($file->fid == $my_fid) {
    return 1;
  }
}
darnzen’s picture

I was pulling my hair out on this trying to use node_reference and user_reference fields.

What I got to work was:


/* I needed to set the following $form_state values in order to get the form to validate */
$form_state['values']['field_user_ref'][0]['uid']['uid'] = some_user_name; // had to use name NOT uid
$form_state['values']['field_node_ref']['nid']['nid'] = some_nid;

/* run drupal_execute */
drupal_execute('my_node_form', $form_state, $some_node);

/* But the user ref and node ref values would save as NULL's so I had to follow up with */
$node = node_load($form_state['nid']);
$node->field_user_ref[0]['uid'] = some_uid;  // NOW it's uid, not user_name
$node->field_node_ref[0]['nid'] = some_nid;
node_save($node);

/* now everything is stored correctly */

Running following versions:
drupal 6.19
cck 6.x-2.9
views 6.x-2.12

The user field was set to autofill text entry, and the node field was set to select list. This seems to play some role as changing node field to auto-fill text entry changed the expected input to "node_title [nid:###]'

Also, I could have sworn I had this working at one point, but sometime after upgrading cck and views, it stopped until I changed code to what I have entered above.

vj’s picture

This is for autocomplete text field


<?php

 $form_state = array();
 module_load_include('inc', 'node', 'node.pages');
$node = array('type' => 'content_type');
$form_state['values']['field_user_ref'][0]['uid'] = array('uid' => '[uid:' . $user->uid . ']');
$form_state['values']['field_node_ref'][0]['nid'] = array('nid' => '[nid:' . $nid . ']');
$form_state['values']['op'] = t('Save');

drupal_execute('my_node_form', $form_state, (object)$node);

?>
pobster’s picture

Wow... Ummm... Why on earth would you use both drupal_execute AND node_save in the same function??? That's kind of defeating the point...

Pobster

darnzen’s picture

Yeah I know. That was the "problem" and why I posted here. I couldn't get it to work using only $form_state and drupal_execute and needed to use a node_save for some of the data. I've since converted that portion of the code to use only node_save as a work-around, but I wanted to understand what I was missing about those specific fields with regards to drupal_execute.

EDIT: My original comment didn't make much sense.

anonymous07’s picture

This is a very useful thread. Thank you

emanaton’s picture

I got the date fields working with the drupal_execute scheme using the following format; note that my field has a from and to defined, hence the 'value2' here:

  $form_state['values']['field_dates'][0] = array(
    'value' => array(
      'date' => '09/01/2011',
      'time' => '03:00',
    ),

    'value2' => array(
      'date' => '09/29/2011',
      'time' => '04:00',
    ),
  );

Happy coding!

Sean

mpaler’s picture

Another obscure tip...

Make sure the op value in your script

$form_state['values']['op'] = t('Save'); // required value

matches the submit button value on your node form. I had node form settings enabled and the values didn't match. Took some time to uncover that small detail.