Magento provides a simple user account signup page. However, some online stores might want to collect relevant customer information for more targeted and personal interactions with their customers. Some examples of details that could be collected include shoe size, clothing size or favourite colour.

Adding your custom fields on the signup form is not difficult, but it is not trivial either, since one cannot use the Magento backend for the task. As an example of how it can be done, I will demonstrate the addition of a new custom field to the signup form asking new users to enter their favourite ice cream flavour.

  1. We need to make a module to extend the functionality of the customer model. Start by adding the module xml file to the app/etc/modules/ directory. You need to decide on a module name. In this example we will use the Fontis scope, and call the module 'Customer'. Therefore we need to create a file named Fontis_Customer.xml and add the following content:

    <config>
    	<modules>
    		<Fontis_Customer>
    			<active>true</active>
    			<codePool>local</codePool>
    		</Fontis_Customer>
    	</modules>
    </config>
  2. Adding the above file means that Magento is now aware of a new custom module. Now, we need to create the module itself, which must be located in the app/code/local/Fontis/Customer directory. Inside this directory, create an etc directory and copying the following file inside:

     app/code/core/Mage/Customer/etc/config.xml

    Edit the file and change :

    <modules>
            <Mage_Customer>
                <version>x.x.x</version>
            </Mage_Customer>
    </modules>

    to:

    <modules>
            <Fontis_Customer>
                <version>1.0.0</version>
            </Fontis_Customer>
    </modules>
  3. This file contains two different <fieldsets> - one in <config><admin> and one in <config><global>. At the end of the second one, in the <config><global><fieldsets><customer_account> scope, add:

    <flavour><create>1</create><update>1</update></flavour>

    In this example flavour is the chosen attribute code. The attribute code that you choose here will be important in all the following steps.

  4. Now we need to add the attribute to the Customer Entity Setup Model. To do this, copy the getDefaultEntities method from app/code/core/Mage/Customer/Model/Entity/Setup.php file to a new file, Model/Entity/Setup.php in our module. Put the method inside the following class:

    class Fontis_Customer_Model_Entity_Setup extends Mage_Customer_Model_Entity_Setup

    Then add the following code at the end of the attributes array inside the customer arrray:

    'flavour' => array(
    	'label'		=> 'Ice Cream Flavour',
    	'visible'	=> true,
    	'required'	=> true,
    ),

    In this case the new attribute is set as compulsory by adding 'required' => true, to the end of that array. If you want the attribute to be optional, remove the required lines.

  5. The new attribute needs to be added to the Magento database. The best way to do this is to tell Magento to insert it for you. You can either create your own script that uses the Magento code or you can just drop the following code into one of your template files:

    $setup = new Mage_Eav_Model_Entity_Setup('core_setup');
    $setup->addAttribute('customer', 'flavour', array(
    	'label'		=> 'Ice Cream Flavour',
    	'type'		=> 'varchar',
    	'input'		=> 'text',
    	'visible'	=> true,
    	'required'	=> true,
    	'position'	=> 1,
    	));

    A handy place to add the above code is the <theme>/template/customer/form/register.phtml file since it needs to be edited to add a custom field for the new attribute. Make sure the above code is enclosed with

    <?php
      ... 
    ?>

    To add the attribute (ie. run this code) view the register new customer page. Once you have viewed the page this code can be removed.

    In this example the attribute type is set to varchar and the input is set to text. This needs to be modified to match the requirements of each specific attribute.

  6. At this point, the attribute is installed and it will show up in the backend. We need to modify the frontend, so that the customers can enter values for the custom attribute. There are two fairly similar phtml files:

     <theme>/template/customer/form/register.phtml
     <theme>/template/customer/form/edit.phtml

    As a minimum, you need to add a new text box in the register.phtml file, since that is what customers see on the register page. You can add the same text box in the edit.phtml file so that customers can make changes to the original entry that they have made on sign-up. Make sure that the ID and name of the new input text box match the attribute code chosen in step 3. In this case it is 'flavour'. The following shows the sample code that adds a new text box with a label:

    <div class="input-box">
     <label for="flavour"><?php echo $this->__('Favourite Ice Cream Flavour') ?><span class="required">*</span></label><br />
     <input type="text" name="flavour" id="flavour" value="<?php echo $this->htmlEscape($this->getFormData()->getFlavour()) ?>" title="<?php echo $this->__('Flavour') ?>" class="required-entry input-text" />
    </div>

Address details


If you only want to add fields for address details you are in luck. Those fields are already setup in the template/customer/form/register.phtml file, but they are disabled by an inactive if statement that so far has not actually been linked to anything else in Magento. Therefore to enable the address related fields you simply need to comment out or delete the following statements:

<?php if($this->getShowAddressFields()): ?>

and

<?php endif; ?>

Note that there are two occurrences of this if statement. One is around the area where the customer actually inputs their data and the other is at the bottom of the file. The first statement shows all the areas for the customer to enter their data and the second statement is around some JavaScript code that sets up the State/Province select.


Removing phone number field


One final issue relates to the telephone number field. By default it is considered a required field and that is hard-coded in the input validation function. If you do not want the telephone field to be mandatory (or any other field for that matter), you need to remove the relevant statement from:

magento/app/code/core/Mage/Customer/Model/Address/Abstract.php

This is the code that needs to be removed from public function validate():

if (!Zend_Validate::is($this->getTelephone(), 'NotEmpty')) {
    $errors[] = $helper->__('Please enter telephone.');
}

In order to make sure that your Magento installation still works properly after you do an upgrade, rather than changing the core file, the safest option is to copy the above file into the following file structure:

magento/app/code/local/Mage/Customer/Model/Address/Abstract.php

Since files in /local don't get affected when you run upgrades, it means your signup form will work as expected after an upgrade.


Magento Compatibility

Post originally written for Magento version: 1.3.2.4
Tested with Magento version(s): 1.3.2.4

Update: Due to a change in core Magento files the technique for adding custom customer attributes had to be modified. This blog post has been updated accordingly, and current instructions have been tested with Magento 1.3.2.4 -- 16/10/2009

Comments

Thanks so much for this article Denis. I think I've followed all the steps here, and I've added my input field to the right spot, but I'm not seeing the custom field show up either in the database or in the administration section. After modifying these files, is there anything that needs to be done to sort of apply these changes in Magento?

Apologies for the long delay, but we have finally been able to work out the issues with adding custom attributes. The instructions have changed substantially. If you are still interested, give it a go and let us know if you encounter any further difficulties.

I've done everything listed here but the values just won't take in the database. The field appears on both public and admin, but can only be edited in the admin. Any clues?

Same thing for me. The fields show up on both the frontend and in the admin panel. When a new account is created none of the data entered into the new fields is saved however. If I go into the admin panel and manually put in the information for the customer it does save correctly there though. For some reason on the frontend its just not saving any of the information for the new fields to the database.

Same problem for me...

dont forget to add your new fields in app\code\core\Mage\Customer\etc\config.xml under

Hi Will H, Brian and Spi,

Basically, when a customer saves the account create or edit form, Magento goes through a list of all the attributes that it knows it needs to save on the customer. It matches this attributes with fields on the form, and saves everything. Therefore, if the saving isn't working, there are two main things to check. Firstly, check that the name and id fields of your form elements are correct. If they are, then the other possible problem is that you have not added your new attribute to the list correctly. This is detailed in step 3 which shows how to add it to the list. It's also possible that you haven't created your module properly, which would be causing your attribute not to be in the list. For a quick check that your module is correct, rather than editing the config file that you copied into the local scope, you can try adding your attribute to the original file in the core scope. This is not a long-term solution, but is a quick and dirty way of determining whether the local module loading is the source of the problem.

Very nice tutorial !
Is there a way to add a customer attribute and make this modification upgrade-proof ?
In /app/etc/local.xml it's written that this file should not be modified.

Thanks a lot.

Hi Denis,

Have you managed to get this working on the latest Magento yet? I really need to get this as an option but I'm stuck at the moment.

We have devised a new technique that works on Magento 1.3.2.4. Please try it out and let us know if there are any problems.

I think I love you. I appreciate Magento is built on an MVC, however it seems to be a little less straightforward than say CodeIgniter and consequently, adding custom fields has been a bit of a nightmare!! Thank you for this fantastic tutorial!

What about the function getFlavour() that you use int point 6???
I suppose it'll grab the data from the DB for fill the textfield. but how?
because if you put it in the edit.phtml you'll get an error cause there is no getFlavour function...

thanks a lot.

Fede

I'm having the same problem as Federico... I'm getting the following error: Call to a member function getFlavour() when I try to run edit account.

-John

Thanks so much for the tutorial. It has provided a lot of insight into the inner workings of magento. However, I can't seem to get edit.phtml to work. I get the following error: Call to a member function getFlavour(). Any ideas?

In the edit template the code should be slightly different:

<?php echo $this->__('Favourite Ice Cream Flavour') ?>*
htmlEscape($this->getCustomer()->getFlavour()) ?>" title="<?php echo $this->__('Flavour') ?>" class="required-entry input-text" />

getFormData has become getCustomer!

yes this fixed :D!!!

Figured out my problem from before...

I was putting <?php echo $this->htmlEscape($this->getFormData()->getFlavour()) ?>

when I should have put <?php echo $this->htmlEscape($this->getCustomer()->getFlavour()) ?>

I use this :

<?php $customer = Mage::getSingleton('customer/session')->getCustomer(); ?>
<?php echo $customer->getResource()->getAttribute('flavour')->getFrontend()->getValue($customer); ?>

It works !

But unfortunatly, it's impossible to register modification from front end.

Perhaps can you help me ?

I don't quite understand what you are trying to achieve with that piece of code, could you elaborate on where the code is placed and what it is supposed to do?

Curious if anyone has figured out adding this into the one page checkout procedure? I think based on what i've read itwould involve adding fields under billings etc in config.xml and then name of the input field in billings.phtml would change to billings[flavour] in this case -- this is key as most of your customers aren't going to be going to register a account seperate of the checkout process and would be great to figure out.. that being said tutorial worked great, cheers!

It is possible, though unfortunately, the checkout isn't really designed to take extra fields. What happens is the new customer isn't actually created until you submit the order. So, between when the customer enters their address details and when the customer object is actually created the data they enter needs to be stored somewhere temporarily. One way to address this is by making some database changes to add columns to one or two of the order tables. We'll see if we can address this in a future post.

If I do this, and you do this, which customer module will Magento choose?
Are the field lists from different Setups merged at some point?
You add ice cream flavour, another mod adds shoe size...
What would we see in the back end... ?

Although copying the files to local namespace prevents an update from destroying your mod, what if the update has new customer fields?

Good info posted here thank you.

You are quite correct pointing out the namespace issues between different modules. Magento's behaviour when it comes to this is fairly straightforward. If something in core is overridden by a something in local, the local one takes preference. The problem becomes that if two modules in local override the same thing in core, only one of them will actually be used. It seems that when this situation arises, Magento gives preference to whichever module name is first alphabetically. For example if Fontis_Australia and Fontis_Customer were both to override the same Core_Customer config file, the Fontis_Australia module would be used and the Fontis_Customer would not.

To avoid this, the trick is to override the least amount of code that you can, and avoid repeating core code in your own modules whenever possible. Yes, if a new version of Magento comes out with a new custom customer attribute this attribute won't be loaded, but to make it loaded you just have to put the relevant lines of code from the core config file into your local config file.

Hi on every tutorial about the attributes i see how to add a new attribute - did every body know how i can update an existing customer_address attribute for example "region" or "country" ???

For the frontend it is easy to modify the validation of this fields, but i search for a global solution.

If you look at step 5 of the post, we use $setup->addAttribute() to add the attribute to the database. You can use that same function to modify existing attributes as well. Just put in the attribute code that you want to edit and you can then make the changes to it that you want with the parameters in the array.

Hey,
Thanks for the tutorial. I am currently running Magento ver. 1.3.2.3 and do not plan on upgrading anytime soon due to numerous custom core code changes I have made. I have created a module from the information above, but I can only see the data from the custom created field via mysql database. I can see it is enabled in my System>Config.>Advanced section, but it's not feeding the feild data into the customer's account info.

What code do I need to change to make this module show up in my backend for version 1.3.2.3?Is there an old post I can look at before this one was updated?

many thanks in advance,
chad

First off, thanks for this tutorial. I went through and i can see the module is working when I look in my SQL database, but do not see any info on the backend being stored/displayed uner the customer info section. I am running magento version 1.3.2.3, and due to numerous changes I have made to keep magento up and running, I do not plan on upgrading for a while.(4 prior upgrades and every time bad news)

I see this has been updated for 1.3.2.4; Do you have a old tutorial for the code changes needed to see the custom stored field data on the back end? Or any suggestions on what code changes are needed? It's getting a little old looking the info up via SQLdatabase everytime. :)

many thanks,
-chad

If the customers information is being saved but is not showing up in the admin panel, then there are two possible causes. One is that step 5 where you run the $setup->addAttribute() method didn't work correctly. The other problem could be that you did run this code, and it worked, but the attribute wasn't made visible. In this case you can safely run the code again but specify 'visible' => true as one of the array parameters.

Very nice tutorial ! Its working well.

Great post Denis !

I installed the module by following your instructions step by step and it works well.

As Tyler suggested, I tried to insert custom field data in the DB from the onepage checkout by editing the billing.phtml file.
But I had no success although the module was working when creating accounts through the register page.

Does anyone have an idea on the way to solve this problem ?

Thanks a lot.

Fred

As I replied to Tyler's message, it takes more than just editing the phtml file to get the attributes to save on a customer. This is probably worthy of a blog post on its own, which I may be able to address in the future.

Hi Denis,

This works perfect. Thank you.

I wanted to make this field to be saved from one page checkout.
How do I do it?

Can you be so kind to be more clear at point 4?

I got setup.php with the followinf line:

***************************************************************************************************
class Mage_Customer_Model_Entity_Setup extends Mage_Eav_Model_Entity_Setup
{

public function getDefaultEntities()
{
return array(
'customer' => array(
'entity_model' =>'customer/customer',
'table' => 'customer/entity',
'increment_model' => 'eav/entity_increment_numeric',
'increment_per_store' => false,
'attributes' => array(......................................................................
***************************************************************************************************

You say "Put the method inside the following class:", so means at the end of the file to do the following:

****************************************************************************************************
class Fontis_Customer_Model_Entity_Setup extends Mage_Customer_Model_Entity_Setup
{

public function getDefaultEntities()
{
return array(

'flavour' => array(
'label' => 'Ice Cream Flavour',
'visible' => true,
'required' => true,
),

)
}

}

*********************************************************************************************

The comma after the first closing ) should be cancelled?

Thank you

The code should work as is. The comma isn't required and it would be a bit neater to remove it, but functionally it shouldn't make any difference.

What about a checkbox input ? I'm not able to get it to work with a checkbox, I got no errors, but it doesn't save the input. I just changed 'input' => 'text' to "checkbox" and changed the output html in both register and edit forms.

I can't find any example of checkbox in the eav_attribute table.

Any help please ?

To complete what I was saying :

Actually, it's when I want to uncheck the input (which isn't posted in the form, only posted when
checked), that way, the value for the user is always "on".

L-O

From the backend, no problem to save content of new attribute but impossible to save it from the front-end ?

Have you any idea ?

(I got attribute manager extension installed.)

THanks

Great post! Is there a way to add this as a drop down menu?

So how do we add a dropdown menu (select box) to the create an account form/process?

Say on my Create an account I want to show a select with 11 possible job types and have the user indicate their job category as part of the signup process. Using the process above, how do values for the options and the text for the options get created?

What about checkboxes? Values and text?

What you wrote above is incredibly helpful! Thank you. It would be complete by showing examples of how to create some other form elements.

Unfortunately, this is a bit more complicated than it looks, and in fact using other form elements for attributes on customers (and products and most anything else) probably warrants its own blog post that hopefully I can address in the future. In brief, to create a select box for a customer attribute you need to use the source_model attribute of the attribute. Have a look at the way the attribute is handled for the is_anchor attribute on categories. You can use the same source model for any checkboxes that you want as well. The source file for the attribute is app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Boolean.php. There are also customer specific source models in the app/code/core/Mage/Customer/Model/Customer/Attribute/Source/ and the app/code/core/Mage/Customer/Model/Entity/Address/Attribute/Source/ directories. You also might need to make some backend models so that the attributes display correctly in the backend. The backend models are displayed in the Backend directories 'next to' the Source directories.

Found the problem and why it probably only affects certain users. If you go here:

http://www.magentocommerce.com/wiki/custom_module_with_custom_database_t...

It says this in part of the document:

Note2: It seems that currently, if you use upper case characters in module names (expecting to show word starts since neither - nor _ is allowed)... the install will fail or rather the module will not work. Suggestion: use a single upper case character, at the beginning of the name.

The problem I had was that I named the module like ModuleName_WholesaleCustomer

When I did that everything appeared to work, but on the frontend it was not taking in the new data that people were filling out. So by changing the above to:

Modulename_Wholesalecustomer

and all the values within the files to match the problem is now fixed! Everything works great now! So the just remember that you can only use a capital letter on the first letter, everything else must be lowercase!

Thanks for this article. I used it to add some more checkboxes to customer preferences.
At first it wasn't saving anything and I had to modify the controller (newsletter/controllers/ManageController.php) to save the values. Somewhere I read that you don't have to do this, but I found that I did. It was quite simple in my case, just find the action which is saving, in my case saveAction and set the extra fields.

Mage::getSingleton('customer/session')->getCustomer()
->setStoreId(Mage::app()->getStore()->getId())
->setIsSubscribed((boolean)$this->getRequest()->getParam('is_subscribed', false))
->setNewfield((boolean)$this->getRequest()->getParam('newfield', false))
->save();

Of course you have to point the form action to the new controller too ;)

Hi,
alandubs, did you do that to add the ability to let new customer subscribe to more than one newsletter? If yes, could you give more details on how you did it?

Thanks
Didier

Wow - great post! This works flawless (so far, at least).

Two questions:

1) Is there any way to include a product drop-down (for Product Registration purposes)

2) Is there a way to have a date/calendar pop-up similar to the date selection feature in the Admin section, so users can select a date and have it stored in the database?

Thank you!

Brady

As I replied to Brian, to use a drop down for your attributes, you need to use the source_model attribute of your attributes to be able to use a drop down. For your particular case (having the drop down populated by products) I'd make the attributes type int which will store the products id. Then, in the custom source model you create use a collection to select the list of products (You might find Chris' post on the efficient use of collections helpful for this) and iterate across the list of products producing an array that is in the same format as the one in the Boolean source file.

As for the calendar, the best thing I can suggest is that you look at how the calendar has been achieved in other frontend areas. Briefly, you need to include the calendar block into your page (which has a number of definitions in it), include the calendar JavaScript and the relevant skin CSS files into the page, create an image for the user to click on and then in JavaScript instantiate the calendar.

Another question - Would would be the "proper" way to adjust the input-box widths on the registration form? Apply a CSS class and set the width in CSS?

Thanks!

Applying a CSS class and setting the width using CSS is the commonly accepted method.

Same problem for me... somene can help us?
I can save new attribute on admin, not in register form...

This post deals with a thing that didn't get any real answer in this thread : using checkboxes for custom registration fields

This will display a checkbox on the frontend and a "Yes/No" select in the backend.
Let's say we want to know if the customer likes (or not) the chocolate flavour.

At step 3.
Add

<chocolate><create>1</create><update>1</update></chocolate>

At step 4.
Add

'chocolate' => array(
'label' => 'Do you like chocolate',
'type' => 'int',
'input' => 'select',
'visible' => true,
'required' => true, // or false
'source' => 'eav/entity_attribute_source_boolean',
),

At step 5.
Add

$setup->addAttribute('customer', 'chocolate', array(
'label' => 'Do you like chocolate',
'type' => 'int',
'input' => 'select',
'visible' => true,
'required' => true, // or false
'position' => 1,
'source' => 'eav/entity_attribute_source_boolean',
));

At step 6.

For register.phtml, add

<div class="input-box">
<label for="chocolate"><?php echo $this->__('Do you like chocoalte') ?><span class="required">*</span></label><br />
<input type="checkbox" name="chocolate" id="chocolate" value="1" title="<?php echo $this->__('Do you like chocolate') ?>" class="class="required-entry checkbox" />
</div>

For edit.phtml, add

<div class="input-box">
<label for="chocolate"><?php echo $this->__('Do you like chocoalte') ?><span class="required">*</span></label><br />
<input type="checkbox" name="chocolate" id="chocolate" value="1" title="<?php echo $this->__('Do you like chocolate') ?>" <?php if($this->getCustomer()->getChocolate()==1): ?> checked="checked"<?php endif; ?> class="class="required-entry checkbox" />
</div>

We also need to modify the AccountController.php file (best practice is to override it to prevent from loosing modification on core updates)

Around line 583, change

foreach ($fields as $code=>$node) {
if ($node->is('update') && isset($data[$code])) {
$customer->setData($code, $data[$code]);
}
}
 
$errors = $customer->validate();

to

foreach ($fields as $code=>$node) {
if ($node->is('update') && isset($data[$code])) {
$customer->setData($code, $data[$code]);
}
}
 
$customer->setChocolate((boolean)$this->getRequest()->getParam('chocolate', false));
 
$errors = $customer->validate();

An we're done. Tested on Magento 1.4.0.1.