Storing Address information in User Profile

Posts: 41
Joined: 09/07/2007

I wanted to ask a few questions before I go too far down the wrong path. I have a few reasons that I need users address info outside of the online store. Specifically, we currently have a product registration system on our site that lets customers who previously purchased our products register them and get a free sheath to put the product in and protect it. We sell our products at several brick and mortar stores, so not everyone that registers a product will order it from us. We also use the contact information for people who register with us or people who purchase from our online store to send out promotional information if they desire it. Our current system stores all of the contact information in a user profile. Ubercart store this information for each order and not in a user profile.

I would like to have contact info stored in the users profile. This will allow someone to create a profile when they register their products, but be able to login later and purchase new products from our online store and have their contact info that they already entered be displayed. This would also work the other way, so when they buy a product, they can later register it by logging in with the same profile and their address info is already there because they have placed an order with us. It would also allow people to login and edit their contact info if it changes.

I understand the benefits for how Ubercart stores this information now. My question is, how hard would it be for me to change the checkout module to store the contact info in a different location in the database, but still work with Ubercart? I've done some research into how to store this info in user profiles, but what would be needed to get that to work with Ubercart?

I am currently looking into these modules for storing the profile info:
http://drupal.org/project/nf_registration_mod
http://drupal.org/project/nodeprofile
http://drupal.org/project/pageroute

1. Can this be done? (I guess anything CAN with enough work, but is it realistic)
2. What issues do you foresee with Ubercart?
3. Would other people find this useful?
4. If I can't figure out how to do it, could I pay someone to get it done?
5. Do you think a custom synchronization function could be used to populate one table if the other tables info already exists?

Thanks for your incite.

Posts: 3898
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

Don't have all the answers for you at this moment... I think I've been tossing ideas around in my head for what to do with addresses for a couple months now. Eye-wink One thing I would say is that it would be fairly trivial for a module to define custom checkout panes that load and store data for addresses in a different DB table and uses hook_order() to populate the order object with that data later on.

I guess ideally addresses would be stored in their own table and the individual order table, so you could alter an order's address however you need without affecting the other addresses but could also populate address fields from the stored address list. But like I said, haven't moved passed the brainstorming phase to make up a game plan for the changes.

Posts: 41
Joined: 09/07/2007

I did some digging into the Ubercart modules today and have a better understanding of how address data is stored and how it could work with user profiles.

My Observations (correct me if I'm wrong)
1. There is currently only one place that pulls old address data for use in the future. This is in the /cart/checkout form under the "Saved Addresses:" field. It does a database query on "uc_orders" for each unique address that a user has used in the past. That query is in the uc_get_addresses() function in uc_store.module.

2. Editing an order pulls the address info from the table "uc_orders" with the function uc_order_load() in uc_order.module.

My Proposal
1. I would like to create a module that adds a table "uc_user_addresses" to the database and contains a function uc_add_user_address() that will save an address to this table when an order is submitted.

2. The "uc_user_addresses" table would contain the same address info like "delivery_" and "billing_". It could also contain a "save_as" column that could be used to save multiple addressed like "Home", "Work", etc.

3. The module would only allow a single address per "save_as" type per user. Currently with the uc_get_addresses() function, you can have multiple addresses that could refer to the same location if they submit a second order and type in their address with a slight variation. For example, if it was "123 55th Ter." and they changed it to "123 55th Terr" when they submitted a second order.

4. In the check out form, add a "Save Address As:" drop down with a few "save_as" types.

5. Change the uc_get_addresses() function to use an if() statement to pull addressed from the new "uc_user_addresses" table if it exists, else, do the current query on the "uc_orders" table to provide backwards compatibility. This should allow the "Saved Addresses:" drop down on the /cart/checkout form to populate with either the new "uc_user_addresses" data or the "uc_orders" history data.

6. Add "Temporary Change" and "Permanent Change" radios to the /admin/store/orders/1/edit form. "Temporary Change" only updates the "uc_orders" table. "Permanent Change" updates both the "uc_orders" table and the "uc_user_addresses" table.

What do you think about this? It doesn't seem as hard as I thought it would be now that I've got it outlined. Although, I've never created a module before, but what better time to start then now?

Posts: 3898
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

Quick response as I head to bed on only two points (will have to think more about it all). Instead of user_addresses, I'd look for either customer_addresses or even just addresses. It may be best to just call it uc_addresses and don't force it to be connected to a user account so you can also store shipping and store addresses from other modules.

Also, I think I'd call the "save as" column "label" instead... that way you can ask them to specify a label which makes a little more logical sense.

Other than that, I think this is a good step in the right direction and am happy to have some other brain power helping me out. Smiling

Posts: 41
Joined: 09/07/2007

I've started to write this module and have run into a issue with the database table that I wanted to make sure was setup properly.

I've decided to create a table called uc_addresses like this:

<?php
function uc_addresses_install(){
 
drupal_set_message(t('Beginning installation of uc_addresses module.'));
  switch (
$GLOBALS['db_type']) {
    case
'mysql':
    case
'mysqli':
     
db_query("CREATE TABLE {uc_addresses} (
        uid mediumint(9) NOT NULL,
        location varchar(32) NOT NULL,
        first_name varchar(32) NOT NULL,
        last_name varchar(32) NOT NULL,
        phone varchar(32) NOT NULL,
        company varchar(64) NOT NULL,
        street1 varchar(64) NOT NULL,
        street2 varchar(64) NOT NULL,
        city varchar(32) NOT NULL,
        zone mediumint(9) NOT NULL,
        postal_code varchar(10) NOT NULL,
        country mediumint(9) NOT NULL,
        created int(11) NOT NULL,
        modified int(11) NOT NULL,
        PRIMARY KEY (uid)
        KEY location (location)
      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "
);
     
$success = TRUE;
      break;
    case
'pgsql':
     
db_query("CREATE TABLE {uc_orders} (
        uid integer NOT NULL default 0,
        location varchar(32) NOT NULL default '',
        first_name varchar(32) NOT NULL default '',
        last_name varchar(32) NOT NULL default '',
        phone varchar(32) NOT NULL default '',
        company varchar(64) NOT NULL default '',
        street1 varchar(64) NOT NULL default '',
        street2 varchar(64) NOT NULL default '',
        city varchar(32) NOT NULL default '',
        zone integer NOT NULL defualt 0,
        postal_code varchar(10) NOT NULL default '',
        country integer NOT NULL default 0,
        created integer NOT NULL default 0,
        modified integer NOT NULL default 0,
        PRIMARY KEY (uid),
        KEY location (location)
      );"
);
     
$success = TRUE;
      break;
    default:
     
drupal_set_message(t('Unsupported database.'));
  }
  if(
$success){
   
drupal_set_message(t('The uc_addresses table was installed successfully.'));
  }
  else{
   
drupal_set_message(t('The installation of the uc_addresses module was unsuccessful.'),'error');
  }
}
?>

I think it is best to store each address in a separate row and not have two in a row like you have in the uc_orders table (delivery_field & billing_field). I have a column called "location" which will be used as the "label" or "save_as" mentioned above. I was looking at how Newegg.com has their user profile information setup and found out that you can have multiple addresses with the same label. You can create two addresses with the label "Home". The way I want to setup this module, the "location" will have to be unique. You cannot have more than one "Home", but you could have "Home2".

1. Do you think that it will be fine to force this field to be unique with form validation?

2. If you disagree, what would you use as a secondary KEY in the table so you will be able to select the proper row for a user which has multiple locations (Home, Home, Work, School, Billing, etc.)?

3. Newegg only allows one billing address per profile to be stored. I think this is fine. What do you think?

4. You mentioned above that this shouldn't be tied to a user so you can store address information from different modules. I'm having a hard time seeing any situations where there would not be a "uid" associated with an address. Can you elaborate on this?

Posts: 3898
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

1/2. I'm all for unique labels and forcing it in the form's validate handler. Make sure it's just checking against the other addresses for that user and not all the stored labels in general.

3. Regarding 1 billing address, I'm not sure. The thing is, someone may move or purchase using different credit cards that have different billing addresses. I guess I don't see a benefit in restricting it to just one.

4. What I'm referring to here is a fringe case. Normally, addresses will be entered by users for use on user accounts. But perhaps I want to save the store's mailing address to the table. I don't want that showing up in a user list, so I'd keep uid set to 0. For this reason, I still like the term "label" better than "location" which seems to narrow down the description of that column a little too much. In the end, it's just semantics, but you may need to store addresses for your store's mailing address, default pickup location for UPS, returned goods address, etc. and maybe these are all physically the same location but with different street2 lines for "Attn: Returns", "Attn: Billing", etc.

Posts: 41
Joined: 09/07/2007

3. I think the way Newegg handles it is to allow only one billing address to be stored, but you can always enter a different billing address when you check out, you just can't save it as a different one. As far as moving, you can edit the billing address, just not have more than one. I guess it doesn't matter either way since the code to have multiples will be there. I'm just assuming that there is a good reason that Newegg only allows one billing address.

4. If you wanted to store the store's addresses (ok that's confusing, let's call it store the business's address) you could use the administrator's account. Since you will be able to store multiple addresses and the administer will always be user 1 (I think it works that way), this shouldn't be a problem. Do you think that will work?

Posts: 41
Joined: 09/07/2007

I've finished creating this module. See the readme.txt for details. It will allow you to add addresses to a table from /user/'uid'. I would love to make a few minor changes to core to allow this module to be used during the order submit process.

Let me know what you think. There are a few minor //TODO's that could be completed. Most of which I don't know how to do yet. I based most of this off of uc_order and uc_cart. I don't know how to work with $_SESSION, so some of that might need to be added or removed.

AttachmentSize
uc_addresses.zip10.78 KB
Posts: 12
Joined: 11/30/2007
Bug Finder

This is the same issue I was just looking into writing my own module to handle. I've tested uc_addresses and it seems to work very well. I hope it will be added into core because this is exactly what I need! Be nice to see it working with the orders page and actually using this during the checkout process.

Posts: 41
Joined: 09/07/2007

I'll be doing some more development on this module soon. If it isn't added to core, I'll need to use hooks to override the checkout address entry forms.

I'm busy at the moment working on deploying an ERP solution. Currently looking at www.postbooks.org. If it works out, I should be able to finish this module sometime in January 08.

Posts: 10
Joined: 12/13/2007

Hi

There's a typo in the _install function; the database name is different for mysql and pgsql

Cheers

Posts: 4
Joined: 01/22/2008

Why are we using "mediumint(9)" for the uid when in Drupal's user table it's defined as "int(10) unsigned"? Does the fact that we're not doing any actual math on these values mean that they're functionally equivalent? Which should I use in these situations?

Posts: 30
Joined: 09/19/2007

I have modified my uc_store.module so that I can use the uc_addresses module during checkout:
Find the function in uc_store called uc_get_address and add these lines below after the line that looks like this:

Look for this line

<?php
$addresses
= array();
?>

Lines to add between the line above and the 'while' line

<?php
if($profile_addresses = uc_addresses_get_address($uid, NULL, 0)){
  foreach(
$profile_addresses AS $index => $profile_address){
   
$addresses[] = (array) $profile_address;
  }
  return
$addresses;
}
?>

This way, the addresses from the users profile that they have created will show in the checkout form. If there are none, the addresses from previous orders will show instead.

Cheers,
Gord.

Posts: 41
Joined: 09/07/2007

Thanks!

I hope you can get some use out of this. I've had to push back my time frame on this, but I will start work on finishing this module soon. I'll roll the above changes in and get the rest of the hooks setup.

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

What kind of progress has been made on this? What are the outstanding tasks to complete the integration?

Posts: 41
Joined: 09/07/2007

I need to write the hooks that will override the address forms for the shopping cart check out screens. Their needs to be an option to save the address at check out if it is new and also select past used addresses from a drop down. I have the forms setup and the database connections made, so the only major thing to do is the hooks. There has been some talk about normalizing the addresses so the name is stored in a different table. I think that would be a good idea, but it would take a little bit more work.

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

Sounds like you're almost there. One of our client is looking for this functionality and is shocked it's not part of Ubercart already. I tried to explain that it might be better to just leave things as they are but he's scared of being ripped off. Also, he needs to be able to only allow UK Mainland customers to checkout and all others to complete the order process without making payment. I guess this is an important step towards that otherwise the system would not know where the customer is based before checkout.

Do you have an idea when you'll get chance to look into the hooks? Do you need any help from me?

Posts: 41
Joined: 09/07/2007

If you read through this post, you'll see that I keep pushing this back. I would say end of March at the moment. I WILL finish this because we NEED the functionality on our site. So if you can wait, I'll make sure and let you know when it's done or when I need some beta testing. In the meantime, if you want to do some work on it, feel free to contribute what you can.

Thanks!

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

Hi bendiy, Can you please provide what you have so I can have a look and see if I can integrate it? That is assuming you changed anything other than that mentioned above.

Posts: 41
Joined: 09/07/2007

It's all here in this post. I haven't done any code editing since the post. Some people above have pointed out above a few typo errors that still need to be fixed.

You might want to take a look at my post here if you haven't already:
http://www.ubercart.org/forum/development/3512/integration_postbooks_erp...

I want to "Normalize" the tables better than they are now. However, you might be able to get by with the functionality that is setup now and just adding the hook in to override the checkout forms.

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

Hey man, just had a look into this and it's setup a little differently to what I was hoping. Let me just clarify this, the user has to register first and then manually manage their addresses.. they are not forced to enter an address when registering?

I'm going to have a look into how to intercept the user register process now.

Posts: 41
Joined: 09/07/2007

You are correct. That's another thing that needs to be done. There are a few modules that I was looking at that could automate the flow that a user would have to follow when registering. This module (uc_addresses) has the Nodes that the flow would force them to go through at registration.

Pageroute:
http://drupal.org/project/pageroute

Workflow: (i'm not sure if it will do this or not, but Ubercart already uses it for somethings)
http://drupal.org/project/workflow

It depends on how your site will work, but I would like to see uc_addresses be used to store and retrieve addresses at checkout. If a user is already registered, they can add an address when they check out, or use an address they have previously used. Just because a user registers on a site doesn't mean that they are going to use the store. Again, this depends on the type of site. If you have a forum, reviews or a wiki, they might just register for that. Therefore the user would not need to enter address info (Unless you want to force that for CRM use). I think it would be best to only capture address info at user registration if they are actually checking out and you are forcing them to login/create account.

I think you can achieve what it sounds like you want to do with the Pageroute module. I hope this helps.

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

Thanks for that, bendiy.

The site I'm working on will need to use zone information to show different payment modules.

I've hacked the uc_addresses.module a bit and changed this function:

<?php
/**
* Implementation of hook_user().
*/
function uc_addresses_user($op, &$edit, &$account, $category = null){
  global
$user;
  switch (
$op){
    case
'view':
      if (
user_access('edit and view addresses') || $user->uid == $account->uid) {
       
$link = l('here', 'user/'. $account->uid .'/addresses');
       
$items = array();
       
$items['addresses'] = array('title' => t('Manage Addresses'),
         
'value' => 'Click ' . $link . ' to manage your addresses.',
         
'class' => 'member',
        );
        return array(
t('Addresses') => $items);
      }
      else {
        return
NULL;
      }
    case
'register':
     
$form['address'] = array(
       
'#type' => 'fieldset',
       
'#title' => t("Address"),
       
'#collapsible' => TRUE,
       
'#collapsed' => FALSE,
      );
     
$form['address']['label'] = uc_textfield('Save Address As', $arg1->label, TRUE);
      if (
uc_address_field_enabled('first_name')) {
       
$form['address']['first_name'] = uc_textfield(uc_get_field_name('first_name'), $arg1->first_name, uc_address_field_required('first_name'));
      }
      if (
uc_address_field_enabled('last_name')) {
       
$form['address']['last_name'] = uc_textfield(uc_get_field_name('last_name'), $arg1->last_name, uc_address_field_required('last_name'));
      }
      if (
uc_address_field_enabled('phone')) {
       
$form['address']['phone'] = uc_textfield(uc_get_field_name('phone'), $arg1->phone, uc_address_field_required('phone'), NULL, 32, 16);
      }
      if (
uc_address_field_enabled('company')) {
       
$form['address']['company'] = uc_textfield(uc_get_field_name('company'), $arg1->company, uc_address_field_required('company'), NULL, 64);
      }
      if (
uc_address_field_enabled('street1')) {
       
$form['address']['street1'] = uc_textfield(uc_get_field_name('street1'), $arg1->street1, uc_address_field_required('street1'), NULL, 64);
      }
      if (
uc_address_field_enabled('street2')) {
       
$form['address']['street2'] = uc_textfield(uc_get_field_name('street2'), $arg1->street2, uc_address_field_required('street2'), NULL, 64);
      }
      if (
uc_address_field_enabled('city')) {
       
$form['address']['city'] = uc_textfield(uc_get_field_name('city'), $arg1->city, uc_address_field_required('city'));
      }
      if (
uc_address_field_enabled('country')) {
       
$form['address']['country'] = uc_country_select(uc_get_field_name('country'), $arg1->country, NULL, 'name', uc_address_field_required('country'));
      }
      if (
uc_address_field_enabled('zone')) {
        if (isset(
$_POST['country'])) {
         
$country_id = intval($_POST['country']);
        }
        else {
         
$country_id = $arg1->country;
        }
       
$form['address']['zone'] = uc_zone_select(uc_get_field_name('zone'), $arg1->zone, NULL, $country_id, 'name', uc_address_field_required('zone'));
      }
      if (
uc_address_field_enabled('postal_code')) {
       
$form['address']['postal_code'] = uc_textfield(uc_get_field_name('postal_code'), $arg1->postal_code, uc_address_field_required('postal_code'), NULL, 10, 10);
      }
      return
$form; // address form isn't themed
   
case 'insert':
     
// insert address to database
     
return;
  }
}
?>

This makes the address form show up on user/register and the fields are validated as usual. How would be the correct way to insert the address into the uc_addresses table in the database?

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

Okay, now I have the address saved to the uc_addresses table in the database. Also, I have added the delete query to delete the addresses associated with a uid. Posting here to get feedback. Gong to see if I can implement the saved addresses in checkout form bit posted above next.

<?php
/**
* Implementation of hook_user().
*/
function uc_addresses_user($op, &$edit, &$account, $category = null){
  global
$user;
  switch (
$op){
    case
'view':
      if (
user_access('edit and view addresses') || $user->uid == $account->uid) {
       
$link = l('here', 'user/'. $account->uid .'/addresses');
       
$items = array();
       
$items['addresses'] = array('title' => t('Manage Addresses'),
         
'value' => 'Click ' . $link . ' to manage your addresses.',
         
'class' => 'member',
        );
        return array(
t('Addresses') => $items);
      }
      else {
        return
NULL;
      }
    case
'register':
     
$form['address'] = array(
       
'#type' => 'fieldset',
       
'#title' => t("Address"),
       
'#collapsible' => TRUE,
       
'#collapsed' => FALSE,
      );
     
$form['address']['label'] = uc_textfield('Save Address As', $arg1->label, TRUE);
      if (
uc_address_field_enabled('first_name')) {
       
$form['address']['first_name'] = uc_textfield(uc_get_field_name('first_name'), $arg1->first_name, uc_address_field_required('first_name'));
      }
      if (
uc_address_field_enabled('last_name')) {
       
$form['address']['last_name'] = uc_textfield(uc_get_field_name('last_name'), $arg1->last_name, uc_address_field_required('last_name'));
      }
      if (
uc_address_field_enabled('phone')) {
       
$form['address']['phone'] = uc_textfield(uc_get_field_name('phone'), $arg1->phone, uc_address_field_required('phone'), NULL, 32, 16);
      }
      if (
uc_address_field_enabled('company')) {
       
$form['address']['company'] = uc_textfield(uc_get_field_name('company'), $arg1->company, uc_address_field_required('company'), NULL, 64);
      }
      if (
uc_address_field_enabled('street1')) {
       
$form['address']['street1'] = uc_textfield(uc_get_field_name('street1'), $arg1->street1, uc_address_field_required('street1'), NULL, 64);
      }
      if (
uc_address_field_enabled('street2')) {
       
$form['address']['street2'] = uc_textfield(uc_get_field_name('street2'), $arg1->street2, uc_address_field_required('street2'), NULL, 64);
      }
      if (
uc_address_field_enabled('city')) {
       
$form['address']['city'] = uc_textfield(uc_get_field_name('city'), $arg1->city, uc_address_field_required('city'));
      }
      if (
uc_address_field_enabled('country')) {
       
$form['address']['country'] = uc_country_select(uc_get_field_name('country'), $arg1->country, NULL, 'name', uc_address_field_required('country'));
      }
      if (
uc_address_field_enabled('zone')) {
        if (isset(
$_POST['country'])) {
         
$country_id = intval($_POST['country']);
        }
        else {
         
$country_id = $arg1->country;
        }
       
$form['address']['zone'] = uc_zone_select(uc_get_field_name('zone'), $arg1->zone, NULL, $country_id, 'name', uc_address_field_required('zone'));
      }
      if (
uc_address_field_enabled('postal_code')) {
       
$form['address']['postal_code'] = uc_textfield(uc_get_field_name('postal_code'), $arg1->postal_code, uc_address_field_required('postal_code'), NULL, 10, 10);
      }
      return
$form; // address form isn't themed
   
case 'insert':
     
db_query("INSERT INTO {uc_addresses} (uid, label, first_name, last_name, "
           
."phone, company, street1, street2, city, zone, postal_code, country, "
           
."created, modified) VALUES (%d, '%s', "
             
."'%s', '%s', '%s', "
             
."'%s', '%s', '%s', "
             
."'%s', %d, '%s', %d, "
             
."%d, %d)", $edit['uid'], $edit['label'],
              
$edit['first_name'], $edit['last_name'], $edit['phone'],
              
$edit['company'], $edit['street1'], $edit['street2'],
              
$edit['city'], $edit['zone'], $edit['postal_code'],
               ((
is_null($edit['country']) || $edit['country'] == 0) ? variable_get('uc_store_country', 840) : $edit['country']),
              
time(), time());
      return;
    case
'delete':
     
db_query("DELETE FROM {uc_addresses} where uid = %d", $account->uid);
      return;
  }
}
?>

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

The temporary solution posted by Gord above works great. At first I thought it would be better to return the label for the address as the option in the list, but I feel it's more valuable to a user to see the first line of the saved address.

There of course is a much more important negative to this and it's that I now have a hacked Ubercart. Would I need to use hook_form_alter to inject the option to avoid hacking Ubercart or is this something that could be changed in uc_store.module? I think this saved addresses functionality is awesome and since a lot of clients are used to the way osCommerce works they expect this kind of behaviour (even though I do try to push anonymous checkout to them).

Someone please chime in and stop me talking to myself!

Posts: 118
Joined: 12/28/2007
Uber DonorBug Finder

Sorry I'm not a programmer to be able to help in coding, but just wanted to let you know I'm also looking forward to see the address-book functionality live in Ubercart (I'm targeting a Christmas gift shop)

Keep up the great work!

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

HI Abilnet, thanks for at least keeping me company!

Posts: 41
Joined: 09/07/2007

My only critique is that you have added code from other parts of the module to the uc_addresses_user function. I'm not a programmer by trade, but I think the concept of Object Oriented Programing is to not reuse code, but to reference/use one function over and over to do what you need.

I think you can get your form for registration by using the uc_addresses_pane_address() function in the uc_addresses_address_pane.inc file. If you added another "case" to that function for 'register' (or just use the 'new' case) and added a hook_menu() to the uc_addresses_menu() function for the path that a user registers at, I think it would work.

You can also perform the insert into the database with the uc_addresses_add_address function near the bottom of uc_addresses.module.

It just makes it easier to maintain in the future.

I'm not sure how to override the forms at check out without editing the Core Ubercart code. That was my next step for development on this module. I know it can be done without making changes. There is a hook that should work, I just haven't had time yet to figure it out. I believe it is the hook_form_alter() function. You can probably use that to insert your address form at registration also. Just use hook_form_alter() to override the default registration form. You will have to make sure a recreate the existing form though (username, email, password, etc.), but you can copy from the default and just add to it.

I hope this helps. They just released the new version of PostBooks ERP system yesterday, so now I can finish the implementation I've been working on. Hopefully it will go smooth so I can finally finish this module soon.

Cheers!

Posts: 286
Joined: 08/28/2007
Early adopter... addicted to alphas.Not KulvikTheminator

Quote:
My only critique is that you have added code from other parts of the module to the uc_addresses_user function. I'm not a programmer by trade, but I think the concept of Object Oriented Programming is to not reuse code, but to reference/use one function over and over to do what you need.

I understand this and I am striving for it but I'm not a native programmer either, just learning this stuff as I go along. At the moment, with my very basic skills I'm finding that I need different things returned, this could be due to the functions not being reusable enough or my lack of knowledge of how to manipulate things before they go into or after they come out of functions.

Quote:
I think you can get your form for registration by using the uc_addresses_pane_address() function in the uc_addresses_address_pane.inc file. If you added another "case" to that function for 'register' (or just use the 'new' case)...

Yes, this would seem like the best idea. I can add a new case to the switch in uc_addresses_pane_address() but using the 'new' case would be better. My only problem with using the 'new' case was that it returns some weird array which I'm not sure how to feed that into the $form. Should I be manipulating the returned value after it comes out of the function? As you can see I'm still stuck on how to theme the form properly. Insight on this would be appreciated.

Quote:
...and added a hook_menu() to the uc_addresses_menu() function for the path that a user registers at, I think it would work.

I guess this would be to fire off callback when a path is visited. Not quite sure what function but what I have done actually works pretty well, albeit with some pretty major bumps. I'm open to ideas so if you'd like to elaborate on this I would be more than happy to refactor things.

Quote:
You can also perform the insert into the database with the uc_addresses_add_address function near the bottom of uc_addresses.module.

It just makes it easier to maintain in the future.

The uc_addresses_add_address() function seems to take an object and not an array like $edit. If I knew how to convert an array into an object (which seems to be what uc_addresses_add_address() needs) then I would have used that. I just wanted to get this working so I could fine tune it later. Is there a standard way in PHP to convert an array into an object? Would it be better for the uc_addresses_add_address() function to accept both arrays and objects, or just arrays? Your input here would be good.

Quote:
I'm not sure how to override the forms at check out without editing the Core Ubercart code. That was my next step for development on this module. I know it can be done without making changes. There is a hook that should work, I just haven't had time yet to figure it out. I believe it is the hook_form_alter() function..

You are right, it is hook_form_alter(). I have been working on this and have the following:

<?php
function uc_addresses_form_alter($form_id, &$form) {
  global
$user;
  if (
$form_id == 'uc_cart_checkout_form') {
   
$addresses[] = array('Select one...');
   
$addresses[] = uc_get_addresses($user->uid, $type);
    if (
$profile_addresses = uc_addresses_get_address($user->uid, NULL, 0)) {
      foreach (
$profile_addresses as $index => $profile_address) {
       
$addresses[] = $profile_address;
      }
    }
   
dprint_r($addresses);
   
//dprint_r($form['panes']['delivery']['delivery_address_select']['#options']);
    //$form['panes']['delivery']['delivery_address_select']['#options'] = $addresses;
 
}
}
?>

The function uc_get_addresses() returns an array which is exactly what is needed but uc_addresses_get_address() returns an object. This is the same problem as above, I guess I need to loop over the elements in the object and output an array?

Sorry for my newbie questions, I'm working on it though!

Posts: 41
Joined: 09/07/2007

I'm in the same boat as you. This module is my first attempt at any PHP programming. Most of this code is pulled from the other Ubercart modules and it took me quite a while to trace it and figure out how to put it all together. What text editor are you using? I've found Eclipse to be really nice. It allows you to see where a function is used and make tracing code much easier.

Anyways... I'll try and give you an idea of how to do this for a new addrress.

rich wrote:
Yes, this would seem like the best idea. I can add a new case to the switch in uc_addresses_pane_address() but using the 'new' case would be better. My only problem with using the 'new' case was that it returns some weird array which I'm not sure how to feed that into the $form. Should I be manipulating the returned value after it comes out of the function? As you can see I'm still stuck on how to theme the form properly. Insight on this would be appreciated.

To use a form, there are 4 functions that it goes through.

  1. uc_addresses_menu() - Each of the elements (if, else) of this function refer to a path that a node is located at. The arg(1) are the parts of that path that can be passed into the function. You'll notice that there are some arg() that corrispond to 'new', 'view', 'edit'. These affect which functions is called next. You will need to add an element here for your path at registation that will call the next funciton.
  2. uc_addresses_new_address() - Is called by the agr() 'new' being part of the path however, you should use whatever the path is for registration and then send the next function 'new'. You will probably have to change the permissions check so the user doesn't have to be logged in if they are registering. This function outputs the form, but not before calling the next funcion
  3. uc_addr