地址格式(address format)
不同国家,有不同的地址格式。不同的地址格式,不同的地址字段顺序,中国是从大到小,美国等拉丁语法一般是从小到大。
解决这个问题,依赖一下东西:
- (Query Address Data ) https://chromium-i18n.appspot.com/ssl-address
比如说,Armenia 国家,和美国的,通过配置如下:
'AM' => [
'format' => "%givenName %familyName\n%organization\n%addressLine1\n%addressLine2\n%postalCode\n%locality\n%administrativeArea",
'postal_code_pattern' => '(?:37)?\d{4}',
'subdivision_depth' => 1,
],
'US' => [
'format' => "%givenName %familyName\n%organization\n%addressLine1\n%addressLine2\n%locality, %administrativeArea %postalCode",
'required_fields' => [
'addressLine1', 'locality', 'administrativeArea', 'postalCode',
],
'uppercase_fields' => [
'locality', 'administrativeArea',
],
'administrative_area_type' => 'state',
'postal_code_type' => 'zip',
'postal_code_pattern' => '(\d{5})(?:[ \-](\d{4}))?',
'subdivision_depth' => 1,
],
添加其它地址字段
比如,我需要添加一个Building name 字段。
我们需要创建一个第三方模块,实现一个事件订阅机制,使得我们在地址格式中的“组织”后插入“Building name”字段:
<?php
namespace Drupal\mymodule\EventSubscriber;
use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\AddressFormatEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Repurposes additional name field as building name.
*/
class BuildingNameEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
$events[AddressEvents::ADDRESS_FORMAT][] = ['onAddressFormat'];
return $events;
}
public function onAddressFormat(AddressFormatEvent $event) {
$definition = $event->getDefinition();
// Place %additionalName after %organization in the format.
$format = $definition['format'];
$format = str_replace('%additionalName', '', $format);
$format = str_replace('%organization', "%organization\n%additionalName", $format);
$definition['format'] = $format;
$event->setDefinition($definition);
}
}
然后,需要将事件订阅类应用在配置(mymodule.services.yml)里:
services:
mymodule.subscriber:
class: Drupal\mymodule\EventSubscriber\BuildingNameEventSubscriber
tags:
- {name: event_subscriber}
然后清一下缓存,就可以看到一个“Middle name”字段出现在地址表单里:
然后,我们需要把Middle name改成Building name,并且更改其文本字段大小以匹配公司文本字段,我们可以利用drupal的更名表单Hook,如下:
<?php
use Drupal\Core\Form\FormStateInterface;
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']['#after_build'][] = 'mymodule_customize_address';
}
}
function mymodule_customize_address($element, $form_state) {
$element['additional_name']['#title'] = t('Building name');
$element['additional_name']['#size'] = 60;
return $element;
}
国家支持
Address module里使用了The Commerce Guys Addressing library
库,默认支持250个国家地区选择。但是我们平时系统不需要那么多国家,如何处理?
下面举个例子如何指定我需要的国家。首先,需要得到国家的标准双字母代码(standard 2-letter codes),然后,通过事件订阅器的写法,具体事件是AddressEvents::AVAILABLE_COUNTRIES
,去实现指定国家,比如我只需要5个国家: Australia, Brazil, Canada, Japan, and the United Kingdom.
<?php
namespace Drupal\mymodule\EventSubscriber;
use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\AvailableCountriesEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LimitCountriesEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
$events[AddressEvents::AVAILABLE_COUNTRIES][] = ['onAvailableCountries'];
return $events;
}
public function onAvailableCountries(AvailableCountriesEvent $event) {
$countries = ['AU' => 'AU', 'BR' => 'BR', 'CA' => 'CA', 'GB' => 'GB', 'JP' => 'JP'];
$event->setAvailableCountries($countries);
}
}
然后在自定义模块对应的services.yml
引用进来,然后清缓存生效。
如何修改地址分支数据
通过事件订阅器,具体的事件是AddressEvents::SUBDIVISIONS
,Address module的代码里有一个测试案例,可以看address/tests/modules/address_test/src/EventSubscriber/GreatBritainEventSubscriber.php.
。
测试案例里,它想给英国国家添加一个县字段和一个预定义的县列表。
首先,实现AddressEvents::SUBDIVISIONS
事件的订阅和腿脚方法onSubdivisions()
public static function getSubscribedEvents() {
$events[AddressEvents::SUBDIVISIONS][] = ['onSubdivisions'];
return $events;
}
public function onSubdivisions(SubdivisionsEvent $event) {
// For administrative areas $parents is an array with just the country code.
// Otherwise it also contains the parent subdivision codes. For example,
// if we were defining cities in California, $parents would be ['US', 'CA'].
$parents = $event->getParents();
if ($event->getParents() != ['GB']) {
return;
}
$definitions = [
'country_code' => $parents[0],
'parents' => $parents,
'subdivisions' => [
// Key by the subdivision code, which is the value that's displayed on
// the formatted address. Could be an abbreviation (e.g 'CA' for
// California) or a full name like below.
// If it's an abbreviation, define a 'name' in the subarray, to be used
// in the address widget dropdown.
'Anglesey' => [],
// You can optionally define an ISO 3166-2 code for each subdivision.
'Blaenau Gwent' => [
'iso_code' => 'GB-BGW',
],
'Bridgend' => [],
'Caerphilly' => [],
'Cardiff' => [],
'Carmarthenshire' => [],
'Ceredigion' => [],
'Conwy' => [],
'Denbighshire' => [],
'Flintshire' => [],
'Gwynedd' => [],
'Merthyr Tydfil' => [],
'Monmouthshire' => [],
'Neath Port Talbot' => [],
'Newport' => [],
'Pembrokeshire' => [],
'Powys' => [],
'Rhondda Cynon Taf' => [],
'Swansea' => [],
'Tarfaen' => [],
'Vale of Glamorgan' => [],
'Wrexham' => [],
],
];
$event->setDefinitions($definitions);
}
如何限制地址可选
比如,我只想部分省份可以选,类似下面的方法实现。
使用hook:hook_form_alter()
和#pre_render
回调结合:
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;
}
强制修改地址字段覆盖值
有时候,部分地址字段无法通过drupal的配置去设置,那么可以通过hook来配置,具体的hook是hook_form_alter()
下面是给组织、地区和邮政编码字段属性设置覆盖值。
<?php
use Drupal\Core\Form\FormStateInterface;
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']['#field_overrides'] = [
'organization' => 'required',
'locality' => 'optional',
'postalCode' => 'hidden',
];
}
}
强制修改地址字段表单属性
在此示例中,我们将对Company字段属性进行以下自定义:将“Company”标签更改为“Organization”。将文本字段大小从60更改为30。*使字段属性成为必填项。
<?php
use Drupal\Core\Form\FormStateInterface;
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']['#after_build'][] = 'mymodule_customize_address';
}
}
function mymodule_customize_address($element, $form_state) {
$element['organization']['#title'] = t('Organization');
$element['organization']['#size'] = 30;
$element['organization']['#required'] = TRUE;
return $element;
}
在本例中,我们将使用两个街道地址字段属性的单独标签来更改默认的“街道地址”标签
function mymodule_customize_address($element, $form_state) {
dpm($element['#field_overrides']);
$element['address_line1']['#title'] = t('Address line 1');
$element['address_line2']['#title'] = t('Address line 2');
$element['address_line2']['#title_display'] = 'before';
设置地址初始值
将默认国家设置为澳大利亚,郊区设置为新南威尔士,城市设置为悉尼。
<?php
namespace Drupal\mymodule\EventSubscriber;
use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\InitialValuesEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class AustraliaDefaultEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
$events[AddressEvents::INITIAL_VALUES][] = ['onInitialValues'];
return $events;
}
public function onInitialValues(InitialValuesEvent $event) {
$initial_values = [
'country_code' => 'AU',
'administrative_area' => 'NSW',
'locality' => 'Sydney',
];
$event->setInitialValues($initial_values);
}
}
记得在yml服务里引用这个:
services:
mymodule.australia_subscriber:
class: Drupal\mymodule\EventSubscriber\AustraliaDefaultEventSubscriber
tags:
- {name: event_subscriber}
隐藏国家字段
法1:创建一个个自定义地址字段格式化程序插件,扩展了默认的地址格式化程序
下面将创一个插件,将显示除美国以外的所有国家/地区的地址的国家/地区。
假设我们创建好了一个第三方模块,然后再创一个第三方插件,名为AddressHideUSFormatter
:
namespace Drupal\mymodule\Plugin\Field\FieldFormatter;
use Drupal\address\Plugin\Field\FieldFormatter\AddressDefaultFormatter;
/**
* Plugin implementation of the 'address_us_default' formatter.
*
* @FieldFormatter(
* id = "address_us_default",
* label = @Translation("Hide US"),
* field_types = {
* "address",
* },
* )
*/
class AddressHideUSFormatter extends AddressDefaultFormatter {
}
注意了,address_us_default
是这个插件的唯一id,可以在注解里看到。重请缓存后,就可以看到我们的插件了:
然后,就可以给插件定制行为了,默认,插件有一个默认的方法可以覆写——postRender()
,比如说写成这样:
public static function postRender($content, array $element) {
/** @var \CommerceGuys\Addressing\AddressFormat\AddressFormat $address_format */
$address_format = $element['#address_format'];
$locale = $element['#locale'];
// Add the country to the bottom or the top of the format string,
// depending on whether the format is minor-to-major or major-to-minor.
if ($address_format->getCountryCode() == 'US') {
$format_string = $address_format->getFormat();
}
elseif (Locale::matchCandidates($address_format->getLocale(), $locale)) {
$format_string = '%country' . "\n" . $address_format->getLocalFormat();
}
else {
$format_string = $address_format->getFormat() . "\n" . '%country';
}
$replacements = [];
foreach (Element::getVisibleChildren($element) as $key) {
$child = $element[$key];
if (isset($child['#placeholder'])) {
$replacements[$child['#placeholder']] = $child['#value'] ? $child['#markup'] : '';
}
}
$content = self::replacePlaceholders($format_string, $replacements);
$content = nl2br($content, FALSE);
return $content;
}
然后,补充相关依赖:
use CommerceGuys\Addressing\Locale;
use Drupal\Core\Render\Element;
这样,插件就大功告成了。
使用Plain address formatter 定制地址显示
如果想精准控制地址的布局,需要考虑这种方法。
这个方法,是通过twig模板的方法。
address-plain.html.twig
变量如下:
- given_name: 名称.
- additional_name: 其它名称
- family_name: 姓氏
- organization: 组织.
- address_line1: 第一地址行.
- address_line2: 第二地址行.
- postal_code: 邮政编码.
- sorting_code: 分类代码.
- country.code: 国家代码.
- country.name: 国家名称.
Subdivision 字段变量:
- dependent_locality: 从属地区.
- locality: 地区.
- administrative_area: 行政区域.
比如,下面需要将城市字段显示出来,如何处理?
可以在twig中,应该把上面的
{{ locality.code }}
改成{{ locality }}
.
code和name如何选择?默认以code为高优先级。
- dependent_locality.code: 从属地区code.
- dependent_locality.name: 从属地区name.
- locality.code: 地区code.
- locality.name: 地区name.
- administrative_area.code: 行政区域 code.
- administrative_area.name: 行政区域 name.