If you allow registrations on your WordPress site, chances are you need to gather some more information about your users. If you absolutely need this information, the best way to get it is to make it required during registration, otherwise there are slim chances that your users will actually visit their profile page and provide that optional information.
Sure, there are countless free and premium plugins that already do that, but you may want to do it manually, either because the plugins don’t do exactly what you want, or because you want to keep your website as lightweight as possible. Yes, there are tutorials also that tell you how to do it, but my research tells me that they are incomplete. What they all seem to be missing is new user registrations from the back-end (you know, when you -the administrator- add a user manually via the dashboard). Heck, the related WordPress codex page doesn’t even mention back-end registrations.
For this tutorial, we’ll add a required field that accepts the user’s year of birth and will be visible in the profile page. We won’t pay too much attention on validation, as it varies per use-case, but we will sanitize appropriately. Let’s see what it takes to add a new, required field in the default WordPress registration forms, from the start (what the user uses), to the middle (what the admin uses), to the end (what both see on the profile page).
If your read our blog regularly, you’ve probably already guessed that we’ll create a new plugin to hold our code. If you’re not sure why, make sure you read our article regarding plugins vs child themes first. Create a new file in /wp-content/plugins/ named registration-fields.php and paste the following content:
Feel free to fill in the blanks or change any of the information. Save and activate the plugin.
First of all, we’ll deal with the visitors’ facing registration form, as it’s usually the most used one. It requires that you’ve enabled user registration by going to Settings → General and checking the Anyone can register checkbox. If you’re working on a live website where really anyone can register, I strongly recommend adding a method to prevent spam registrations. Personally, I use the Invisible reCaptcha for WordPress plugin, and I’m very happy with it.
Displaying the field
Add the following in your plugin file:
The action ‘register_form‘ fires right after the email field, so that’s where our new field will appear. Unfortunately there isn’t any other place we can hook. Line 8 checks the existence and sanitizes the value, in order to re-populate it in line 19. This is useful when the form re-appears, for example when a required value is missing or is wrong.
Right now we can visit the registration page and we’ll see that the field appears. You can find the link to your registration page, either by assigning the Meta widget (it includes a link to the registration form), or visiting directly http://your-website/wp-login.php?action=register
Validating the field
If you now enter a username and email, and leave the year of birth is empty, no error will be thrown. Since we said the field will be required, let’s go ahead and fix that. Add the following:
The ‘registration_errors‘ filter fires before the user is created, so we have a chance of adding our own error messages if something is missing, and avoid creating a user record with incomplete information. All we need is to $errors->add() our errors depending on our requirements. In this case, two different messages are used, depending if the year is missing or is less than 1900. This is were you’d validate a bit more, if for example you wanted to make sure a user is older than 18 years old, based on the year (or more other fields) he/she provided. Finally, we need to return $errors. While we don’t use $sanitized_user_login and $user_email, you could use them to throw more errors, if for example the user’s username contains your brand name.
Now, go ahead and fill in some of the registration form fields. Leave the year of birth empty, or enter a value less than 1900 and press Register. You should see a box listing all errors, including “Please enter your year of birth.” or “You must be born after 1900.“, the ones we added in the code inside crf_registration_errors().
Sanitizing and saving the field
If you do enter a valid year of birth at this point, a user will be registered but the year will not be saved. Add the following code to the plugin file:
Action ‘user_register‘ fires after a user is inserted into the database. Only now can we store their year of birth, as we needed the user ID in order to work with user metadata. Make sure a value exists, and sanitize the value by passing it through intval() as it definitely needs to be an integer.
At this point, you may go ahead and fill in all the registration fields for a hypothetical new user. Once you press the Register button and no errors are thrown, their year of birth will have been stored in your database. How can you confirm that the value is actually stored? You can’t right now, not unless you want to dive into the actual database tables (it’s the wp_usermeta table, in case you’re interested). If you’re impatient to see that it works, jump to the Profile display section below, and then come back here to continue.
It’s time to tackle the back-end registration, that is, the form that an administrator (or other role that can) registers users manually. This might be the only way to add new users if you don’t enable user registrations from Settings → General. You might consider this a bit redundant, but we shouldn’t leave any cases uncovered.
Displaying the field
The back-end registration form has a different format (and more fields) than the front-end form, so it requires different markup, not to mention that there are a whole different lot of actions and filters. To add display the year of birth field, add the following code to the plugin file:
Just like the ‘register_form‘ action, the ‘user_new_form‘ action fires after all fields are displayed, so again, our field will be placed last before the Add New User button. Now, the ‘user_new_form‘ action is used twice in the WordPress core, once for new user registration, and once when assigning an user to a blog (in multisite installations), so an appropriate string is passed through $operation which we check and handle only when it’s equal to ‘add-new-user‘ (line 7).
The $year variable is set in exactly the same way as it was set in crf_registration_form() earlier. Same thing with the <input> field, except for the CSS class name (we re-use what WordPress provides). I’m repeating these as the wrapping elements are now different (following the WordPress format, we now display the field inside a table). We could of course refactor the input element so as not to repeat ourselves, but it would add some unnecessary complexity for the purposes of this tutorial. Finally, the heading on line 15 is not required, but is a nice touch nonetheless.
Go to your Dashboard → Users → Add New, and like previously, you can now see the field, however there’s no validation, no sanitization, and no saving happening at this point.
Validating the field
In order to validate our field, we’re going to use the ‘user_profile_update_errors‘ action.
Again, the validation bit (lines 7-12) is exactly the same as before, therefore it can be refactored, but let’s focus on the differences instead. The ‘user_profile_update_errors‘ action provides three parameters, $errors, $update and $user, that hold the errors, an update flag and a WP_User object respectively. In our case, $update contains a falsey value (and true if the code is reached by the profile update page), and the WP_User object has most of its properties blank, as there’s not actual user at this time. Since we don’t have any plans on allowing updates on the year of birth, lines 3-5 make sure we only proceed if $update is false.
Note that ‘user_profile_update_errors‘ is an action, in contrast with the earlier ‘registration_errors‘ which is a filter, and therefore we don’t need to return anything. In this case, $errors is passed by reference by the caller, so our $errors and the caller’s $errors is actually the same instance.
Sanitizing and saving the field
While a valid year will pass validation and the user will be created, the year will not get saved at this point. We need this:
The ‘edit_user_created_user‘ action is our equivalent to the previously used ‘user_register‘ action. Both actions pass the $user_id as their first parameter. ‘edit_user_created_user‘ also passes a $notify string parameter (as in the last parameter of wp_new_user_notification()) but since we don’t do anything with it, we can re-use our crf_user_register() by hooking it without any modifications.
You guessed it. You can now register a new user, and their year of birth will persist.
All this hard work, and we still can’t see a user’s year of birth. Let’s fix it!
The HTML markup inside crf_show_extra_profile_fields(), is again similar to crf_admin_registration_form() but instead of displaying an input field, we’re just printing the value.
Both the ‘show_user_profile‘ and ‘edit_user_profile‘ actions are exactly the same (pass the user as a parameter) and fire at the same point, but on slightly different cases. ‘show_user_profile‘ fires when you’re viewing your own profile page (or any user their page, for that matter), while ‘edit_user_profile‘ fires when you’re viewing another user’s profile (or any user that has the capabilities to do so). You could leverage this difference to, for example, create a scratchpad that only you can see, or a user rating feature where the user can’t vote for himself but see the ratings instead.
Enough with the examples. Make sure you create a user, and of course provide a year of birth. The system won’t let you proceed without it anyway! Then go to Dashboard → Users and Edit your new user. Scroll down, and voila!
That was it! With just a few lines of code, we managed to add a required registration field, and learned a whole lot in the process. Some of the hooks can also be useful by themselves, allowing to further restrict registration criteria based on existing fields. Can an existing plugin do that? Perhaps. Can the ready-made plugin be as flexible and expressive as your code? Probably not!
What do you think? Did you find this tutorial useful? Do you have any questions? Leave a comment and let’s discuss!
The full code can be found in GitHub.