class Address in Address 8
Provides an address form element.
Use #field_overrides to override the country-specific address format, forcing specific properties to be hidden, optional, or required.
Usage example:
$form['address'] = [
'#type' => 'address',
'#default_value' => [
'given_name' => 'John',
'family_name' => 'Smith',
'organization' => 'Google Inc.',
'address_line1' => '1098 Alta Ave',
'postal_code' => '94043',
'locality' => 'Mountain View',
'administrative_area' => 'CA',
'country_code' => 'US',
'langcode' => 'en',
],
'#field_overrides' => [
AddressField::ORGANIZATION => FieldOverride::REQUIRED,
AddressField::ADDRESS_LINE2 => FieldOverride::HIDDEN,
AddressField::POSTAL_CODE => FieldOverride::OPTIONAL,
],
'#available_countries' => [
'DE',
'FR',
],
];
Plugin annotation
@FormElement("address");
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\address\Element\Address
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Address
4 string references to 'Address'
- address.info.yml in ./
address.info.yml - address.info.yml
- address.schema.yml in config/
schema/ address.schema.yml - config/schema/address.schema.yml
- AddressDefaultWidgetTest::setUp in tests/
src/ FunctionalJavascript/ AddressDefaultWidgetTest.php - field.field.node.address_test.field_address_test.yml in tests/
modules/ address_test/ config/ install/ field.field.node.address_test.field_address_test.yml - tests/modules/address_test/config/install/field.field.node.address_test.field_address_test.yml
2 #type uses of Address
- AddressDefaultWidget::formElement in src/
Plugin/ Field/ FieldWidget/ AddressDefaultWidget.php - Returns the form for a single field widget.
- DefaultValueTestForm::buildForm in tests/
modules/ address_test/ src/ Form/ DefaultValueTestForm.php - Form constructor.
File
- src/
Element/ Address.php, line 52
Namespace
Drupal\address\ElementView source
class Address extends FormElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return [
// List of country codes. If empty, all countries will be available.
'#available_countries' => [],
// FieldOverride constants keyed by AddressField constants.
'#field_overrides' => [],
// Deprecated. Use #field_overrides instead.
'#used_fields' => [],
'#input' => TRUE,
'#multiple' => FALSE,
'#default_value' => NULL,
'#process' => [
[
$class,
'processAddress',
],
[
$class,
'processGroup',
],
],
'#pre_render' => [
[
$class,
'groupElements',
],
[
$class,
'preRenderGroup',
],
],
'#after_build' => [
[
$class,
'clearValues',
],
],
'#attached' => [
'library' => [
'address/form',
],
],
'#theme_wrappers' => [
'container',
],
];
}
/**
* Ensures all keys are set on the provided value.
*
* @param array $value
* The value.
*
* @return array
* The modified value.
*/
public static function applyDefaults(array $value) {
$properties = [
'given_name',
'additional_name',
'family_name',
'organization',
'address_line1',
'address_line2',
'postal_code',
'sorting_code',
'dependent_locality',
'locality',
'administrative_area',
'country_code',
'langcode',
];
foreach ($properties as $property) {
if (!isset($value[$property])) {
$value[$property] = NULL;
}
}
return $value;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
// Ensure both the default value and the input have all keys set.
// Preselect the default country to ensure it's present in the value.
$element['#default_value'] = (array) $element['#default_value'];
$element['#default_value'] = self::applyDefaults($element['#default_value']);
if (empty($element['#default_value']['country_code']) && $element['#required']) {
$element['#default_value']['country_code'] = Country::getDefaultCountry($element['#available_countries']);
}
// Any input with a NULL or missing country_code is considered invalid.
// Even if the element is optional and no country is selected, the
// country_code would be an empty string, not NULL.
if (is_array($input) && !isset($input['country_code'])) {
$input = NULL;
}
if (is_array($input)) {
$input = self::applyDefaults($input);
if (empty($input['country_code']) && $element['#required']) {
$input['country_code'] = $element['#default_value']['country_code'];
}
}
return is_array($input) ? $input : $element['#default_value'];
}
/**
* Processes the address form element.
*
* @param array $element
* The form element to process.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The processed element.
*
* @throws \InvalidArgumentException
* Thrown when #used_fields is malformed.
*/
public static function processAddress(array &$element, FormStateInterface $form_state, array &$complete_form) {
// Convert #used_fields into #field_overrides.
if (!empty($element['#used_fields']) && is_array($element['#used_fields'])) {
$unused_fields = array_diff(AddressField::getAll(), $element['#used_fields']);
$element['#field_overrides'] = [];
foreach ($unused_fields as $field) {
$element['#field_overrides'][$field] = FieldOverride::HIDDEN;
}
unset($element['#used_fields']);
}
// Validate and parse #field_overrides.
if (!is_array($element['#field_overrides'])) {
throw new \InvalidArgumentException('The #field_overrides property must be an array.');
}
$element['#parsed_field_overrides'] = new FieldOverrides($element['#field_overrides']);
$id_prefix = implode('-', $element['#parents']);
$wrapper_id = Html::getUniqueId($id_prefix . '-ajax-wrapper');
// The #value has the new values on #ajax, the #default_value otherwise.
$value = $element['#value'];
$element = [
'#tree' => TRUE,
'#prefix' => '<div id="' . $wrapper_id . '">',
'#suffix' => '</div>',
// Pass the id along to other methods.
'#wrapper_id' => $wrapper_id,
] + $element;
$element['langcode'] = [
'#type' => 'hidden',
'#value' => $element['#default_value']['langcode'],
];
$element['country_code'] = [
'#type' => 'address_country',
'#title' => t('Country'),
'#available_countries' => $element['#available_countries'],
'#default_value' => $element['#default_value']['country_code'],
'#required' => $element['#required'],
'#limit_validation_errors' => [],
'#ajax' => [
'callback' => [
get_called_class(),
'ajaxRefresh',
],
'wrapper' => $wrapper_id,
],
'#weight' => -100,
];
if (!empty($value['country_code'])) {
$element = static::addressElements($element, $value);
}
return $element;
}
/**
* Builds the format-specific address elements.
*
* @param array $element
* The existing form element array.
* @param array $value
* The address value, in $property_name => $value format.
*
* @return array
* The modified form element array containing the format specific elements.
*/
protected static function addressElements(array $element, array $value) {
$size_attributes = [
AddressField::ADMINISTRATIVE_AREA => 30,
AddressField::LOCALITY => 30,
AddressField::DEPENDENT_LOCALITY => 30,
AddressField::POSTAL_CODE => 10,
AddressField::SORTING_CODE => 10,
AddressField::GIVEN_NAME => 25,
AddressField::ADDITIONAL_NAME => 25,
AddressField::FAMILY_NAME => 25,
];
$field_overrides = $element['#parsed_field_overrides'];
/** @var \CommerceGuys\Addressing\AddressFormat\AddressFormat $address_format */
$address_format = \Drupal::service('address.address_format_repository')
->get($value['country_code']);
$required_fields = AddressFormatHelper::getRequiredFields($address_format, $field_overrides);
$labels = LabelHelper::getFieldLabels($address_format);
$locale = \Drupal::languageManager()
->getConfigOverrideLanguage()
->getId();
if (Locale::matchCandidates($address_format
->getLocale(), $locale)) {
$format_string = $address_format
->getLocalFormat();
}
else {
$format_string = $address_format
->getFormat();
}
$grouped_fields = AddressFormatHelper::getGroupedFields($format_string, $field_overrides);
foreach ($grouped_fields as $line_index => $line_fields) {
if (count($line_fields) > 1) {
// Used by the #pre_render callback to group fields inline.
$element['container' . $line_index] = [
'#type' => 'container',
'#attributes' => [
'class' => [
'address-container-inline',
],
],
];
}
foreach ($line_fields as $field_index => $field) {
$property = FieldHelper::getPropertyName($field);
$class = str_replace('_', '-', $property);
$element[$property] = [
'#type' => 'textfield',
'#title' => $labels[$field],
'#default_value' => isset($value[$property]) ? $value[$property] : '',
'#required' => in_array($field, $required_fields),
'#size' => isset($size_attributes[$field]) ? $size_attributes[$field] : 60,
'#attributes' => [
'class' => [
$class,
],
'autocomplete' => FieldHelper::getAutocompleteAttribute($field),
'autocapitalize' => 'words',
],
];
if (count($line_fields) > 1) {
$element[$property]['#group'] = $line_index;
}
}
}
// Hide the label for the second address line.
if (isset($element['address_line2'])) {
$element['address_line2']['#title_display'] = 'invisible';
}
// Add predefined options to the created subdivision elements.
$element = static::processSubdivisionElements($element, $value, $address_format);
return $element;
}
/**
* Processes the subdivision elements, adding predefined values where found.
*
* @param array $element
* The existing form element array.
* @param array $value
* The address value, in $property_name => $value format.
* @param \CommerceGuys\Addressing\AddressFormat\AddressFormat $address_format
* The address format.
*
* @return array
* The processed form element array.
*/
protected static function processSubdivisionElements(array $element, array $value, AddressFormat $address_format) {
$depth = $address_format
->getSubdivisionDepth();
if ($depth === 0) {
// No predefined data found.
return $element;
}
$subdivision_properties = [];
foreach ($address_format
->getUsedSubdivisionFields() as $field) {
$subdivision_properties[] = FieldHelper::getPropertyName($field);
}
// Load and insert the subdivisions for each parent id.
$locale = \Drupal::languageManager()
->getConfigOverrideLanguage()
->getId();
$current_depth = 1;
$parents = [];
foreach ($subdivision_properties as $index => $property) {
if (!isset($element[$property]) || !Element::isVisibleElement($element[$property])) {
break;
}
$parent_property = $index ? $subdivision_properties[$index - 1] : 'country_code';
if ($parent_property && empty($value[$parent_property])) {
break;
}
$parents[] = $value[$parent_property];
$subdivisions = \Drupal::service('address.subdivision_repository')
->getList($parents, $locale);
if (empty($subdivisions)) {
break;
}
$element[$property]['#type'] = 'select';
$element[$property]['#options'] = $subdivisions;
$element[$property]['#empty_value'] = '';
unset($element[$property]['#size']);
if ($current_depth < $depth) {
$element[$property]['#ajax'] = [
'callback' => [
get_called_class(),
'ajaxRefresh',
],
'wrapper' => $element['#wrapper_id'],
];
}
$current_depth++;
}
return $element;
}
/**
* Groups elements with the same #group so that they can be inlined.
*/
public static function groupElements(array $element) {
$sort = [];
foreach (Element::getVisibleChildren($element) as $key) {
if (isset($element[$key]['#group'])) {
// Copy the element to the container and remove the original.
$group_index = $element[$key]['#group'];
$container_key = 'container' . $group_index;
$element[$container_key][$key] = $element[$key];
unset($element[$key]);
// Mark the container for sorting.
if (!in_array($container_key, $sort)) {
$sort[] = $container_key;
}
}
}
// Sort the moved elements, so that their #weight stays respected.
foreach ($sort as $key) {
uasort($element[$key], [
SortArray::class,
'sortByWeightProperty',
]);
}
return $element;
}
/**
* Ajax callback.
*/
public static function ajaxRefresh(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
$parents = $triggering_element['#array_parents'];
$triggering_element_name = array_pop($parents);
// The country_code element is nested one level deeper than
// the subdivision elements.
if ($triggering_element_name == 'country_code') {
array_pop($parents);
}
$address_element = NestedArray::getValue($form, $parents);
return $address_element;
}
/**
* Clears dependent form values when the country or subdivision changes.
*
* Implemented as an #after_build callback because #after_build runs before
* validation, allowing the values to be cleared early enough to prevent the
* "Illegal choice" error.
*/
public static function clearValues(array $element, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
if (!$triggering_element) {
return $element;
}
$keys = [
'country_code' => [
'dependent_locality',
'locality',
'administrative_area',
'postal_code',
'sorting_code',
],
'administrative_area' => [
'dependent_locality',
'locality',
],
'locality' => [
'dependent_locality',
],
];
$triggering_element_name = end($triggering_element['#parents']);
if (isset($keys[$triggering_element_name])) {
$input =& $form_state
->getUserInput();
foreach ($keys[$triggering_element_name] as $key) {
if (isset($element[$key])) {
$parents = array_merge($element['#parents'], [
$key,
]);
NestedArray::setValue($input, $parents, '');
$element[$key]['#value'] = '';
}
}
}
return $element;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Address:: |
protected static | function | Builds the format-specific address elements. | |
Address:: |
public static | function | Ajax callback. | |
Address:: |
public static | function | Ensures all keys are set on the provided value. | |
Address:: |
public static | function | Clears dependent form values when the country or subdivision changes. | |
Address:: |
public | function |
Returns the element properties for this element. Overrides ElementInterface:: |
|
Address:: |
public static | function | Groups elements with the same #group so that they can be inlined. | |
Address:: |
public static | function | Processes the address form element. | |
Address:: |
protected static | function | Processes the subdivision elements, adding predefined values where found. | |
Address:: |
public static | function |
Determines how user input is mapped to an element's #value property. Overrides FormElement:: |
|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormElement:: |
public static | function | Adds autocomplete functionality to elements. | |
FormElement:: |
public static | function | #process callback for #pattern form element property. | |
FormElement:: |
public static | function | #element_validate callback for #pattern form element property. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
public | function | Constructs a \Drupal\Component\Plugin\PluginBase object. | 92 |
RenderElement:: |
public static | function | Adds Ajax information about an element to communicate with JavaScript. | |
RenderElement:: |
public static | function | Adds members of this group as actual elements for rendering. | |
RenderElement:: |
public static | function | Form element processing handler for the #ajax form property. | 1 |
RenderElement:: |
public static | function | Arranges elements into groups. | |
RenderElement:: |
public static | function |
Sets a form element's class attribute. Overrides ElementInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |