I'm in a situation where I would like to use the address field module but am having some difficulties. The problem is that the address stored is for magazine subscriptions which need to store a lot of values individually. I believe this has something to do with the ability to look up the address and make sure it exists.

What I need to store is

• Street
• House number
• Something best translated as section, I believe it's used for when you have several apartment buildings using the same house number
• Floor
• Side (If there are several living on the same floor, normal for apartment buildings)
• Apartment (I don't actually know the use case for this one)

I don't know if it would be overkill to create a column for each one, some of these might also be specific for Denmark, as it's only Danish addresses that needs to be stored. I can see two solutions to this, one way would be to actually create columns for all of these use cases (I might not be the only one). That would make it possible to store these things if a field were created for them

Another solution could be to create a "data" or "additional fields" column which could store some serialized data. It would then be up to module creating the fields to make sure the extra fields were serialized. Going this route would also require to exposing the `hook_field_presave` and `hook_field_load` for the addressfield through a custom hook, as it otherwise is very difficult to do this.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sportel’s picture

I would also like the ability to store data in seperate fields. See also http://drupal.org/node/1034158#comment-4374880

googletorp’s picture

I talked with DamZ on IRC about this, and seems like the solution is to use a micro format, e.g. saving the street + house number as

"Street, housenumer" and use php to divide the two. With the data columns already present, this should solve my problem.

davidwhthomas’s picture

subscribing.

davidwhthomas’s picture

I implemented my own addressfield plugin and added new fields, however they're never saved and when the address field is rendered, it shows the form element fields only, with no values. Is there something I'm missing there?

davidwhthomas’s picture

OK, I've made some progress on extending with custom fields using a custom addressfield plugin.

the approach is basically to

  1. Provide an addressfield plugin that extends the format with it's own fields ( set default $format field values from $address variable )
  2. Implement hook_field_attach_presave to store the new field values as a serialized array in the addressfield "data" column.
  3. Implement hook_field_attach_load to unserialize the "data" values into the relevant additional fields.

One problem I've noticed is addressfield needs a patch to support the "render callback" key in the ctools plugin.
At the moment, addressfield only provides a single render callback for all format plugins afaik.
I guess ideally the addressfield module would handle storing and loading the field values to/from the data column in hook_field_presave and hook_field_load - but that's another issue.

Proof of extended field storage and load concept tested and working, may have a code snippet tomorrow.

cheers,

DT

davidwhthomas’s picture

OK, this was a difficult exercise but finally have found success.

Here's the key code snippets posted to help others looking to do the same thing

/**
 * Implements hook_field_attach_presave
 * Store additional address fields serialized in addressfield data column
 */
function example_field_attach_presave($entity_type, $entity) {

  if ($entity_type == 'commerce_customer_profile' && isset($entity->commerce_customer_address)){
    $data = array();
    $default_fields = array_keys(addressfield_default_values());
    $language = isset($entity->language) ? $entity->language : LANGUAGE_NONE;
    foreach($entity->commerce_customer_address[$language][0] as $field => $value){
      // Compare fields with addressfield default fields and store any additional fields in data
      if (!in_array($field, $default_fields)){
        $data[$field] = $value; // Store additional field value
      }
    }
    if($data){
      $entity->commerce_customer_address[$language][0]['data'] = serialize($data);
    }
  }
  
}

/**
 * Implements hook_field_attach_load
 * Load and set additional addressfield field values from serialized data column
 */
function example_field_attach_load($entity_type, $entities, $age, $options){

  if ($entity_type == 'commerce_customer_profile'){
    foreach($entities as $key => $entity){
      $language = isset($entity->language) ? $entity->language : LANGUAGE_NONE;
      if (!isset($entity->commerce_customer_address)) continue;
      if (!empty($entity->commerce_customer_address[$language][0]['data'])){
        // Unserialize data, else skip if string
        if( $data = unserialize($entity->commerce_customer_address[$language][0]['data'])){
          foreach($data as $field_key => $field_value){
            // Store unserialized data values in additional fields
            $entities[$key]->commerce_customer_address[$language][0][$field_key] = $field_value;
          }
        }
      }
    }
  }
  
}

Any additional fields added in the addressfield plugin are stored and loaded automatically from the "data" column.

Example data column looks like this:

a:6:{s:20:"flat_room_suite_type";s:5:"Suite";s:21:"flat_room_suite_value";s:3:"602";s:16:"floor_level_type";s:5:"Floor";s:17:"floor_level_value";s:1:"6";s:16:"block_tower_type";s:5:"Block";s:17:"block_tower_value";s:11:"Iona Towers";}

Note, in order to control the rendering of the new fields, I had to add to the addressfield plugin this, as the default addressfield render only renders the default field child elements, and the 'render callback' isn't used in the ctools plugin:

  if ($context['mode'] != 'form') {
    // Add our own render callback for the format view
    $format['#pre_render'][] = 'example_render_address';
  }

I hope that helps!

cheers,

DT

nclavaud’s picture

Thanks David, that helps a lot indeed.

I wish there would be a simpler way to extend those address fields. Actually the hard-coded arrays of field names in addressfield.module are somehow forcing us to rewrite a bunch of code...

xatoo’s picture

Category: feature » bug
Status: Active » Needs review
FileSize
2.13 KB

The hardcoded arrays are indeed forcing plugin users to hack into the addressfield module.
It prevents, for instance, fields from Address Field Phone to be rendered:
See #2012798: Phone fields not rendered on display

Here is a patch that gets rid of the hardcoded arrays.

AaronBauman’s picture

Status: Needs review » Needs work

This patch results in a fatal error when submitting any form with an addressfield on it:

Fatal error: Cannot create references to/from string offsets nor overloaded objects in /includes/common.inc on line 6548 

Call Stack: 0.0002 352976 
1. {main}() /index.php:0 0.4499 41032248 
2. menu_execute_active_handler() /index.php:21 0.4515 41181596 
3. call_user_func_array() /includes/menu.inc:517 0.4515 41181912 
4. drupal_get_form() /includes/menu.inc:517 0.4515 41182644 
5. drupal_build_form() /includes/form.inc:131 0.4550 41496316 
6. drupal_process_form() /includes/form.inc:374 0.4550 41499656 
7. form_builder() /includes/form.inc:842 0.4756 42104668 
8. form_builder() /includes/form.inc:1857 0.4757 42108052 
9. form_builder() /includes/form.inc:1857 0.4759 42113772 
10. form_builder() /includes/form.inc:1857 0.5251 42821520 
11. form_builder() /includes/form.inc:1857 0.5254 42827324 
12. form_builder() /includes/form.inc:1857 0.5258 42832684 
13. form_builder() /includes/form.inc:1857 0.5259 42833500 
14. _form_builder_handle_input_element() /includes/form.inc:1795 0.5259 42834060 
15. drupal_array_set_nested_value() /includes/form.inc:2003

It does work to display phone fields already in the database.
However, (and maybe i'm opening a can of worms here) in the case of Address Field Phone, phone number gets rendered in the middle of the address block.
Seems like there should be a way to determine where the value should be rendered.
Since this data is serialized, I'm guessing that's not easy to build with views either.

xatoo’s picture

Status: Needs work » Needs review
FileSize
1.26 KB

Hmm, I was a bit overenthusiastic removing both hardcoded arrays but apparently I missed something.
Here is a patch that only removes the hardcoded array when rendering:

AaronBauman’s picture

Title: Support for a way of storing more separate values. » Support for a way of storing and rendering more separate values
Status: Needs review » Needs work
FileSize
12.42 KB

Yes, this works now to collect and display phone data.
However, outputting the phone between street address and city, state, zip is extremely confusing.

I think this needs to be addressed before this patch moves ahead.

xatoo’s picture

Status: Needs work » Needs review

aaronbauman, what you describe is a different bug in Addressfield Phone. See:
#2010452: Phone gets rendered between address field 1 and state country fields (CR: weight change)

I'll post a patch for that one soon. Please restrict your feedback on this issue to the patch in #10.

steven.wichers’s picture

This has fixed the issue for me.

cameron prince’s picture

I tried the patch listed in number 10 and it failed for both current release and dev versions of addressfield. Attached is a quick re-roll and I can confirm that this does cause the extra fields to be rendered.

cameron prince’s picture

The patch in #14 wouldn't apply due to a header problem. Here's a replacement.

dooug’s picture

patch in #15 didn't apply and said it was corrupt. Re-rolled.

Confirmed that it shows the plugin field in the case of the addressfield_phone module.

TBarina’s picture

Confirmin #11.
Output of the phone is between street address and city, state and zip.

Besides, adding a label (Phone, Fax) would be much clearer.

TBarina’s picture

Sorry, I overlooked #12.

5n00py’s picture

Also we have problems with loading default value. When we add some additional data to field we need to add new keys to $address variable in addressfield_field_widget_form.

There is no way to extend it. Function addressfield_default_values return hardcoded data:

  return array(
    'country' => $default_country,
    'name_line' => '',
    'first_name' => '',
    'last_name' => '',
    'organisation_name' => '',
    'administrative_area' => '',
    'sub_administrative_area' => '',
    'locality' => '',
    'dependent_locality' => '',
    'postal_code' => '',
    'thoroughfare' => '',
    'premise' => '',
    'sub_premise' => '',
    'data' => '',
  );

In result we can't have full object.

And when its done, appears another "hole" called addressfield_process_format_form.

in_array($key, array('name_line', 'first_name', 'last_name', 'organisation_name', 'country', 'administrative_area', 'sub_administrative_area', 'locality', 'dependent_locality', 'postal_code', 'thoroughfare', 'premise', 'sub_premise'))

And don't know, maybe addressfield_process_format_form task can be achieved via '#process' callback in format definition.

This things make addressfield broken as api module.

Stomper’s picture

I'm currently using Address Field within a taxonomy for later use with one of the various geolocation/mapping modules. Like the OP, I'd also like the ability to filter and display addresses by component - city, state, zipcode etc.

I currently have a CSV file of several thousand addresses. Unfortunately, the addresses are stored in separate components, a row/column for street address, city, state, zipcode etc.

I'd like to bulk import these addresses into my taxonomy vocabulary but an unsure whether this module, or a import/export module like Taxonomy CSV or Feeds is able to map and format separated address components into the Address Field.

If not, I will have to abandon using this module for the addresses, and instead create individual text fields for each address component

5n00py’s picture

Hi Stomper!
This issue is not related to Your question.
As I see addressfield have feeds support. See next issue:
https://drupal.org/node/1408796

bdone’s picture

@stomper: migrate is another solution, and w/ the help of this patch (committed in 7.x-1.x-dev), it includes addressfield support.

Stomper’s picture

Thanks for the tips, I will look to those approaches.

I tried it with Taxonomy CSV just to see what would happen, but since my vocabulary has less fields then by CSV has rows/columns, it didn't work. No worries, will try the other approaches.

One other thing, is there a means to use multiple fields to create an address (Address Field). Basically, if I have to separate the street address to match the format of my CSV list, since each "component" is being stored separately. How can I convert the separate fields into one Address Field address?

5n00py’s picture

In #19 I say that addressfield_process_format_form is problem for extending, but I solve my problem via #process. For more details see #2121651: Better form processing.

As for this issue.
Addressfield shouldn’t render/process data that stored separately (data column included).
Addressfield module just don't know how to do that.
So if You provide additional data & form elements, then You need to to this jobs with #process & #pre_render callbacks.

I think we need to write some docs (how to do this) and close this issue without any of prev. patches.

Stomper’s picture

I'd definitely appreciate some documentation on how to do it. If possible, an approach to accomplish this for non-programmers would be much appreciated, GUI would be perfect.

With some research, it seems possible to do what I need to do with Feeds Tamper/Explode, although not 100% sure.

https://drupal.org/node/1217356

rszrama’s picture

Title: Support for a way of storing and rendering more separate values » Add a hook to determine what address components get rendered by Address Field
Category: Bug report » Feature request
Issue summary: View changes
Status: Needs review » Active

I was considering the patch but ultimately came to a similar conclusion as 5n00py; I don't necessarily like this module blindly printing anything out it finds in the array, because we just don't know what might put data in there.

However, I wouldn't mind a patch similar to #16 that let modules explicitly define the keys that should be rendered via the default rendering process. This would require some appropriate hook (no ideas on name atm), address field registering its own keys, and other modules implementing the hook as necessary. If a module doesn't use the hook, we'd assume it is using the callbacks 5n00py mentions above to show the values of whatever elements its adding to the address.

vasike’s picture

Status: Active » Reviewed & tested by the community

imho #16's patch is more than enough.
anyone who's want to extend the addressfield data with extra address elements, i think will now to use the '#access' property for the extra element unneeded to be displayed.

so i think we have a RTBC.

DuaelFr’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
1.9 KB

The same kind of thing might be done in _addressfield_process_format_form(), isn't it ?