[drupal-devel] Question on taxonomy_form() function
Good morning! I've read the API docs on this, and even looked at the function's source code. I understand how it works, but apparently not how to use it properly. :-) My new image_import module (a feature that I'm developing in cooperation with walkah's excellent work, and yes, I've emailed with him to discuss it first) has code that looks like this: $html = ''; $vocabs =& taxonomy_get_vocabularies(IMAGE_IMPORT_IMAGE_NODETYPE); // The above function orders by weight, so we don't have to sort $group = ''; foreach ($vocabs as $vocab) { $group .= taxonomy_form($vocab->vid, $_POST['edit']['taxonomy'],NULL); } $html .= form_group(t('Categories'),$group,t(' ... long help text here ...')); The constant IMAGE_IMPORT_IMAGE_NODETYPE is just the string 'image'. After submitting a form, the $_POST['edit']['taxonomy'] subarray looks like this: array(3) { [0]=> string(1) "1" [1]=> array(1) { [0]=> string(1) "3" } [2]=> array(1) { [0]=> string(1) "7" } } The three selected term IDs are "1", "3", and "7". My problem is that the second vocabulary (vid==2, tid=={3,7}) allows multiple selection, whereas the first vocabulary (vid==1, tid==1) does not. The way the underlying form_select() called by taxonomy_form() seems to work is that it uses a subarray for the field only if multiple selection is allowed. This puts the resulting tids at different levels in the array tree, and causes my $_POST['edit']['taxonomy'] to fail as a way to pass the existing value(s) to taxonomy_form() for a given vid. My first thought was to try using a $name parameter for taxonomy_form(), something akin to taxonomy_1 for vid 1, taxonomy_2 for vid 2, etc. That seems awfully clumsy. I also tried passing $name as an array-like string, e.g., $name=="[taxonomy][1]" for vid 1, etc. That failed syntactically. There isn't a good way to match up those sub-subarrays with the vid to which they point, so simple conditional logic inside my foreach{} loop is not going to be feasible if the site administrator has associated more than one multi-term- allowed vocabulary with images. There's probably a "right way" to do this, but I'll be damned if I see it. The only method I can figure out looks like a hideous kludge. Can someone suggest the correct approach to this, or point me to a module that does it for some example code? The only place I could think of that does this is in node.module, but the problem there is that is has a $node object and passes that to an entirely different function instead of taxonomy_form. My module needs to pick vocabulary terms in advance of the existence of a node object, then apply them to multiple nodes as those nodes are created, so I don't have a $node to pass as node.module and other node-type modules do. My module doesn't define any new node types, but just adds a new way to create nodes of an existing type. I'm probably going to feel stupid when I see the answer to this, but I'll risk it. Any suggestions? Is it worth considering a new core function like taxonomy_forms() that would accept a node type as its parameter and return the HTML for all of the vocabs associated with that node, in such a way that the resulting $_POST is easily parsed by vid to support previews? Thanks! Scott -- -----------------------+------------------------------------------------------ Scott Courtney | "I don't mind Microsoft making money. I mind them scott@4th.com | having a bad operating system." -- Linus Torvalds http://4th.com/ | ("The Rebel Code," NY Times, 21 February 1999) | PGP Public Key at http://4th.com/keys/scott.pubkey
My problem is that the second vocabulary (vid==2, tid=={3,7}) allows multiple selection, whereas the first vocabulary (vid==1, tid==1) does not. The way the underlying form_select() called by taxonomy_form() seems to work is that it uses a subarray for the field only if multiple selection is allowed. This
There isn't a good way to match up those sub-subarrays with the vid to which
In my fiddling with folksonomy, I know what you're talking about. The taxonomy form doesn't, in any instance, give a damn about the vid. The ONLY effect of choosing "Multiple select" is the UI element: there is no underlying code that actually checks to make sure only $n terms were passed for a particular $vid. As a bug, "Multiple select is determined by form UI, not logic." With that said, the $vid isn't required for taxonomy_save to work. To the underlying code, all it cares about is the $nid and the $tid, which get saved into term_data. The $vid isn't required for this - the $tids are unique so you can always find the $vid later on in life. The subarray you're seeing is just a side-effect of the "Multiple select" option; the "no good way to match up with the $vid" is entirely "by design". If you're really insanely interested in the $vid, you could use the _nodeapi on 'validate' to enforce your own checks: look over each $tid, find the matching $vid, and check against vocabulary.multiple, and form_set_error if you received more than you expected.
they point, so simple conditional logic inside my foreach{} loop is not going to be feasible if the site administrator has associated more than one multi-term-
I'm not sure why you actually /need/ the $vid. -- Morbus Iff ( you are nothing without your robot car, NOTHING! ) Culture: http://www.disobey.com/ and http://www.gamegrene.com/ Spidering Hacks: http://amazon.com/exec/obidos/ASIN/0596005776/disobeycom icq: 2927491 / aim: akaMorbus / yahoo: morbus_iff / jabber.org: morbus
On Wednesday 30 March 2005 09:32, Morbus Iff wrote:
I'm not sure why you actually /need/ the $vid.
The problem is that when I call taxonomy_form() for each $vid, I need to pass this function the array containing all the current selections for that particular vid's terms. It's not that I can't *get* the tids I need for taxonomy_save(), but rather that I have no good way to pass them back to taxonomy_form() when the user clicks "preview". Your comments do raise another possibility, though, in that I could simply walk through the complex array and make a simple one containing *all* the tids for *all* the vocabs represented on my page. Presumably taxonomy_form does an array search as it emits each <OPTION> tag, to see if that particular tid should get SELECTED or not. If there are extra tids listed in the array, one would presume that they would simply never match on the searches, and would therefore do no harm. It also occurs to me that for my particular module, maybe I just don't need the preview feature at all. I built a dummy "preview" submit button for testing, but maybe in real life all I need is "Make It So, Mr. Crusher!" to actually do the import. That would make those tid numbers a one-way trip, never going back to the UI at all. Scott -- -----------------------+------------------------------------------------------ Scott Courtney | "I don't mind Microsoft making money. I mind them scott@4th.com | having a bad operating system." -- Linus Torvalds http://4th.com/ | ("The Rebel Code," NY Times, 21 February 1999) | PGP Public Key at http://4th.com/keys/scott.pubkey
Your comments do raise another possibility, though, in that I could simply walk through the complex array and make a simple one containing *all* the tids for *all* the vocabs represented on my page. Presumably taxonomy_form does an array search as it emits each <OPTION> tag, to see if that particular tid should get SELECTED or not. If there are extra tids listed in the array, one would presume that they would simply never match on the searches, and would
I believe that is correct, yes. I remember noticing this in one of my fiddly sessions with folksonomy (the folksonomy patch actually does add a $vid to the incoming $node->taxonomy, but only for folksonomy-related vocabularies, meaning it'd be of little use to you). -- Morbus Iff ( you are nothing without your robot car, NOTHING! ) Culture: http://www.disobey.com/ and http://www.gamegrene.com/ Spidering Hacks: http://amazon.com/exec/obidos/ASIN/0596005776/disobeycom icq: 2927491 / aim: akaMorbus / yahoo: morbus_iff / jabber.org: morbus
On Wednesday 30 March 2005 09:49, Morbus Iff wrote:
Your comments do raise another possibility, though, in that I could simply walk through the complex array and make a simple one containing *all* the tids for *all* the vocabs represented on my page. Presumably taxonomy_form does an array search as it emits each <OPTION> tag, to see if that particular tid should get SELECTED or not. If there are extra tids listed in the array, one would presume that they would simply never match on the searches, and would
I believe that is correct, yes. I remember noticing this in one of my fiddly sessions with folksonomy [...]
It does work as Morbus and I had presumed. You can safely pass a simple array with *all* the tids (not vids) to taxonomy_form() and it behaves as expected. I wrote a function that extracts the tids from a complex taxonomy array, with the assumption that the any non-array element is a tid. Here is the code: /** * Scans a multi-level array with term ID numbers buried inside arrays * inside arrays inside arrays...etc., and returns a simple array with just * the tid numbers linearized plus, optionally, any free-tags entered by the * user if a free-tag string is present in the input array. * * WARNING: This function uses recursion; modify with care or an infinite * recursion loop is possible. * * For the outermost (non-recursed) invocation only, the parameter $keep_tags * (default TRUE) tells the function to retain, unmodified, any element of * the $tid_array whose subscript is 'tags'. This causes the subarray placed * by the free-tag patch to taxonomy.module to work correctly across previews. * Note that $tid_array['tags'] is a single-element sub-array containing one * string element with the free-form tags. * * If your installation does not have the free-tag support, the $keep_tags * parameter has no effect since $tid_array['tags'] will not be present. * It is necessary to disable $keep_tags only if you want to ensure that the * returned array from this function has only existing tids in it. */ function _image_import_extract_tids($tid_array, $keep_tags=TRUE) { $tids = array(); if (is_array($tid_array)) { foreach ($tid_array as $key=>$element) { if (is_int($key)) { if (is_array($element)) { // $keep_tags is NEVER useful when recursing $tids = array_merge($tids, _image_import_extract_tids($element, FALSE)); } else { $tids[] = $element; } } } } if ($keep_tags && isset($tid_array['tags'])) { $tids['tags'] = $tid_array['tags']; } return $tids; } This might be a useful function (if renamed) to include in taxonomy.module as an externally-callable API. I suspect this wheel will otherwise need to be invented by other modules that need to use term selection forms without an extant node object. My immediate issue, though, solves the problem by calling a different function from Morbus' patched taxonomy.module. I realized that I could jig up a dummy node pretty easily, and then call So my form generation code now looks like this: define('IMAGE_IMPORT_IMAGE_NODETYPE','image'); function _image_import_form() { $html = ''; $group = ''; $dummy_node =& new image_import_node(); $dummy_node->type = IMAGE_IMPORT_IMAGE_NODETYPE; $dummy_node->taxonomy =& _image_import_extract_tids($_POST['edit']['taxonomy']); $taxonomy_fields_array = taxonomy_node_form(IMAGE_IMPORT_IMAGE_NODETYPE, $dummy_node); foreach($taxonomy_fields_array as $field) { $group .= $field; } $html .= form_group(t('Categories'),$group,t(' .... explanatory text ....')); // ...snip... $html .= form_submit(t('Import images')); return form($html,'POST'); } class image_import_node { // dummy class } image_import_node is a trivial (as in, empty!) class that I added to my module just so I'd have an empty object. Interestingly, there seems to be no actual definition of an object class called "node" or "Node" in Drupal. We are relying on mysql_fetch_object() and the corresponding functions for other databases to create an object on node load, and then we treat that object as a node. The above works like a charm, and even has Morbus' free-tag text box so that the user can enter their tags. It was tricky to get the free-tags to persist across the call to _image_import_extract_tids(), but I've solved that by making the array element "tags" a special case. If the free-tag patch isn't installed, no harm is done because that special case simply never occurs. I haven't tried the following yet, but I believe that calling taxonomy_node_save() after I've imported each new node will properly update {term_node} and {term_data} tables. From looking at the code, it seems that this function does exactly what I need it to do. Anyway, I wanted to thank those who posted suggestions, and to let others know how I solved the problem. Since Morbus' patch is new, it seemed relevant to let other coders know that this is a soluble issue and to share the solution. If all goes well, the new "walkah" image.module will have a mass import feature very soon. Scott -- -----------------------+------------------------------------------------------ Scott Courtney | "I don't mind Microsoft making money. I mind them scott@4th.com | having a bad operating system." -- Linus Torvalds http://4th.com/ | ("The Rebel Code," NY Times, 21 February 1999) | PGP Public Key at http://4th.com/keys/scott.pubkey
* the $tid_array whose subscript is 'tags'. This causes the subarray placed * by the free-tag patch to taxonomy.module to work correctly across previews. * Note that $tid_array['tags'] is a single-element sub-array containing one * string element with the free-form tags.
Not necessarily one. 'tags' is an array of vid keys, so that you could have multiple "Free tagging" vocabularies for one node. If you had "Free tagging 1" (vid 3) and "Free tagging 2" (vid 17) applied to "node", then there would be two input boxes in the UI, and a submitted 'tags' would contain the following: [tags] [3] => 'tags, applied, to, free tagging 1', [tags] [17] => 'tags, applied, to, free tagging 2' With that said, since your code doesn't touch 'tags' at all, things will work as (I've) intended; the only thing to "fix" is your documentation.
This might be a useful function (if renamed) to include in taxonomy.module
Looks good. I'm not entirely sure I like the dependency on _image though, but I'm sure we could make that a (non-)private function in taxonomy too. Good stuff! -- Morbus Iff ( you are nothing without your robot car, NOTHING! ) Culture: http://www.disobey.com/ and http://www.gamegrene.com/ Spidering Hacks: http://amazon.com/exec/obidos/ASIN/0596005776/disobeycom icq: 2927491 / aim: akaMorbus / yahoo: morbus_iff / jabber.org: morbus
On Thursday 31 March 2005 11:33, Morbus Iff wrote:
* Note that $tid_array['tags'] is a single-element sub-array containing one * string element with the free-form tags.
Not necessarily one. 'tags' is an array of vid keys, so that you could have multiple "Free tagging" vocabularies for one node. If you had "Free tagging 1" (vid 3) and "Free tagging 2" (vid 17) applied to "node", then there would be two input boxes in the UI, and a submitted 'tags' would contain the following:
[tags] [3] => 'tags, applied, to, free tagging 1', [tags] [17] => 'tags, applied, to, free tagging 2'
Yeah, I figured that out just after I posted, as I went back into working on the code. Duh. I had been wondering where that "3" in my trace var_dump() calls was coming from, never occurred to me it was a vid number. Thanks for the concise explanation.
With that said, since your code doesn't touch 'tags' at all, things will work as (I've) intended; the only thing to "fix" is your documentation.
[...] * For the outermost (non-recursed) invocation only, the parameter $keep_tags * (default TRUE) tells the function to retain, unmodified, any element of * the $tid_array whose subscript is 'tags'. This causes the subarray placed * by the free-tag patch to taxonomy.module to work correctly across previews. * Note that $tid_array['tags'] is a sub-array containing one or more string * element(s), indexed by vocabulary ID (vid), with the free-form tags. [...] Better? :-)
This might be a useful function (if renamed) to include in taxonomy.module
Looks good. I'm not entirely sure I like the dependency on _image though, but I'm sure we could make that a (non-)private function in taxonomy too.
Which is why I suggested renaming it. If you choose to add it into your patch, you could call it something like taxonomy_extract_tids() and make it a non- private API call. If you do that, I'll just drop the code from my module.
Good stuff!
Laptop computer: $2200. SuSE Linux DVD: $89. Drupal: $0. Having Morbus Iff call your code "good stuff": Priceless. Thanks! Scott -- -----------------------+------------------------------------------------------ Scott Courtney | "I don't mind Microsoft making money. I mind them scott@4th.com | having a bad operating system." -- Linus Torvalds http://4th.com/ | ("The Rebel Code," NY Times, 21 February 1999) | PGP Public Key at http://4th.com/keys/scott.pubkey
Which is why I suggested renaming it. If you choose to add it into your patch, you could call it something like taxonomy_extract_tids() and make it a non- private API call. If you do that, I'll just drop the code from my module.
For now, I'm gonna keep it out, under the desire to keep a) the patch as small as possible, b) you moving forward on your own code without a dependency that may never occur, c) I've got other crap to do <g>. -- Morbus Iff ( you are nothing without your robot car, NOTHING! ) Culture: http://www.disobey.com/ and http://www.gamegrene.com/ Spidering Hacks: http://amazon.com/exec/obidos/ASIN/0596005776/disobeycom icq: 2927491 / aim: akaMorbus / yahoo: morbus_iff / jabber.org: morbus
On Thursday 31 March 2005 13:59, Morbus Iff wrote:
For now, I'm gonna keep it out, under the desire to keep a) the patch as small as possible, b) you moving forward on your own code without a dependency that may never occur, c) I've got other crap to do <g>.
Fair enough. If you change your mind later, I'm still willing to go along with moving the code out of my module and into yours. Scott -- -----------------------+------------------------------------------------------ Scott Courtney | "I don't mind Microsoft making money. I mind them scott@4th.com | having a bad operating system." -- Linus Torvalds http://4th.com/ | ("The Rebel Code," NY Times, 21 February 1999) | PGP Public Key at http://4th.com/keys/scott.pubkey
participants (2)
-
Morbus Iff -
Scott Courtney