hello,

what is the proper code to programmatically save a node with a content_taxonomy cck field with autocomplete widget? i looked at the $_POST variable when submitting a node through a browser, and i see:

$_POST['field_instrument']['value'] = 'Piano, Violin'

so I tried to set up my $form_state that way and then run drupal_execute, but none of the terms get saved. in the example above the terms are new terms, so there is no corresponding tid.

Comments

azinck’s picture

You can save existing terms in the following format:
(if term Piano has TID == 10 and term Violin has TID == 11)

$_POST['field_instrument'][0]['value'] = 10;
$_POST['field_instrument'][0]['value'] = 11;

but like the OP I can't figure out how to save new terms or terms where you don't already know the TID.

xjm’s picture

See taxonomy_get_term_by_name() for getting a term's ID when you have its name. However, the saving of the terms to {term_node} happens in taxonomy.module's hook_nodeapi(), I believe. (taxonomy_nodeapi()). Take a look at content_taxonomy_field() to see what the module does; the presave op takes care of saving new terms.

azinck’s picture

Hi xjm,

The trouble is that I'm trying to use this with the Services module (xml_rpc). I don't want to try to keep term ids on my remote app or try to synchronize those or anything fancy there. With the core taxonomy module I can just send a comma-delimited list of term names and it will take care of creating new terms or associating with existing terms in the vocabulary. With content taxonomy I'm forced to correlate the terms to TIDs on the remote app side which is error-prone and inconvenient.

xjm’s picture

Ah, I see. I wonder if this is because content taxonomy adds its terms to the node object in the presave phase rather than on update? Hmmm... I peeked under the hood of the node services module, and found this:

function node_service_save($edit) {
78 	
79 	// validate node
80 	node_validate($edit);
81 	if ($errors = form_get_errors()) {
82 	return services_error(implode("\n", $errors));
83 	}
84 	
85 	$node = node_submit($edit);
86 	node_save($node);
87 	watchdog('content', t('@type: updated %title.', array('@type' => t($node->type), '%title' => $node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
88 	} 

node_save($node) should theoretically be invoking the presave case in Content Taxonomy, though. Either it isn't for some reason, or the terms are not passed through properly.

Is this field configured with the Save values additionally to the core taxonomy system (into the 'term_node' table) checked?

xjm’s picture

See also: #354975: Adding values with drupal_execute(). Are any of the suggestions there relevant in your situation?

azinck’s picture

Hi xjm,

Thanks for spending some time looking into this. I think you must be looking at a different version of Services. Here's the function in the version I'm using: (from services 2)

function node_service_save($edit) {
113 	// Load the required includes for drupal_execute
114 	module_load_include('inc', 'node', 'node.pages');
115 	$nid = NULL;
116 	$edit = (array) $edit;
117 	if ($edit['nid']) {
118 	$node = node_load($edit['nid']);
119 	if ($node->nid) {
120 	// Setup form_state.
121 	$form_state = array();
122 	$form_state['values'] = (array) $edit;
123 	$form_state['values']['op'] = t('Save');
124 	
125 	// Later on in the chain node_form seems to expect a copy of
126 	// the old node object.
127 	$form_state['node'] = (array) $node;
128 	
129 	$ret = drupal_execute($node->type .'_node_form', $form_state, (object)$node);
130 	
131 	// If the node is immediately reloaded after update, it will
132 	// load the OLD cached version.
133 	node_load(0, NULL, TRUE);
134 	
135 	// Set $nid, so it can be returned
136 	$nid = $node->nid;
137 	}
138 	else {
139 	return services_error(t('Node not found'), 401);
140 	}
141 	}
142 	else {
143 	// If the submitted node has no author (for instance if it was
144 	// created programmatically by hand) then set the currently logged
145 	// in user as the author.
146 	if (!$edit['uid']) {
147 	global $user;
148 	$edit['uid'] = $user->uid;
149 	}
150 	
151 	// Setup form_state
152 	$form_state = array();
153 	$form_state['values'] = $edit;
154 	$form_state['values']['op'] = t('Save');
155 	
156 	$ret = drupal_execute($edit['type'] .'_node_form', $form_state, (object)$edit);
157 	
158 	// Fetch $nid out of $form_state
159 	$nid = $form_state['nid'];
160 	}
161 	if ($errors = form_get_errors()) {
162 	return services_error(implode("\n", $errors), 401);
163 	}
164 	return $nid;
165 	} 

I'm not so familiar with the Forms API but is it possible that drupal_execute doesn't invoke the presave case in the same way as node_save? I haven't tested the cron-run theory on the issue you linked to because I ended up implementing a different (slightly awkward) solution involving a custom module to accomplish what I needed and no longer have a convenient test-bed set up for this case.

xjm’s picture

Well, a quick way to check whether the presave case is getting fired would be to log to watchdog during CT's presave operation. You'd modify the module's code in content_taxonomy_field() around line 154 of content_taxonomy.module:

function content_taxonomy_field($op, &$node, $field, &$items, $teaser, $page) {
  switch ($op) {
    case 'presave':
      // MODIFICATION: Log a message to watchdog for troubleshooting.
      watchdog(WATCHDOG_INFO, "Executing presave condition for Content Taxonomy for node {$node->nid}.");
      // END MODIFICATION

      if ($field['save_term_node']) {
        static $_content_taxonomy_array_cleared;
	if (!is_array($_content_taxonomy_array_cleared) || !$_content_taxonomy_array_cleared[$node->nid][$field['vid']]) {
          _content_taxonomy_taxonomy_unset($node->taxonomy, array($field['vid']));
          $_content_taxonomy_array_cleared[$node->nid][$field['vid']] = TRUE;
        }

	foreach ($items as $key => $entry) {
          if ($entry['value']) {
            if (is_object($node->taxonomy[$entry['value']])
              || (is_array($node->taxonomy) && in_array($entry['value'], $node->taxonomy))
              || (isset($entry['_remove']) && $entry['_remove'] == 1)) {
              continue;
            }
            elseif (is_array($node->taxonomy[$field['vid']])) {
              if (!in_array($entry['value'], $node->taxonomy[$field['vid']])) {
                $node->taxonomy[$field['vid']][] = $entry['value'];
              }
            }
            // when saving an existing node without presenting a form to the user,             
            // the terms are objects keyed by tid. there's no need to re-set these             
            // terms, and to do so causes php warnings because the database rejects            
            // the row insert because of primary key constraints.                              
            else {
              $node->taxonomy[$field['vid']] = array($entry['value']);
            }
          }
        }
        // the $node->taxonomy array should never be empty, because in this case the           
        // taxonomy nodeapi doesn't call taxonomy_node_save which handles removing             
        // and inserting of terms                                                              
        if (empty($node->taxonomy)) {
          $node->taxonomy[$field['vid']] = NULL;
        }
      }
      break;
  }
}

Then there should be a record at admin/reports/dblog any time that function happens, even if it's triggered remotely.

You could also add a similar message to any branch of the function to test whether that code happens and what state the variables are in there.

Magnus’s picture

Status: Active » Closed (fixed)

Please reopen issue if problem still exists.