I know it may be difficult or undesirable to do this as CCK fields are complex. But here is the situation.

With most non-CCK node types and some CCK fields you can do the following safely in 4.7:

// Note this is 4.7 code as we can use drupal_execute() in Drupal 5 that will probably solve this.
$node = node_load(40);
$node->promote = 0; // Simple for sake of an easy example
node_validate($node);
$node = node_submit($node);
node_save($node);

But when you use this code on a node, say for example, with a nodereference select list with multiple nodes selected the values will get wiped out!

Why?

This happens because the nodereference data we get from node_load() is different than what node_save() wants.

Nodereference data from node_load().

    [field_related_blog_posts] => Array
        (
            [0] => Array
                (
                    [nid] => 7
                )

            [1] => Array
                (
                    [nid] => 20
                )

            [2] => Array
                (
                    [nid] => 28
                )

            [3] => Array
                (
                    [nid] => 93
                )

        )

Nodereference field data sent to submit handler from form

    [field_related_blog_posts] => Array
        (
            [nids] => Array
                (
                    [93] => 93
                    [7] => 7
                    [20] => 20
                )

        )

This came about when I wanted to programatically add 'blog' nodes to a nodereference field for an 'project' CCK node type. Just wanted to get this out there to see if this warrants a change in the way we're handling CCK data, but again I *think* this is where drupal_execute() comes into play in Drupal 5 so maybe I should just add this to the handbook somewhere. Ideas?

Here's the function I use:

/**
 * Helper function that attaches a blog post to a project.
 */
function _mymodule_blog_submit($blog_nid, $project_nid) {
  // Load project node.
  $node = node_load($project_nid);
  $field_related_blog_posts = array('nids' => array());

  // Change CCK nodereference data from load structure to submit structure.
  foreach($node->field_related_blog_posts as $delta => $item) {
    // If this blog is already attached to this project, cancel.
    if ($item['nid'] == $blog_nid) {
      return;
    }

    $field_related_blog_posts['nids'][$item['nid']] = $item['nid'];
  }

  // Attach blog post and save project node.
  $field_related_blog_posts['nids'][$blog_nid] = $blog_nid;
  $node->field_related_blog_posts = $field_related_blog_posts;
  node_validate($node);
  $node = node_submit($node);
  node_save($node);
}

Comments

KarenS’s picture

The nodereference module should transform its unusual handling of the node array back into the normal version when the 'process form values' operation runs, and that should be called on node_submit(), so after that your node should be correctly formatted. I don't have time to test this now, but it might work if you just change your code a bit to (not trying to use a returned $node value on submit):

$node = node_load(40);
$node->promote = 0; // Simple for sake of an easy example
node_validate($node);
node_submit($node);
node_save($node);

If that doesn't work, it may be that the nodereference module needs a fix, since it should work.

KarenS’s picture

I was assuming you were saying you couldn't save the node, but now I see you were maybe saying it was hard to see how to add another nid to the array since the format is odd? I agree with you that this makes it hard to use and I've always wondered if we could rework the module so it behaves in the same way other modules do, but never had time to investigate it. If nothing else, maybe there's a way to fix it to make it easier to add a node programmatically, since this is still going to be a problem in Drupal 5.

I agree we should either change the way the module works, provide a hook of some kind that can be used to programmatically add, change, or remove nids, or just document the odd behavior. This is also true for the userreference module, BTW.

RobRoy’s picture

Except we have a returned $node in node_submit so I think the code is valid as this is what node_form_submit uses. I think it's just some funk with how the data is handled for nodereference and it's probably worth a look. I'm still dealing with this issue a bit and need to see if I really have a workaround as node_validate() is messing with me now. :(

function node_submit($node) { 
// $node not passed by reference, but rather returned
...
}
RobRoy’s picture

Okay, I figured out that this code works in PHP 4, but not PHP 5. If I take out the $node = node_submit($node); then it works in PHP 5. This must have something to do with cloning. I've even applied the cloning fix at http://drupal.org/node/86842, but this is still baffling me.

$node = node_load(89);
_mymodule_transform_cck_select_list_field($node->field_related_blog_posts);
node_validate($node);
dpr($node->field_related_blog_posts); // Shows it fine
$node = node_submit($node); // I even tried to drupal_clone here and in node_submit() but it didn't help.
dpr($node->field_related_blog_posts); // It's empty!!!
node_save($node);
KarenS’s picture

So, is this differing behavior of node_submit between php 4 and php 5 unique to the nodereference field, unique to cck, or true for all kinds of nodes? We need to nail down which module needs attention.

dopry’s picture

Status: Active » Closed (fixed)

seems like this was fixed or not a cck issue.