2

I'm building out a custom workflow. Newly registered users can create a node that has several fields - some of the fields are required, others are pre-filled, and others are disabled. The benefits for having 1 content type to handle this greatly outweigh the negatives.

Fields that I set the #default_value, #access, or any other FAPI attribute on are working properly, but fields where I set #required = TRUE are not validating and are not stopping the node from being created!

Here's my code:

function my_custom_callback($arg) {
  module_load_include('inc', 'node', 'node.pages');
  $page['node_form'] = node_add('content_type_name');

  if ($arg == 'foo') {
    $page['node_form']['field_favorite_color'][LANGUAGE_NONE][0]['value']['#required'] = TRUE;
  } else if ($arg == 'bar') {
    $page['node_form']['field_favorite_food'][LANGUAGE_NONE][0]['value']['#required'] = TRUE;
  }
}

If I visit this page with $arg = 'foo', I can submit this form without entering anything in field_favorite_color, even though this field shows the red asterisk and has the class 'required' and is showing as #required = TRUE in the form's render array when I do a dpm() within the callback function.

Why won't Drupal require that this required fields be filled out? Do I need to rebuild the form somehow after I've altered the fields? If I set the same code in a hook_form_alter() then it works as expected, but in that case I don't have the control that I get through my custom callback to lead the user through a multi-step workflow.

Edit: This is Drupal 7. Tried adding that tag but am seeing 'drupal-7' and 'drupal7' tags are not allowed.

  • Reading this answer drupal.stackexchange.com/a/17027/3586 makes me think I'm just affecting the theming layer, however my default values are still working even as the required attributes are not. – Charlie Schliesser Jul 9 '12 at 22:07
  • I can't tell much without more code. And I don't understand why you are creating a custom callback in the first place. Why not just form_alter the node form? You can play with session variables, DB variables or just URL args to get a multi-step workflow if need be. – user7667 Jul 12 '12 at 5:46
3
+200

In short, to answer your question "How can I specify required fields when programmatically loading a node creation form?" ... use hook_form_alter() and keep your sanity! :)

Here is what I ended up doing after toying with a bunch of proposed solutions I ran across wile learning hook_form_alter() and Drupal's Form API.

I'm sure this solution could be massively improved, but this worked for me and it seems to be fairly simple to manage/maintain.

Managing a Single Content Type With a Complex Workflow Using Node Edit Forms

First after hours of debugging, I decided not to use a custom callback. Instead I edited the built in node add/edit form using nothing more than hook_form_alter(). (I like hook_form_FORM_ID_alter() instead of hook_form_alter() as it helps me keep my code organized. A personal preference for sure.)

Note: Drupal 7 use the Administration theme by default for creating new nodes/content. You can get around this by unchecking the 'Use the administration theme when editing or creating content' field at the bottom of 'admin/appearance' page in the admin panel.

Using the Rules Module To Keep Track Of Workflow Stages

I added a new field called "workflow" to my node type (Added through UI at: http://example.com/admin/structure/types/manage/%content_type%/fields) so I could keep track of what stage the user was currently at.

My workflow field ($form['field_workflow']) was a select list with predefined options (stages).

 'new' => 'New', //Default Value For New Nodes
 'foo' => 'Foo',
 'bar' => 'Bar'

I used the Rules module to move the workflow along. Rules allowed me to move a bunch of the 'submit function' code into rules configuration. This made updates simpler as I don't need to make as many module edits.

Rules makes it very simple to do things like 'if the node is new, set the workflow to foo after saving' or 'if the workflow is set to foo, set the new workflow state to bar after saving.'

Organization: Ugh... Setting Up The Actual Flow

First I tried to keep each stage of the workflow as clean as possible by packing alterations in their own functions with a custom naming convention.

function MODULE_stage_new(&$form) {
    //Insert all alterations here for use when node has not yet been saved. It's new!
    $form['field_favorite_color'][LANGUAGE_NONE][0]['value']['#access'] = FALSE;
    $form['field_favorite_food'][LANGUAGE_NONE][0]['value']['#access'] = FALSE;
}

function MODULE_stage_foo(&$form) {
    //Insert all 'foo' stage alterations here.
    $form['field_favorite_color'][LANGUAGE_NONE][0]['value']['#required'] = TRUE;
}

function MODULE_stage_bar(&$form) {
    //Insert all 'bar' stage alterations here.
    $form['field_favorite_food'][LANGUAGE_NONE][0]['value']['#required'] = TRUE;
}

/**
* Implements hook_form_FORM_ID_alter().
*/
function MODULE_form_FORM_ID_alter(&$form, $form_state) {
    /**
    * Place any alterations here that apply to all stages here before the 
    * 'if' statements below.
    *
    * I generally place my validation callbacks and CSS additions here.
    **/

    if (empty($form['nid']['#value'])) {
        MODULE_stage_new($form);
    } else if ($arg == 'foo') {
        MODULE_stage_foo($form);
    } else if ($arg == 'bar') {
        MODULE_stage_bar($form);
    }
}

Now that hook_form_alater() is being used exclusively without a custom callback, you shouldn't have any issues with the ['#required'] elements.

Occasionally I would have to add an '#after_build' function (yea, yet another function to keep track of...) but did so with a common naming convetion. For example, if I needed to add a new stylesheet at the 'foo' stage I would use the following functions.

function MODULE_stage_foo(&$form) {
    //Insert all 'foo' stage alterations here.
    $form['field_favorite_color'][LANGUAGE_NONE][0]['value']['#required'] = TRUE;
    $form['#after_build'][] = 'MODULE_stage_foo_after_build';
}

function MODULE_stage_foo_after_build($form, &$form_state) {
    drupal_add_css(path_to_theme() . "/css/foo.css");
    return $form;
}

I hope this wasn't too overkill or off topic. I honestly found that sticking to hook_form_alter() for node add/edit form modifications saved me a ton of headaches over how I was trying to do it before. The custom callback method using module_load_include('inc', 'node', 'node.pages'); never worked for me the way it seemed to for people writing the examples I found. Then again, I'm still pretty new to Drupal. :)

0

One answer (that I really hope is not the correct one!) to this is to have my custom callback remain in charge of the node creation form's appearance:

function my_custom_callback($arg) {
  module_load_include('inc', 'node', 'node.pages');
  $page['node_form'] = node_add('content_type_name');

  if ($arg == 'foo') {
    // do something informative to the user.
  } else if ($arg == 'bar') {
    // do another thing informative to the user.
  }
}

and then set my requirements and general form alterations in hook_form_FORM_ID_alter(), but this means I'm spreading my code out in 2 places and I've got to look in the arg() function to see what the user's workflow state is, which is generally messy.

  • 2
    You can set a global flag instead of relying on arg(). But yes, that is your only choice, you can not alter the form after it has been returned from drupal_get_form() because validation happens based on the cached form data and that does not include your changes. – Berdir Jul 12 '12 at 0:10
  • Argh! I've tried every combo of drupal_rebuild_form(), drupal_build/prepare_form(), etc., but I just can't get any of my new stuff to take effect. Can you point me in the direction of why the FAPI works this way? Why can't I just clear the cached form data and rebuild it? – Charlie Schliesser Jul 12 '12 at 1:10
  • Implementing hook_form_alter() to change form structure is the correct way to do it, no matter how 'messy' it may seem to you. And you don't need to check arg() in your callback function, since it's already checked and correct settings are set in your hook_form_alter no? All your callback needs to do is call node_add and return the page elements, and let hook_form_alter do the altering. So you don't even need callback function then, you just specify node_add as 'page calback' in hook_menu and 'content_type' as 'page argument'. Then you only need hook_menu and hook_form_alter – Beebee Jul 16 '12 at 10:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.