Drupal - node form validation methods in hook_form_alter should be set using #after_build to always execute on form submission

Last week, I was trying to add a custom validation method for node form of a particular content type in Drupal. My initial approach was as naive as specifying my custom validation method in hook_form_alter, something like the following:

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function mymodule_form_alter(&$form, &$form_state, $form_id) { switch($form_id) { case 'artist_node_form': $form['#validate'][] = '_mymodule_custom validation'; break; } }{/syntaxhighlighter}

However my custom validation method was not always called when the node form was submitted. Confused, on a little testing and stepping through the Drupal code, I found that the custom validation method was only called when all core Drupal validation methods on the node form succeeded. If any native validation on the node form (where native means the validations performed by Drupal on the submitted form content based on your assocaited content type's settings) failed, then the custom validation method was not called.

And this was not what I wanted. This meant that the user did not see all data entry errors on the form together. Only when the first set (i.e. Drupal's validations) on the form passed was the next set of validation methods (i.e. the custom validation methods on the node form) were invoked. So, after getting errors first time, correcting those and re-submitting, the user might again get validation failures triggered by custom validation, which was not very user-friendly.

Clearly I needed a way to somehow tell Drupal to always invoke the custom validation method on each form submission, so that all validation errors could be shown to the user together.

After some playing around, I decided to hook my custom validation method in the #after_build method instead of doing it in hook_form_alter. So, the updated code looked something like the following:

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function mymodule_form_alter(&$form, &$form_state, $form_id) { switch($form_id) { case 'artist_node_form': $form['#after_build'][] = 'mymodule_custom_after_build'; break; } } function mymodule_custom_after_build($form, &$form_state) { $form['#validate'][] = 'mymodule_custom_validation'; return($form); } function mymodule_custom_validation($form, &$form_state) { //My custom validations went here. }{/syntaxhighlighter}

Here I specify #after_build method for the node form in hook_form_alter. Then in the #after_build method, I specify the #validate method for the custom validation I needed.

And it now worked exactly as I needed. My custom validation method was always called which meant all input errors were shown to the user together on each form submission for the node form.

An important point to take care above is that you need to return the $form object from the #after_build method, or it won't work.

It lacked sense to me that Drupal does not invoke the additional validation methods when specified directly in hook_form_alter but invokes them when spcified in the after_build method for the form. I searched a bit and found this issue being reported on the official Drupal issue queue on drupal.org, but which was closed with a comment that it works as designed. For me, this is not the correct design, but then, every developer has his own way of looking at things...
Atleast there's was way to have it done so that functionally it behaves as you want it to.

 

Comments

Nice tip, thanks! :)

Hey dear  Thats nice idea to add one extra validation array elimant in node_validation

This is usefull for me
thanks ...

Nice Tip. You are my hero!!!

rahul's picture

Smile thanks!!

Thanks your post helped me. :)

Great article! One small improvement would be to include some validation code in the mymodule_custom_validation() function, I think this would be useful.

Thanks

rahul's picture

Well Eddy, there's a comment in that method mentioning you should add your validations there. I guess that should be enough for most users.

Thank you!! Saved me lots of time!!

Thanks a lot. Greetings from Florianópolis, Santa Catarina, Brazil.

Really nice article. Thx!

Thank you for your help. I need some add. When I do this not return to form but shows the comment.

How do I post when validation fails?

My code is :

function shiftvalidation_form_alter(&$form, &$form_state, $form_id) {
    print_r($form_id) ;
    if ($form_id =='work_schedule_node_form') {
        $form['#after_build'][] = 'shiftvalidation_after_build';
        echo '<br />' . $form['taxonomy'][3]['#title'] . '<br />' ;
    }
 }

 function shiftvalidation_after_build($form, &$form_state) {
    $form['#validate'][] = 'shiftvalidation_shift_validate';                   
    return($form);
}

 function shiftvalidation_shift_validate($form, &$form_state) {
    echo 'This is a test' ;
  }

Can you help me ?

Thanks.
Manuel.

rahul's picture

Hi Manuel, any errors in form validation should be flagged using form_set_error to prevent further form processing.

I preciate your fast answer.

Thank you.

Thank you so much! Just wish I had found you a couple weeks ago...

This example was very helpful.

Thanks, works brilliantly!

Do the $form_id is available in the custom after build function? When I tried to call form_id in this function, I got the warning "missing parameter 3" or can we pass any custom varaible to after build function?

rahul's picture

Nopes, neither is form_id available as a parameter, nor can you pass custom variables to the after_build callback.
But have a look at the $form parameter. I do not recall exactly but form _id should be available either as $form['form_id'] or $form['id'].

Yes, I got the id in $form['#form_id'].. Thanks a lot...

I can use #after_build and #validation in form_alter of edit node, I upload images in field_collection but not start validation when save node. Any ideas? Thanks.

rahul's picture

Hi Laura, which version of Drupal are you using? And can you please elaborate "upload images in field collection".

thanks, this article save me a lot of time and headache.

thanks again.

Amazing blog here, some interesting ideas and thoughts. Appreciate your efforts and hope you will keep on producing such a quality content. Wish you good luck, thanks!

Hello rahul,

Can you help to override the content type submit button function to my own function. I want to submit that data into different custom table.

Thanks !!

rahul's picture

I think you can use hook_form_alter and change #submit for the form and/or the submit button. I haven't tried doing that myself tough.