Play nice when binding to node types

At one time or another you're going to come up with a great piece of minor functionality that binds itself to a node in some cunning way. Here we'll explore the one most common methods to achieve this; throwing your crap straight into the node build process. You could attach it via a node link also but this article is looking at the badness made in a recent CVS application. So we'll stick with a that to keep things short.

So what has this to do with interoperability and node types? Well, not a lot actually, it's more about playing nice with node types and how you attach your cruft to them.

Take this wonderful snippet lifted from a recent application:

  1. function foo_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  2.  
  3.   // ... do something funky and put it in $my_cruft ...
  4.  
  5.   $node->content['foo'] = array<(
  6.     '#value' => $my_cruft,
  7.     '#weight' => CONST_NO_ONE_CAN_CHANGE,
  8.   );
  9. }

OK, so come on. You can spot straight away that this "friendly" module is basically a kin to allowing someone into your beautiful home and taking a crap in every room.

Modules really should provide methods for site maintainers and webmasters to say "I'd like you to only take a crap in the rest room please". Yes, we need some sort of admin setting to declare what node type to bind to.

There's probably a dozen ways to do this. If you have a complex module with lots of settings then no doubt you'll have an admin settings page at admin/settings/foo. However, we just want a single binary piece of information, "use with this node type or not?". In this case it's better (imho) to bind yourself to the node type's setting form. This kind of addition commonly goes in the workflow fieldset. So let's look at how to add that in.

  1. function foo_form_alter(&$form, $form_state, $form_id) {
  2.   if ($form_id == 'node_type_form') {
  3.     $form['workflow']['foo'] = array<(
  4.       '#type' => 'checkbox',
  5.       '#title' => t('Enable foo'),
  6.       '#default_value' => variable_get('foo_'. $form['#node_type']->type, 0),
  7.     );
  8.   }
  9. }

As you can see, not a lot to it. Core's node_type_form_submit()< handles the setting of the variable foo_xyx for you. Keep things simple is always a good philosophy. On a side note, if you do have a large settings page it doesn't do any harm to reproduce the following also for the small per node type binary style flags.

Now, let's make use of these new admin variables. Returning to the previous snippet we can now rewrite it to use the new per node type variable.

  1. function foo_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  2.  
  3.   if (variable_get('foo_'. $node->type, 0)) {
  4.  
  5.     // ... do something funky and put it in $my_cruft ...
  6.  
  7.     $node->content['foo'] = array<(
  8.       '#value' => $my_cruft,
  9.       '#weight' => CONST_NO_ONE_CAN_CHANGE,
  10.     );
  11.   }
  12. }

Your module can now be instructed to use the rest room like a good puppy. And it doesn't bloat your modules code by having a special admin form with a ton of checkboxes for each node type called with a hook_menu() callback to drupal_get_form() that has a _validate and _submit handlers, etc etc. You get the idea by now.

Notice we didn't look at CONST_NO_ONE_CAN_CHANGE, that's an exercise for the reader (hint, this also can be added in the hook_form_alter()< section).