Taxonomy controls on spatially separate places on node form
knugar - April 12, 2007 - 18:42
I use several taxonomies but would like to place one at the top of the node edit form and the rest at the end like this:
[taxonomy-select] (in my case a taxonomy used for image copyright)
[title]
[body]
...
...
[taxonomy-select] (all other categories)Using hook_form_alter() it is possible to rearrange forms in various ways. But, as far as I understand, all taxonomy stuff must go under $form['taxonomy'] (or $form['whatever']....['taxonomy'] for the submit to work correct and this means all taxonomy stuff is rendered together.
An alternative is to implement hook_nodeapi() and handle my copyright vocabulary separatly. But, it would be great if there was a way of using the existing Forms API to achive this, any suggestions welcome.

Try...
What I did in This post..
Should be able to pull out one of the terms to display at the top, and then the rest at the bottom by modifying that and having it in your node template instead of simply printing $terms
...works while viewing nodes but not for edit
Thanks kaniz for your suggestion. If all I wanted was to tweak the way terms are displayed on node views I would do something similar.
But, my concern is related to the form for editing nodes. When you edit/create a node, you select terms from the enabled vocabularies. When you have more than one vocabulary these are grouped together on the form. I would like to have the option to split and "move" the select-term(s) controls around on the form.
Welcome to hook_form_alter
What you want to do is use hook_form_alter to modify the node edit form after the taxonomy module alters it.
You'll have to do some nice hacking to do this, and make sure that you give your module a higher weight in the system table in your database so that it occurs after the taxonomy module.
I've tried using hook_form_alter()
Thansk for the tip but as I wrote in my initial post:
And yes, I've realized (for various reasons) that it is a good thing to give my module a high weight in the system table when using hook_form_alter().
The problem is not that I cannot find the vocabulary stuff in $form. I can also move vocabulary stuff around using someting like this:
$vid = variable_get('image_copyright_vid', -1);
if ($taxonomy[$vid]) {
$copyright = $taxonomy[$vid];
unset($taxonomy[$vid]);
$copyright['#weight'] = -4;
$form['copyright'][$vid] = $copyright;
};
When the form is displayed this works just fine, the problem is the copyright vocabulary is not saved or updated to the database. In taxonomy.module
function taxonomy_nodeapi($node, $op, $arg = 0) {switch ($op) {
case 'load':
$output['taxonomy'] = taxonomy_node_get_terms($node->nid);
return $output;
case 'insert':
taxonomy_node_save($node->nid, $node->taxonomy);
break;
case 'update':
taxonomy_node_save($node->nid, $node->taxonomy);
break;
case 'delete':
taxonomy_node_delete($node->nid);
break;
case 'validate':
taxonomy_node_validate($node);
break;
case 'rss item':
return taxonomy_rss_item($node);
case 'update index':
return taxonomy_node_update_index($node);
}
}
Here I see that during insert and update, $node->taxonomy is used to pass the taxonomy to taxonomy_node_save(). As far as I understand, the form API automatically put all submitted data from the taxonomy part of the form $form['yada']['yada_yada']['taxonomy'] = array(....) in $node->taxonomy
Hence, the problem is, there is no easy way to break up the form and have taxonomy selects on different places on the form, they all have to go under $form['taxonomy']. I might be wrong about this so feel free to point me in any direction you think might be helpful.
Use nodeapi and save it yourself
So add your own hook_nodeapi, and save your modified taxonomy information yourself. You can create a very similar function which only looks for the $node->copyright and saves that term to the database.
What I would do is create your hook_nodeapi, and use print_r($node) in the insert/update operations and see if the new copyright stuff you did is being attached to the $node object or not.
yes - I'm trying this right now
Hi again dwees and thanks for helping me out.
Using hook_nodeapi() and save it my self is probably the best/easiest way to accomplish this. In the beginning I was curios to find out if there was some "hidden" support for this in the forms API but I guess it is not. Using hook_nodeapi() will do just fine for me.
One thing that seems a little odd to me is the way $form['taxonomy'] is given special treatment. If you bare with me I will try to explain.
In taxonomy_form_alter():
$form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), $vocabulary->help);
That is, each vocabulary is given an index (equal to its vid) in the taxonomy array and print_r($form['taxonomy]) gives me this:
Array
(
[4] => Array
(
[#type] => select
[#title] => En kategori (key=4, weight=0)
[#default_value] => Array
(
[0] => 13
)
[#options] => Array
(
[0] =>
[1] => stdClass Object
(
[option] => Array
(
[12] => roligt
)
)
[2] => stdClass Object
(
[option] => Array
(
[13] => tråkigt
)
)
)
// snipp
[5] => Array
(
[#type] => select
[#title] => Image Copyright (key=5, weight=0)
[#default_value] => 14
[#options] => Array
(
[0] => stdClass Object
(
[option] => Array
(
[14] => Creative Commons
)
)
[1] => stdClass Object
(
[option] => Array
(
[15] => -cc-2.0-by
)
)
// snipp
In an attempt to separate the copyright vocabulary, by using hook_form_alter() I mimic the ordinary taxonomy form element and do this:
$vid = variable_get('image_copyright_vid', -1);if ($taxonomy[$vid]) {
$copyright = $taxonomy[$vid];
unset($taxonomy[$vid]);
$copyright['#weight'] = -4;
$copyright['#tree'] = TRUE;
$form["copyright"] = array($vid => $copyright);
};
A print_r($form['copyright']) looks like this:
Array
(
[5] => Array
(
[#type] => select
[#title] => Image Copyright (key=5, weight=0)
[#default_value] => 14
[#options] => Array
(
[0] => stdClass Object
(
[option] => Array
(
[14] => Creative Commons
)
)
//snipp
By the way, something that I started using instead of plain print_r or (dprint_r if you've got the devel module enabled) is to print debugg messages to the Drupal message stream by using a small utility function like this:
function _dmsg($title, $msg) {ob_start();
print "<h3>$title</h3>";
print_r($msg);
drupal_set_message('<pre>' . ob_get_contents() . '</pre>');
ob_clean();
}
Using hook_nodeapi() I've now investigate the $node object:
During 'validate' print_r($node):
//snipp
[taxonomy] => Array
(
[4] => Array
(
[12] => 12
)
)
[5] => 19
)
NOTE: $node->taxonomy is an array but my copyright taxonomy is attached to the node like $node->5 (5 is the vid of my copyright vocabulary).
During 'update' there is no longer a $node->5 field!! (this is not even legal PHP syntax).
A work around is to change in hook_form_alter() to this:
$form["copyright-$vid"] = $copyright;Now, during 'update' in hook_nodeapi() $node->copyright-5 will give me the selected copyright term and I can choose to extract the vid from the string 'copyright-5' if I need to..
STRANGE?: somehow taxonomy gets special treatment. Form elements are usually attached to the node object by the name of outermost array index. Allthough, taxonomy module declares form elements like
$form['taxonomy'][$vocabulary->vid] = array(....), here the outer most index is a vid number but all vocabularies and terms are "glued" together and attached as $node->taxonomy (not the outermost index).
My first attempt also used a number (vid) as the outermost index and a preview attached it as $node->vid.
I've tried to find where this "special" treatment happens but so far with no success...
simple node save
I found a very neat way of doing this. In hook_nodeapi()
function symbios_misc_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {switch ($op) {
case 'submit':
$vid = variable_get('image_copyright_vid', -1);
$node->taxonomy[$vid] = $node->copyright;
break;
This put my image copyright vocabulary back into the ordinary taxonomy and it will be saved to the database just like any other taxonomy.
Very clever
Yeah that is clever, I'll have to remember that trick.
Dave
Brilliant, that works
Brilliant, that works exactly as needed - thanks!