Last updated September 9, 2012. Created by carwin on February 16, 2011.
Edited by Jackinloadup. Log in to edit this page.

There are many reasons to create a custom User Registration form in Drupal, so I won't get into the "Why?" of it. This isn't a tutorial about necessarily how to use FAPI, but rather a particular way to accomplish something with FAPI. I assume when writing this that you understand hook_form() and the basics of putting your own module together.

Essentially we start with FAPI and hook_form() to build out the form itself in our module:

hook_form()

<?php
function foo_user_register_form($form, &$form_state){
   
$form['name'] = array(
       
'#title' => 'username',
       
'#description' => 'choose a username',
       
'#type' => 'textfield',
       
'#required' => TRUE,
    );
   
$form['mail'] = array(
       
'#title' => 'email',
       
'#description' => 'enter a valid email address',
       
'#type' => 'textfield',
       
'#required' => TRUE,
    );
       
$form['submit'] = array(
       
'#type' => 'submit',
       
'#value' => t('Save'),
    );
?>

The username and email are all that are specifically required to create the account, other fields created with Field UI can be added in at will. You could even call them something different if you wanted, here in the hook_form().

The real magic of saving the user happens in the submit handler of your module, which utilizes user_save().

Submit Handler

<?php
function foo_user_register_form_submit($form, &$form_state){
   
$edit = array(
         
'name' => $form_state['values']['name'],
         
'pass' => user_password(),
         
'mail' => $form_state['values']['mail'],
         
'init' => $form_state['values']['mail'],
         
'status' => 1,
         
'access' => REQUEST_TIME,
    );
   
user_save(drupal_anonymous_user(), $edit);
}
?>

The user_password() function generates a random string to be stored as the user's password.

Adding additional fields to the submit handler

Let's say that we added some custom fields "First Name" (field_fname) and "Last Name" (field_lname) to our users via the UI in d7. We can represent that in our form any way we want to because the key of the $edit array (in our submit handler) is what matters.

<?php
function foo_user_register_form($form, &$form_state){
   
$form['name'] = array(
       
'#title' => 'username',
       
'#description' => 'choose a username',
       
'#type' => 'textfield',
       
'#required' => TRUE,
    );
   
$form['mail'] = array(
       
'#title' => 'email',
       
'#description' => 'enter a valid email address',
       
'#type' => 'textfield',
       
'#required' => TRUE,
    );
       
$form['field_fname'] = array(
       
'#title' => 'First Name',
       
'#type' => 'textfield',
    );
       
$form['field_lname'] = array(
       
'#title' => 'Last Name',
       
'#type' => 'textfield',
    );
       
$form['submit'] = array(
       
'#type' => 'submit',
       
'#value' => t('Save'),
    );
?>

In Drupal 6 we had a 'data' key in the new user array, in Drupal 7 it's been deprecated. To add these fields programmatically when saving a user we add them to our submit handler like so:

<?php
function foo_user_register_form_submit($form, &$form_state){
   
$edit = array(
         
'name' => $form_state['values']['name'],
         
'pass' => user_password(),
         
'mail' => $form_state['values']['mail'],
         
'init' => $form_state['values']['mail'],
         
'status' => 1,
         
'access' => REQUEST_TIME,
             
'field_fname' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_fname']))),
         
'field_lname' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_lname']))),
    );
   
user_save(drupal_anonymous_user(), $edit);
}
?>

If you happen to be fond of adding fieldsets to your forms with FAPI (probably to make use of #states or something) and you slap your extra fields in one, you do not need to have to actually mention that fieldset in the submit handler.

<?php
$form_state
['values']['fieldset_name']['field_fname']
?>

vs.

<?php
$form_state
['values']['field_fname']
?>

If you add the fieldset in betwixt the ['values'] and ['field_name'] in the submit handler, you'll get a NULL value in the database for that field.

Saving roles

In my form I decided I would let users choose their own roles. I created a field of #type "select" and used the role names I created as options. In the submit handler I added $roles as an empty variable and set it within if() statements based on the $form_state['value'] of my field.

<?php
$roles
= '';
    if(
$form_state['values']['user_kind'] == 'A User Role'){
       
$roles = array('1' => 'A User Role');
    }
    elseif(
$form_state['values']['user_kind'] == 'A Different Role'){
       
$roles = array('2' => 'A Different Role');
    }
?>

Keep in mind that the key must match the value in the database. After this simply add the following to the $edit array:
<?php
       
'roles' => $roles,
?>

Set up notification email.

In drupal 6 there was drupal_mail_send() which was great. Drupal 7 took that away and left us with what is, in my opinion, a too complicated function called drupal_mail(). I opted to use php's mail() instead.

In the hook_form() I added a checkbox:

<?php
$form
['send_message'] = array(
       
'#type' => 'checkbox',
       
'#title' => t('Notify user of new account via email.'),
       
'#description' => 'If left unchecked, a message will not be sent.',
       
'#default_value' => 1,
    );
?>

At the end of the submit function I tacked on the setup for a notification:
<?php
$to
= $form_state['values']['mail'];
   
$subject = 'New account created';
   
$headers = "From: something@somewhere.org\nContent-type: text/html";
   
$body = 'A new user account has been created for you at <a href="http://something.org/">Something.org</a>.<br /> Your login details are as follows:<br />Username: <b>'.$form_state['values']['name'].'</b><br /> Password: <b>'.$form_state['values']['password'].'</b> <br /><br /> Please login to <a href="http://something.org/">Something.org</a> and change your password.<br />';
    if(
$form_state['values']['send_message'] == 1){
       
mail($to,$subject,$body,$headers);
    }
?>

It's really important that your headers use new lines "\n" between each other, and I also discovered that it didn't really work unless $headers value was in double quotes.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

Hi,

Thanks this helped a lot, but I still got a problem. I'm trying to save checkboxdata to a custom field. So it can have multiple values. I tried like this but then the first field get overwritten en only the last field is add to the database:

                        'field_other' => array(LANGUAGE_NONE => array(array('tid' => '84'))),
'field_other' => array(LANGUAGE_NONE => array(array('tid' => '83'))),

Is it possible to do this? I just want to create 2 rows (for each tid) in the database.

I don't find any reference to these hooks in the API. Could it be that you mean foo_user_register_form_alter for example?

Actually I'm using hook_form() rather than hook_form_alter() so the syntax foo_user_register_form() is fine.

what about validation ?

Form validation is something I could probably throw in here, though the reason I've left it out is that most people will want to validate in their own way.

Can you help me modify it that it uses the data from a non-drupal db, let's call it realmd. Basically when you create an account, it writes the account, password and email in realmd. Also the login, request new password will call on that database.
What I need is: add a new database, change the registration and login form.

Thanks in advance!

I have found that if you configure your custom module to save roles like:

function hook_user_register_form_submit($form, &$form_state){
  $edit = array(
          'name' => $form_state['values']['name'],
          'pass' => $form_state['values']['pass'],
          'mail' => $form_state['values']['mail'],
          'init' => $form_state['values']['mail'],
          'status' => 1,
          'access' => REQUEST_TIME,
//Make shure you have defined a role of "client" in: admin/people/permissions/roles befor using this module.
//!!!!using/configuring core triggers module to redirect (admin/structure/trigger/user) "Trigger: After creating a new user account" breaks this code (will not set user role on sign up); should hook_form_alter the trigers module to eliminate comflict or fix conflict.
$roles = array('5' => 'client'),
'roles' => $roles,
);
$account->is_new = TRUE;
user_save($account, $edit);
}   

and if you then set up the core triggers module to redirect (admin/structure/trigger/user) "Trigger: After creating a new user account" breaks (will not set user role on sign up) this code.

Hope this helps save some one some frustration.

Craig Marshall

can't get this to work, and not sure if it's because of the function name (should that be foo_user_register_form or foo_user_register_form_alter? how does that change the submit function name?)

if this is working, would it automatically replace the user/register form?

hartogsmith.com

In my custom module I set up my custom form as hook_user_register_form(){}. Then the submit function would have to be hook_user_register_form_submit(){}.

if this is working, would it automatically replace the user/register form?

If you create a custom registration module like this thread shows your core Drupal registration form will not be changed. If you simply want to change core Drupal registration form you can create a module and use hook_form_alter(){} on Drupal registration form if you want.

Craig Marshall

I am trying to follow your code (referred from the buildamodule site) but I am having trouble getting it to work. Would you mind sharing only one of your modules? I would like to create pages with specific user fields, similar to your block idea.

Thanks,
Maria

marshallexcavating is correct. This page is meant to show you how to create a separate user register form so you can have an easier time changing / modifying things.

the foo_user_register_form() in the post above returns nothing.
Should be

<?php
return $form;
?>

added that return $form line but when I call this function it just returns an array().
Should I call some kind of render engine with the MYMODULE_user_register_form() as argument?

Also I'm calling this from within a template not sure if I should pass the form as argument for my template and if so how? ( can I use the function as argument, or should I render it first ? ) which brings me back to my first question ;)

KTHXBYE

Got the form working within my template:
<?php print drupal_render(drupal_get_form('popover_form')); ?>

KTHXBYE

Thanks Jackinloadup for the helpful information, im confuse, you need to add this 2 fields in users table or create another table. I try adding 2 fields in users table. after adding submission data. this to fields got NULL value. help will be appreciated.

I used this code.

'field_fname' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_fname']))),
'field_lname' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_lname']))),

I can do this through Christ

How would I use this form in place of my registration form? I would like to have control over the entire process of registration, validation, submission.