Form Altering Node Add/Edit Forms

Ryan Kois

One advantage of not coding directly in binary is the ability to express computational intention in a human-readable form. Just as there are words and gestures that communicate intentionality to others in conversation, there are many expressions that communicate intention in code, though not all expressions are as elegant and functional as they could be. Such is the case for altering certain forms in Drupal.

On most form alters in Drupal, you target a specific form based on the form ID. Node forms are weird because the content type is included in the form ID. If you have a content type named example, the form ID would be example_node_form.

In order to only alter a specific content type's node form, you can use hook_form_FORM_ID_alter(). But what if you want to affect all node forms, perhaps in the case where you want to affect a few content types' node add forms? For that, you can use hook_form_BASE_FORM_ID_alter().

Why use these alternative methods of form altering? Well, there are a few reasons:

  1. It reduces the amount of code processed when rendering a given form.
  2. It reduces the cyclomatic complexity of a single hook_form_alter() implementation.
  3. It allows us to target certain forms in a cleaner matter.

To elaborate on the first two points, consider the following code. Lets imagine we have an example.module where we're altering forms. We might have some code that looks like this:

<?php
 
function example_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'search_form') {
    // do some stuff
  }
 
  if ($form_id == 'user_register') {
    // do some other stuff
  }
 
  // Using regex to match form_id. Nasty.
  if (preg_match('/node_form$/')) {
    // alter some node forms.
  }
 
  // Even nastier way to target node forms. Yikes.
  if (strpos(strrev($form_id), 'mrof_edon') == 0 ) {
    // alter some more node forms.
  }
}

In general terms, this function is doing too much. Each form alter should alter a form or group of forms. This function is quantitatively complex as well, having a Cyclomatic Complexity of 5 (because there are 5 potential paths through this code), and an NPath Complexity of 16 (as those 5 paths could be combined in 16 different ways). This makes writing tests more difficult, and it also leaves the code in a less than ideal state for ongoing maintenance. Let's refactor this function using the Extract Method refactoring pattern!

<?php
 
function example_form_search_form_alter(&$form, &$form_state, $form_id) {
  // do stuff
}
 
function example_form_user_register_alter(&$form, &$form_state, $form_id) {
  // do stuff
}
 
function example_form_node_form_alter(&$form, &$form_state, $form_id) {
  // do stuff
}

Giving every distinct alter its own function name is much more elegant and maintainable, and really easy to do with Drupal node forms.

When using hook_form_BASE_FORM_ID_alter() to alter node forms, node information can be gathered from $form['#node'], so if you'd like to affect a few different content type's node forms, you can also do a conditional on $form['#node']->type.

This is a simple example of a much broader process of analyzing and reducing code complexity, which you can apply throughout your code in Drupal, and beyond.