I found some code on the web to implement multi-step forms in Drupal 7, and currently dissecting it and trying to understand it. (My PHP understanding is decent, but not "great.") I've got most of it worked out and understood, and now I'm flipping the code to put into a multi-step user registration form. So I print out the $form array on the user registration form, and here's a snippet of what I get (edited down for simplicity).
Array
(
[account] => Array
(
[name] => Array
(
[#type] => textfield
[#title] => Username
[#maxlength] => 60
[#description] => Spaces are allowed; punctuation is not allowed except for periods....
)
[mail] => Array
(
[#type] => textfield
[#title] => E-mail address
[#description] => A valid e-mail address. All e-mails from the system...
)
)
)
Now here's a snippet of my code for my custom module...
static $steps = array(
'account' => -1,
);
return $steps;
In a nutshell, this code targets and manipulates everything in the [account] array, and it works.
However, what I need to do is actually target the 'account => mail' and 'account => name' fields/arrays specifically. How would I do that in the above PHP code? I tried variations like 'account->name', 'account => name' and even 'account[name]' but nothing works. The 'account' in the code above is listed in a string so I can understand why those previous attempts didn't work, but removing the quotes invalidates the code.
Comments
Hmmm, something I overlooked,
Hmmm, something I overlooked, the line that $steps (as $key in the loop) is called....
Particularly it's the "else" statement that I'm trying to make work. Now I understand why 'account' has to be a single variable and why manipulating it didn't work. There would be no problem if 'name' and 'mail' was on the same level of the array as 'account', but it's one level down.
I changed the $form[$key]['#access'] line to $form['account'][$key]['#access'] and it worked, but that will only work on arrays on that particular level. Acceptable for now I suppose, but problematic if I need to target something a level up on the while loop. Trying to figure out how to revise this code...
Please elaborate
Hi,
Are you saying you want to take the existing user registration form and break into steps? Or, are you adding fields to the existing registration form and then breaking that extended form in to "multi-steps"?
Are you simply trying to figure our how to implement a multi-step form with Drupal 7? Or do you need to customize a user registration form specifically?
The Drupal form api (fapi) docs are located here:
http://drupal.org/node/751826
http://api.drupal.org/api/drupal/developer--topics--forms_api_reference....
http://drupal.org/project/examples
http://api.drupal.org/api/drupal/includes--form.inc/group/form_api/7
Great, looks like you just added some more source code from whatever tutorial you are using. You need to post a lot more for anyone to help you :)
In general, you implement hook_form_alter() http://api.drupal.org/api/drupal/modules--system--system.api.php/functio... to customize an existing form. In addition, you need to become familiar with the different form handling phases (definition, validation, submission). You'll need to implement a custom submit handler to control the various steps. You will also need to customize the form definition so that it stores the various "step" states.
My recommendation is to get into the habit of reviewing Drupal's source code and get your self a source code debugger (like Netbeans with xdebug http://netbeans.org/). Some of the form you are trying to customize resides in the user.module listing, user_account_form() method. I'm not say change the source, but look through it to see how the form is being constructed and built. No need to guess, Drupal is open source, That means you can look right at the source code :)
Feel free to peruse my blog http://public-action.org/content/drupal-7-form-api-drupal-7-multi-step-f.... It's a post showing you how to implement a multi-step form using Drupal 7's form api. It might help. The module examples located here http://drupal.org/project/examples are always the best place to start.
Hope that helps.
Hi Jason, Thanks, I'll check
Hi Jason,
Thanks, I'll check some of that out, though some I know already. I also came across that tutorial on Google today, but as I wasn't building a form from scratch, I didn't look too much into it yet and wasn't sure it applied to my situation. (In addition, I had already build some of my following code from a while back prior to that tutorial existing.)
Pertaining to your first question, I guess I'm saying a little of both. Right now I'm working on making the user registration form multi-step as is: just name and email fields. Later on, I can foresee adding additional fields to the user registration form and/or a terms of use, so that's where my concern about targeting the array level comes in.
I am adapting the code based on this tutorial and have already successfully implemented it into a node add/edit form. I've taken that code and re-purposed it with different hooks into a multi-step registration form, and I actually have most of that working too. The fields validate / error if not filled in, and you can go forward and back, yay. The only issues right now is that the values are not saved whenever you progress or go back in the form, plus the "create new account" button still shows up on the first step, but admittedly I'm still working on the code at this moment and have overlooked something. Below is current of this moment:
A few ideas
Hi,
To remove the "create new account" button, looks like you should
unset($form['actions']['submit']);
. I'm looking a the source that creates the button (user.module, user_register_form() ). That is where I got the $form element definition from.Next, change the following:
to
What the code is doing is, defining a list of functions that are called when the button is clicked (submitted). You want to call the user_register_submit() method (also in user.module),
FYI -- Really nice job for someone new to the Drupal form api !! :) Really close. Sorry, I haven't tested the code. I looked at your code, checked the source, etc. See if that helps and I'll check back here. Again. really nice work!! :)
Ooops! Almost forgot. You should add a switch statement to your hook_form_alter implementation. As written, your module will attempt to modify every form. You want something like
switch($form_id) { case 'user_register_form' : ...... break; }
The unset suggestion for the
The unset suggestion for the button worked, thanks.
I was looking through the D7 API for a user registration function (and I got lost looking through the user module), I used user_account_form to see what would happen, but yes, nothing. As for replacing it with 'user_register_submit,' what happens when I do is that the user form automatically submits whenever I click the next button. So I took that off and just left the 'multiform_node_form_next' function.
The big issue with the form currently is not remembering values when you come back to them. I noticed some multi-step forms pass values to $form_state, but what is weird is in the example tutorial I'm using for the multi-step node form, I don't see anything being passed to $form_state but the step number, and all fields remain filled in when you go forward and back and submit. Wonder why it works for that but not this.
I think I got it worked out
I think I got it worked out now as far as the disappearing fields goes, I did some tweaking based on the tutorial Jason posted along with the Examples module (very, very cool, BTW) and came out with the following (note, haven't implemented the "case" yet as suggested)....
So basically it stores the variables with the $form_state. Again, curious why the other example did not need it, but can't knock it if it works.
But I still have one lingering issue, and this goes back to my original question. Via Drupal 7 fields, I added a "Full Name" field (field_profile_name) to the user account and displayed it on user registration. As I thought it would, the $form array comes out like this (I'm not posting the entire thing here, shortening it for simplicity's sake)...
So basically, the field_profile_name lives one level up from the name and mail arrays, where the regular user registration form items lives. As I pointed out in my second post, in the form_alter hook function I had to use "$form['account'][$key]['#access']" as opposed to "$form[$key]['#access']" in order to not display the username or email fields on the steps they don't belong on.
So is there a better way of working that portion of the code to traverse the array based on level?
Great Work -- Let's Complete the Post
Hi,
Really great job!! :)
You just solved many different issues all in one post:
Please consider other folks in the community who will be searching this forum when facing the same challenge. Those folks will be looking at the subject line of your original post. Currently, your subject line does not describe the actual issue solved. Therefore, please change the subject line and please prefix it with [solved] (per the forum guideline http://drupal.org/forum-posting ) :)
Sorry to harp on the point of changing the subject line, but again, you've demonstrated how to solve an important challenge. Please contribute back to the community by making you post available via a search. How is it available via a search? Answer: Change the subject line so that it reflects the main issue (e.g. Altering User Registration Form to Multi-Step Drupal 7, or something better :) ).
I'll be happy to answer you second subject briefly, However, please consider creating a separate thread if you indeed want to engage in a lengthy discussion of "How do inspect Drupal's Data Structures". That subject to me, is a separate thread. Again, why a separate thread? Well we want other folks to be able to find the solution.
Now briefly -- Data Structures:
Drupal 7 expands functionality. For example, a programmer can now dynamically add all sorts of attributes dynamically to "input forms", "page reports", the database, widgets, content types, fields, field attributes, etc etc etc etc.
As Drupal processes all of these dynamic attributes, it builds up complex data structures. Your question is, "How to I inspect a dynamic complex data structure?". The answer is:
Again hope that helps. Please change the subject line of you post so that other folks in the community can use it.
Also, the tutorial you referenced uses a static variable to maintain state. I agree, if it works we have plenty of better things to do than critique working code. I personally don't recommend using that portion of the code. Using the $form_state['storage'] data structure is a standard Drupal solution, thus preferable.
So it was that static line of
So it was that static line of the example code that was saving the variables, that explains a lot. So I guess I went the right route after all ;)
Thanks for all your help. While I still need answers to my original question, I will repost as a new topic, though would like to see if I can get it working first before I post a new one.
Thanks for your work.This is
Thanks for your work.
This is very helpfull. I'm new on drupal and i want to do a multistep registration with profile module. But a fieldset has been generated and stay on my step 1. How can i manage this ?
thanks
ps :(english is not my first language, so, sorry for the mistakes)
Where does this code go and how is it called?
I am not sure where does this code go?
Is it placed in user.module file of the user module? or is it neccesary to create our module?
Which lines must be changed to call this multiform_form_user_register_form_alter fucntion? and where?
Thank you
is there a way to pull profile2 fields?
Hi,
the code works great! I am trying to pull custom profile2 that I created for user registration and I am struggling on using it to step2? is there any clue how to implement it?
thank you
How about a step-by-step
Boy, I would love to be able to do this, but... It all sounds foreign to me as a designer. Would it be possible to have a step-by-step or maybe a module out of this?
I am trying to use this code,
I am trying to use this code, The username field is hidden in step 1, but shows up empty in step 2. How do i hide it in step 2?
You can change code of step 2
You can change code of step 2 according to your program where function call define('MULTIFORM_USER_REGISTRATION_LAST_STEP', 2);
I'm subscribing this thread
I'm subscribing this thread for my future references.
I also need to build multi step registration form in Drupal 7.
Thanks a lot for the light guys :)
Cheers
How you fixed problem with hiding drupal password field?
Drupal password field is needed to be hidden. But then i can't return it to work and to save data that user inputs in step 1.
Any ideas?
subscribing.
subscribing.
trouble with the password field
this is a superb reference, thank you!
my only problem is that I cannot seem to get the password field to save properly. I have most fields on step 1, including password. After completing step 2 and clicking the 'create account' button, I get an error about a missing password field.
any tips on this one?
thanks again,
-Benj
Post your code
Hi,
I would recommend posting the relevant portions of your code. It's highly unlikely someone can offer suggestions if they haven't reviewed what you coded :)
Also, note the strong recommendation for developing locally with a source code debugger. Are developing on a local installation with a debugger, as recommended?
Hope that helps :)
Good Luck
Same problem
I'm having the same problem with not being able to save due to Password validation failing. Here's how to reproduce the problem:
(1) Go to Admin > Configuration > Account Settings, and disable "Require e-mail verification when a visitor creates an account", so that the registering user can set their password on the form
(2) Modify the above "multiform_node_form_by_step" function to be:
(3) Modify the beginning of the "multiform_node_form_previous" to be:
The end result is that after you submit the form on page 2, there is a validation error message that says the "Password field is required." I've been trying to step through the code, but without much previous drupal programming experience, I haven't made too much progress. All I can tell is that when the validation is called on the final submission, the name someone makes it way back into the $form_state['values']['name'] field, but the password does get stored back into the $form_state['values']['pass'] field (the password is however still stored in $form_state['storage']['pass'].
Any thoughts on how to fix this problem?
Approaches to solving the password confirm problem
After digging through the code, I think there may be several different approaches (only one of which I've tested so far). I'm not sure which would be the "correct" way of doing it- they all kind of seem like hacks to me, but thats probably because I'm so new to Form API and the FAPI workflow
Approach #1
The first way is most in line with how the original code was set up. That is, during the "Next" submit handler, the values are pushed into $state_form["storage"], and when the form is subsequently created, those values are pushed into the form elements as their default values.
However, password_confirm elements don't have a #default_element attribute, so this wont work:
If you look at form.inc's form_process_password_confirm, it actually populated the field not based on a #default_value, but based on a #value attribute not listed in FAPI. And of course, that's split up for the two text fields ($element['#value']['pass1'] and $element['#value']['pass2']). Keeping it in line with the original solution might production something like this (which also doesn't work quite yet):
The problem with this code is that when the form is submitted (via Next button) its rebuilt before the Next button's submission handler is called. Thus, the password field is rebuilt with blank values b/c the submission handler hasn't put the values in storage yet. And these blank values override whatever the user just submitted. So that page actually fails to validate even though the user just entered in the password.
So the solution is to only push the stored values to the form element, if the stored values actually exist:
This will work (when used with the original code snippets I listed in the previous comment), but it feels like a bit of a hack to me, given what the documentation says about #value and password_confirm.
Other Approaches
The other approaches I think might work would be to use a #process callback for the password_confirm element, inserted before form.inc's form_process_password_confirm. Or to use an #element_validate callback, inserted before form.inc's password_confirm_validate function. (I tried just using a general form validator, but it was being called after the individual element's validators). The only other approach I can think of would be to use the form_type_hook_value to insert all the stored values back into the form, but I'm not really sure how to set that up.
This is what I needed.
This is what I needed. Thanks everyone......... :)
Working code sample (for custom module)
Hi guys, the following is some code I got working for one of my projects. It needs to be added into your own custom module, but should work almost as-is. Just change everywhere you see "mymodule" in the code below to the name of your module and this should do the trick.
Thank you very much
This worked. Thanks a bunch
Drupal Scavenger
Thanks! I added some functionality for custom fields.
Here is a multi-step registration form alter that I got working with mulitple custom fields. Originally my problem was figuring out how to cache the custom fields from page to page before the submission.
I had to add some logic to the "vmb_rules_register_back()" function in order to store the values in the ['storage'] nested array.
It is working now.
Hopefully this will help someone else out.
Wowee
This helps me out alot. Thank you. You saved me a ton of time.
One outlying issue I am having difficulty with is that I have an Addressfield Autocomplete field that is hooked in with Geofield (on step 1 of my user form) and it just does not want to Autocomplete the addresses, and also does not save the addresses.
Here is my version of your code:
--
anawillem
http://jellobrain.com
Callback function never get invoked
Thanks for sharing this code!
I have a little trouble, could you give me a tip please ?
This line of code
'#submit' => array('lex_misc_register_back'),
is never get called for me, do you have any idea why ? should I change something in the code, does the submit button ("Create new account") of the user registration form interfere somehow ? I am trying to figure out what going on two days now. I have exactly implement all the steps described above but with no luck. The callback function is never get invoked.Thanks in advance.
If $form['#submit'] is never
If $form['#submit'] is never called, it means that there is a submit handler attached to the button that is being clicked. You will need to attach the submit function to that button, instead of the form.
Thanks for your quick
Thanks for your quick response, I really appreciate that.
I am not an experienced developer though, do you mean this:
$form['actions']['submit']['#submit'] = array('lex_misc_register_next');
If I am not wrong this line adds a submit handler to the submit button ("Create new account"), correct ?
Works great - something extra
I found your code is working brilliantly and apart from the fact that I need to work out a three part form (my problem!) I also found out that form_alters render in a specific order. In my case the legal module was rendering after my alter so I was unable to manipulate it. A quick google revealed this. Simply use at the end of your module if required.
thank you for the code can
thank you for the code
can you just tell me what this module will do? will it take the current user registration form and break it in to multiple pages?
what if i have extra fields? how would u define which field goes on which page?
Yes
This module will break the registration form into multiple steps. If you read the code comments you can see where you take the (complete) form, and hide the fields you want only on page 2.
Then on page two, you take the (complete) form and just hide the fields that were completed on page 1
This is exactly what I want
This is exactly what I want to get done. I am working on a site that requires multiple step registration and role selection. I will like to add other fields if possible. Can you please explain to me which module to modify as stated above.
Thanks
Create your own
You will need to create your own, custom module to use this code. There are some good videos on Drupalize.me and buildamodule.com that will walk you through the process of creating your own module if you have not done it before.
One word of caution, please be safe and do not attempt to develop a new module on your live site, build one locally, or on a test or dev copy of your site first, then once you confirm it's working push it to the live site.
I got this error message on
I got this error message on the code I copied from above.
Parse error: syntax error, unexpected '.' in /home/asktechs/public_html/job/sites/all/modules/multi_reg/multireg.module on line 94
What does the error message means?
A couple of suggestions
Hello,
You might consider starting a new thread. This is a pretty long conversation your add too :)
Next, try using a programmers editor that understands php syntax. Those syntax exceptions happen to everyone, that's why so many programmer editors have built-in syntax checkers. For example, I'm using VIM right now. It takes a little to get used to, but it does syntax checking on almost every computer language I use.
Good Luck :)
How can i add more than two steps?
Hi! I'm trying to use your code with a profile2 register form whith a lot of fields and i need split up this in four steps. How can i do it?
How To Add More Steps?
Hi there, can anyone help me to expand this code to add 4 steps while registration?
Also I want to use profile2 profile form on second step, can anyone give me an example?
Thanks in advance.