Cannot modify subdivision options in a form alter as documentation states that you can.

Created on 26 August 2022, about 2 years ago
Updated 10 January 2024, 11 months ago

Problem/Motivation

The code listed for this action on this page does not work:

https://docs.drupalcommerce.org/commerce2/developer-guide/customers/addr...

function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (($form_id == 'profile_customer_edit_form') || ($form_id == 'profile_customer_add_form')) {
    $form['address']['widget'][0]['address']['#pre_render'][] = 'mymodule_prerender';
}

function mymodule_prerender($element) {
  if ($element['country_code']['#default_value'] == 'US') {
    $include_states = ['', 'NY', 'NJ', 'PA', 'DE', 'MD', 'DC', 'VA', 'WV'];
    $options = array_intersect_key($element['administrative_area']['#options'], array_flip($include_states));
    $element['administrative_area']['#options'] = $options;
  }
  return $element;
}
}

When I try this, I get two different errors.

The first one is:

Drupal\Core\Security\UntrustedCallbackException: Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function.

If you set the pre_render and assign it the function itself, you get:

Exception: Serialization of 'Closure' is not allowed in serialize()

Proposed resolution

Update the documentation with [#2966725]. It will not work any other way.

After creating a class like the change linked above, and doing:

/**
 * Implements hook_form_alter().
 */
function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (preg_match('/foo/i', $form_id)) {
    $form['field_address']['widget'][0]['address']['#pre_render'][] = [
      MyTrustedCallbackClass::class, 'myMethod',
    ];
  }
}

It then worked.

💬 Support request
Status

Fixed

Version

2.0

Component

Code

Created by

🇺🇸United States kevinquillen

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇺🇸United States tdwhite

    I am struggling to make this work. It seems the #options for administrative_area doesn't exist because I keep getting error "Warning: Undefined array key "#options" ". It seems like limiting the list (US states in my case) for an address field on a node should be simple but can't figure out what I am doing wrong.

    Relevant snipped from node_form_alter function in my .module file:

    $form['field_address'][0]['address']['administrative_area']['#pre_render'][] = '\Drupal\mycustommodule\MyTrustedCallbackClass::preRender';
    

    And from MyTrustedCallbackClass.php:

    <?php
    
    namespace Drupal\mycustommodule;
    
    use Drupal\Core\Form\FormStateInterface;
    use Drupal\Core\Security\TrustedCallbackInterface;
    
    class MyTrustedCallbackClass implements TrustedCallbackInterface {
    
      /**
       * {@inheritdoc}
       */
      public static function trustedCallbacks() {
        return ['preRender'];
      }
    
      /**
       * Callback to limit options.
       */
      public static function preRender($element) {
        $include_states = ['NY', 'CA'];
        $options = array_intersect_key($element['#options'], array_flip($include_states));
        $element['#options'] = $options;
        return $element;
      }
    }
    ?>
    
  • 🇺🇸United States tdwhite

    It took me quite a while but I did get this working. There is an odd issue in that the State and Zip code fields now show on separate lines of the edit form. I'd prefer that didn't happen but now that this is functional I'm happy enough to move on.

    Example of working code for .module file:

    function mycustommodule_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
      if ($form_id == 'your_form_id') {
        $form['field_address']['widget'][0]['address']['#pre_render'][] = [
          MyTrustedCallbackClass::class, 'myMethod',
        ];
      }
    }
    

    And for the MyTrustedCallbackClass.php:

    namespace Drupal\mycustommodule;
    
    use Drupal\Core\Security\TrustedCallbackInterface;
    
    class MyTrustedCallbackClass implements TrustedCallbackInterface {
    
      public static function trustedCallbacks() {
        return ['myMethod'];
      }
    
      public static function myMethod($element) {
        $include_states = ['NY', 'CA'];
        $options = array_intersect_key($element['administrative_area']['#options'], array_flip($include_states));
        $element['administrative_area']['#options'] = $options;
        return $element;
      }
    }
    

    In retrospect it wasn't all that difficult but as someone who is still getting up to speed with most of my experience being a Drupal 7 site builder and not a programmer sometimes I get tripped up easily.

  • 🇺🇸United States dinonet

    Hi tdwhite,

    I've been able to modify the subdivisions this way but the part I'm missing is the translated labels coming from the json files (ex. AE.json)

    It's the local_code which is the translated label that I cannot override. Any ideas on how to use a custom json for these?

    {
    "country_code": "AE",
    "locale": "ar",
    "subdivisions": {
    "Abu Dhabi": {
    "local_code": "أبو ظبي",
    "iso_code": "AE-AZ"
    },
    "Sharjah": {
    "local_code": "إمارة الشارقةّ",
    "local_name": "الشارقة",
    "iso_code": "AE-SH"
    },
    "Fujairah": {
    "local_code": "الفجيرة",
    "iso_code": "AE-FU"
    },
    "Umm Al Quwain": {
    "local_code": "ام القيوين",
    "iso_code": "AE-UQ"
    },
    "Dubai": {
    "local_code": "إمارة دبيّ",
    "local_name": "دبي",
    "iso_code": "AE-DU"
    },
    "Ras al Khaimah": {
    "local_code": "إمارة رأس الخيمة",
    "local_name": "رأس الخيمة",
    "iso_code": "AE-RK"
    },
    "Ajman": {
    "local_code": "عجمان",
    "iso_code": "AE-AJ"
    }
    }
    }

  • Status changed to Fixed 11 months ago
  • 🇷🇸Serbia bojanz

    Note that Address 2.0.0 has fixed Subdivision event subscriber doesn't receive a list of subdivisions already set for a country Fixed , which means that the best way to modify subdivisions (and their translations) is using the SubdivisionEvent, the same one documented here.

    Of course #3 works, though I generally prefer #after_build for all address element modifications, as documented here or shown here 💬 There is not possible to set a description for form elements of address (fields of address) Fixed .

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024