Node Form Dominos via Node Reference and Prepopulate
I like Prepopulate. I like to have that pseudo-RESTful way of preloading a form to minimize the amount of work a user has to do to get to the point of submitting a form. But I also like clean URLs. This post reviews in detail a technique to use a single prepopulated nodereference field to prepopulate a bunch of other fields based on that reference. Since Prepopulate’s recent 2.0 release, it because a whole lot more difficult to use the Form API to work magic on what it provides.
I use this in conjunction with nodereference to tailor node forms for their relationship with the referenced node.
Step 1: Setting up your links
In order for anything to happen, first you need to shape your links properly. The way to use Prepopulate with Node Reference fields (any many others) has subtly shifted. In the case of nodereference, you now what your querystring to look like this:
edit[field_my_nodereference_field][∆][nid][nid]=[nid: ###]
Where ∆ is the delta of your field (probably 0), and ### is the Node NID.
Step 2: Set up a module
Create a new module, or use any old custom module you have laying around. Be sure it’s weight is higher than Prepopulate’s lofty “10”, because your module needs to react to what Prepopulate does. Read up on setting your module’s weight if need be.
You could avoid worrying about module weight if you wanted to wrangle the URL on your own, but at that point why are you using Prepopulate?
Step 3: hook_form_alter() and #after_build
This post won’t review how to use hook_form_alter(), but if you do not know about this amazing function, I am shocked. Go read up on it. Master it. And return here.
hook_form_alter() is great to change a form before it’s built. But Prepopulate now applies it’s changes after the form is built. That means you need to use #after_build to specify another function to come along after Prepopulate, grab the values it has applied, and make your changes.
function custom_form_alter(&$form, &$form_state, $form_id) {
// You might want to put a check somewhere that this should only apply to
// new users or new nodes. For example, if ($form['#node']->nid == NULL)
$form['#after_build'] = 'custom_prepopulate_after_build';
}
Step 4: Grab the referenced node.
In order to inherit some values from the referenced node, you need to load it.
function custom_prepopulate_after_build($form, &$form_state) {
$node = node_load(custom_prepopulate_nodereference_nid($form['field_my_nodereference_field']));
if (empty($node)) {
// Bad reference from prepopulate. Nothing more to be done.
return;
}
// Do stuff to your form here.
// DO NOT FORGET THIS!
return $form;
}
/**
* Get the nid from the string [nid: #]
*
* @param $value
* String containing the nid.
*
* @return
* Integer value of a prospective nid.
* @see nodereference_autocomplete_validate
*/
function custom_prepopulate_nodereference_nid($form_field) {
preg_match('/^(?:\s\*|(.\*) )?\[\s\*nid\s\*:\s\*(\d+)\s\*\]$/', $value, $matches);
if (!empty($matches)) {
// Explicit [nid:n].
list(, $title, $nid) = $matches;
return $nid;
}
return NULL;
}
Step 5: Time for dominos
Now that we have a code skeleton with all the data we need, we can start in with some magic.
Put things like this in the “do stuff” section commented in the code block above.
Title Inheritance
if (empty($form['title']['#default_value'])) {
$form['title']['#value'] = 'Discussion of "' . $node->title . '"';
}
Taxonomy Inheritance
foreach ($form['taxonomy']['tags'] as $vid => $taxonomy) {
if (!is_numeric($vid)) {
continue;
}
if (empty($form['taxonomy']['tags'][$vid]['#default_value'])) {
$form['taxonomy']['tags'][$vid]['#value'] = taxonomy_implode_tags($node->taxonomy, $vid);
}
}
Was that complex?
Seems a bit involved, so I’m working out the best way to produce a helper module.
If what you want is to inherit values into your output, check out the Field Inherit project.