5

I'm trying to set the default value for a country in an addressfield (caught in hook_form_alter) and ensure that the associated fields (administrative area, etc.) are refreshed properly. How can I do this?

3

Finally figured it out. The idea is to emulate the code in addressfield_field_widget_form(). Here's the recipe:

 /**
  * Implements hook_form_FORMID_alter() for `commerce_checkout_form_checkout`.
  */
 function mymodule_form_commerce_checkout_form_checkout_alter(&$form, &$form_state) {
    // Get the addressfield element we're modifying.
    // Adapted from addressfield.module/addressfield_field_widget_form()
    $element =& $form['customer_profile_shipping']['commerce_customer_address'][LANGUAGE_NONE][0];
    $element_key = $element['element_key']['#value'];
    $field = $form_state['field']['commerce_customer_address'][LANGUAGE_NONE]['field'];
    $instance = $form_state['field']['commerce_customer_address'][LANGUAGE_NONE]['instance'];
    $delta = $element['#delta'];
    $settings = $instance['widget']['settings'];
    $langcode = $element['#language'];
    $items = array($delta => $element['#address']);

    // Get the default address used to build the widget form elements, looking
    // first in the form state, then in the stored value for the field, and then
    // in the default values of the instance.
    $address = array();

    if (!empty($form_state['addressfield'][$element_key])) {
      // Use the value from the form_state if available.
      $address = $form_state['addressfield'][$element_key];
    }
    elseif (!empty($items[$delta]['country'])) {
      // Else use the saved value for the field.
      $address = $items[$delta];
    }
    else {
      // Otherwise use the instance default.
      $address = (array) $instance['default_value'][0];
    }

    // Merge in default values to provide a value for every expected array key.
    $countries = _addressfield_country_options_list($field, $instance);
    $address += addressfield_default_values($countries);

    // Set our default country.
    // THAT'S THE PAYLOAD!
    if ($address['country'] != $my_default_country) {
      $address['country'] = $my_default_country;
      $address['administrative_area'] = '';
    }

    // Add the form elements for the standard widget, which includes a country
    // select list at the top that reloads the available address elements when the
    // country is changed.
    if ($instance['widget']['type'] == 'addressfield_standard') {
      // Generate the address form.
      $context = array(
        'mode' => 'form',
        'field' => $field,
        'instance' => $instance,
        'langcode' => $langcode,
        'delta' => $delta,
      );
      $element = array_merge($element, addressfield_generate($address, $settings['format_handlers'], $context));

      // Mark the form element as required if necessary.
      $element['#required'] = $delta == 0 && $instance['required'];
    }
  }
  • 1
    As you can see, it's a lot of code to do what #default_value should be handling :-) – infojunkie Aug 25 '12 at 22:55
  • 3
    Time has passed, and there's now a better way. See: drupal.org/node/2392855, and get the latest version of the addressfield module. – Atomox Sep 8 '16 at 17:15
  • Just to add to this, that issue basically just links to the commit at cgit.drupalcode.org/addressfield/diff/… that provides hook_addressfield_default_values_alter(&$default_values, $context). – Mrweiner Jul 13 '18 at 20:53
1

The problem that I ran into, is that I could set the default values by doing the following...

If making use of the commerce module, you can load the entity, and access the address values using the following code

$address_info_profile = commerce_customer_profile_load($profile_id);
$address_info = $address_info_profile->commerce_customer_address[LANGUAGE_NONE][0];

$address_info now contains all of the default values, with $address_info['country'] containing the country code.

Now, you can set the default values for an addressfield widget using the following code

$form['commerce_customer_address'][LANGUAGE_NONE][0]['#address'] = $address_info;

#address is the important part, as that is where the default values are set.

Another problem arises if you allow for multiple countries. If your websites default country is Canada (CA) as my client is, and your profile is for United States (US), you run into an issue with the widget loading the correct default country, but the 'administrative_area' shows the wrong #title, and the wrong #options. The have it show the States, you have to select a different country, and back to United States.

Furthermore, those requests are handled via #ajax, so you want this to happen only once.

The result I came up with is as follows. I am leaving in the comments to assist others

//verification that we have a default value to work with
if (isset($address_info_profile->commerce_customer_address) &&
    isset($address_info_profile->commerce_customer_address[LANGUAGE_NONE]) &&
    count($address_info_profile->commerce_customer_address[LANGUAGE_NONE]) > 0) {

  //obtain the default address information to use as defaults
  $address_info = $address_info_profile->commerce_customer_address[LANGUAGE_NONE][0];

  //set the default values for the addressfield
  $form['commerce_customer_address'][LANGUAGE_NONE][0]['#address'] = $address_info;

  //verify that this is not an ajax request, otherwise country changes will not be able to change the administrative_area information. 
  //in otherwise, let this section only happen once, and let ajax make changes as expected
  if (!(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')) {

    //obtain the default country code. If the default values country and the servers default country match, no reason to call the bit of code
    $default_country = variable_get('site_default_country', NULL);

    if ($address_info['country'] != $default_country) {
        module_load_include('inc', 'addressfield', 'plugins/format/address');

        //create a new default addressfield to use, which will use the default values country as the default country, as opposed to the servers default
        $default = addressfield_default_values(array($address_info['country']=>$address_info['country']));

        //where all of the data will be stored when function is called
        $format = array();

        //required by addressfield_format_address_generate, and returns the content as rendered
        $context = array('mode' => 'render');
        addressfield_format_address_generate($format, $default, $context);

        //new defaults and administrative_area have been generated. set these values to the already built form
        $form['commerce_customer_address'][LANGUAGE_NONE][0]['locality_block']['administrative_area'] = $format['locality_block']['administrative_area'];
    }
  }
}

Hopefully that helps you out if you are having similar problems getting default values to load.

Also, this was done using field attach form, to attach the commerce_customer_profile.

$type = 'billing';
$profile = commerce_customer_profile_new($type, $user->uid);
field_attach_form('commerce_customer_profile', $profile, $form, $form_state);
  • +1,000,000 for $form['commerce_customer_address'][LANGUAGE_NONE][0]['#address'] = $address_info; #address is the important part, as that is where the default values are set. – wxactly Aug 26 '15 at 22:27
0

Load the default value in the '#default_value' as you need. Alsso need to populate the related drop-down options using your '#default_value' and put it in the related drop downs '#options'. For changing the values on select of parent dropdown you need to do an ajax call.

  • Thank but that's not what I'm looking for: * I need a function that automatically generates the options of the related drop-downs, using the code from addressfield/plugins/format/address.inc. The code should also update the labels of these drop-downs based on the country. * Also, ajax does not play a role here because we haven't even rendered the page for the first time: we're still building the page. – infojunkie Aug 25 '12 at 8:10
  • Maybe I should explicitly say that I know how to set the default value on the individual country field. But the related drop-downs are not updated when I do so. – infojunkie Aug 25 '12 at 8:12
0

I know this is old, but this is one of the few answers I've found out there. I wasn't able to get the default code working, so I modified it a little in case anyone else needs it.

/**
 * Implements hook_form_FORMID_alter() for `commerce_checkout_form_checkout`.
 */
function mymodule_form_commerce_checkout_form_checkout_alter(&$form, &$form_state) {
  // Get the addressfield element we're modifying.
  // Adapted from addressfield.module/addressfield_field_widget_form()
  $element =& $form['customer_profile_shipping']['commerce_customer_address'][LANGUAGE_NONE][0];
  $element_key = $element['element_key']['#value'];
  $field = $form_state['field']['commerce_customer_address'][LANGUAGE_NONE]['field'];
  $instance = $form_state['field']['commerce_customer_address'][LANGUAGE_NONE]['instance'];
  $delta = $element['#delta'];
  $settings = $instance['widget']['settings'];
  $langcode = $element['#language'];
  $items = array($delta => $element['#address']);

  // Get the default address used to build the widget form elements, looking
  // first in the form state, then in the stored value for the field, and then
  // in the default values of the instance.
  $address = array();

  if (!empty($form_state['addressfield'][$element_key])) {
    // Use the value from the form_state if available.
    $address = $form_state['addressfield'][$element_key];
  }
  elseif (!empty($items[$delta]['country'])) {
    // Else use the saved value for the field.
    $address = $items[$delta];
  }
  else {
    // Otherwise use the instance default.
    $address = (array) $instance['default_value'][0];
  }

  // Set the default country here, based on the available values
  // provided by the addressfield module.
  // THAT'S THE PAYLOAD!
  if ($address['country'] != 'SOME_COUNTRY_CODE') {
    $address['country'] = 'SOME_COUNTRY_CODE';
    $address['administrative_area'] = '';
  }

  // After setting the value above, merge the default values
  // to provide a value for every expected array key.
  $countries = _addressfield_country_options_list($field, $instance);
  $address += addressfield_default_values($countries);


  // Add the form elements for the standard widget, which includes a country
  // select list at the top that reloads the available address elements when the
  // country is changed.
  if ($instance['widget']['type'] == 'addressfield_standard') {
    // Generate the address form.
    $context = array(
      'mode' => 'form',
      'field' => $field,
      'instance' => $instance,
      'langcode' => $langcode,
      'delta' => $delta,
    );
    $element = array_merge($element, addressfield_generate($address, $settings['format_handlers'], $context));

    // Mark the form element as required if necessary.
    $element['#required'] = $delta == 0 && $instance['required'];
  }
}

Mostly I just moved the code that actually modifies the country and placed it above the array merge that changes those fields to something that the form api can work with.

0

I found that implementing function hook_field_widget_addressfield_standard_form_alter(&$element, &$form_state, $context) then changing $element['#address'] items did the trick. Changing the city: $element['#address']['locality'] = 'Zurich';, for example. I needed this method since the suggested address data needed to come from the $form_state, which wasn't possible with the default address hook provided by the module.

0

Use hook_addressfield_default_values_alter():

function YOUR_MODULE_addressfield_default_values_alter(&$default_values, $context) {
    // Set default to Belgium.
    if (empty($default_values['country'])) {
        $default_values['country'] = 'BE';
    }
}
0
 $form['field_supplier_address']['widget'][0]['address']['#default_value']['country_code'] = "IL";

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.