Addresses
Addresses are special elements Craft includes so each User can have an address book.
They’re element types with their own fields, but they’re strictly available to User’s field layouts. Querying addresses and working with their field data, however, is nearly identical to the experience you’d have with any other element type.
# Managing Address Fields
By default, address fields aren’t shown anywhere in the control panel—but they’re ready to customize and add to User field layouts.
In the control panel, navigate to Settings → Users.
The User Fields editor lets you manage whatever fields you’d like to be available for all users:
You can drag the native Addresses field into the user field layout. If you created your own “Contact Information” tab, for example, and moved Addresses into it you’d see that tab and field on every user detail page:
Back in User Settings, the Address Fields editor lets you manage the fields that are part of each address. Label, Country, and Address are included by default, with several other native fields available:
You can add any native and custom fields to this field layout just like any other element type.
# Address Repository
The commerceguys/addressing (opens new window) library helps power planet-friendly address handling and formatting, and its exhaustive repository of global address information available to any Craft project. If you’d like to get a list of countries, states, or provinces, for example, you can most likely fetch them from these included repositories.
You can use Craft’s Addresses (opens new window) service to do this from Twig templates and PHP.
Here’s how you can fetch a list of countries, for example:
You’ll get an array of Country (opens new window) objects back, which you could use to populate a dropdown menu:
{% set countryNamesByCode = countries|map(value => "#{value.name}") %}
<select name="myCountry">
{% for code, name in countryNamesByCode %}
<option value="{{ code }}">{{ name }}</option>
{% endfor %}
</select>
{# Output:
<select name="myCountry">
<option value="AF">Afghanistan</option>
<option value="AX">Åland Islands</option>
...
</select>
#}
You can similarly get a list of subdivisions, which are hierarchical and can have up to three levels depending on how a given country is organized: Administrative Area → Locality → Dependent Locality. We can get all U.S. states by getting subdivisions whose parents are 'US'
:
Each resulting array item is a Subdivision (opens new window) object, which we could treat similarly to the country example above:
{% set states = craft.app.getAddresses().subdivisionRepository.getAll(['US']) %}
{% set stateNamesByCode = states|map(value => "#{value.name}") %}
<select name="myState">
{% for code, name in stateNamesByCode %}
<option value="{{ code }}">{{ name }}</option>
{% endfor %}
</select>
{# Output:
<select name="myState">
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
...
</select>
#}
If we just want a key-value array instead of the extra information that comes with Country and Subdivision objects, we can call either repository’s getList()
method instead and save ourselves the |map
step from each example above:
{% set countries = craft.app.getAddresses().countryRepository.getList() %}
<select name="myCountry">
{% for code, name in countries %}
<option value="{{ code }}">{{ name }}</option>
{% endfor %}
</select>
{# Output:
<select name="myCountry">
<option value="AF">Afghanistan</option>
<option value="AX">Åland Islands</option>
...
</select>
#}
{% set states = craft.app.getAddresses().subdivisionRepository.getList(['US']) %}
<select name="myState">
{% for code, name in states %}
<option value="{{ code }}">{{ name }}</option>
{% endfor %}
</select>
{# Output:
<select name="myState">
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
...
</select>
#}
There’s a lot more you can do here, including translated place names, postal codes, timezones, and formatting! Check out the addressing docs (opens new window) for more details and examples.
# Querying Addresses
You can fetch addresses in your templates or PHP code using address queries.
See Element Queries to learn about how element queries work.
# Example
We can get all the addresses for a user by passing their ID to the ownerId
parameter.
- Create an address query with
craft.addresses()
. - Set the
ownerId
parameter on it. - Fetch the addresses with
.collect()
. - Loop through the addresses using a
{% for %}
(opens new window) tag. - Output preformatted address details with the
|address
filter.
{# Prepare an element query for addresses belonging to user having ID = 3 #}
{% set myAddressQuery = craft.addresses().ownerId(3) %}
{# Fetch the addresses as a collection #}
{% set addresses = myAddressQuery.collect() %}
{# Loop through addresses and output each one #}
{% for addr in addresses %}
{{ addr|address }}
{% endfor %}
# Parameters
Address queries support the following parameters:
Param | Description |
---|---|
administrativeArea | Narrows the query results based on the administrative area the assets belong to. |
afterPopulate | Performs any post-population processing on elements. |
andRelatedTo | Narrows the query results to only addresses that are related to certain other elements. |
asArray | Causes the query to return matching addresses as arrays of data, rather than Address (opens new window) objects. |
cache | Enables query cache for this Query. |
clearCachedResult | Clears the cached result (opens new window). |
countryCode | Narrows the query results based on the country the assets belong to. |
dateCreated | Narrows the query results based on the addresses’ creation dates. |
dateUpdated | Narrows the query results based on the addresses’ last-updated dates. |
fixedOrder | Causes the query results to be returned in the order specified by id. |
id | Narrows the query results based on the addresses’ IDs. |
ignorePlaceholders | Causes the query to return matching addresses as they are stored in the database, ignoring matching placeholder elements that were set by craft\services\Elements::setPlaceholderElement() (opens new window). |
inReverse | Causes the query results to be returned in reverse order. |
limit | Determines the number of addresses that should be returned. |
offset | Determines how many addresses should be skipped in the results. |
orderBy | Determines the order that the addresses should be returned in. (If empty, defaults to dateCreated DESC .) |
owner | Sets the ownerId parameter based on a given owner element. |
ownerId | Narrows the query results based on the addresses’ owner elements, per their IDs. |
preferSites | If unique() (opens new window) is set, this determines which site should be selected when querying multi-site elements. |
prepareSubquery | Prepares the element query and returns its subquery (which determines what elements will be returned). |
relatedTo | Narrows the query results to only addresses that are related to certain other elements. |
search | Narrows the query results to only addresses that match a search query. |
siteSettingsId | Narrows the query results based on the addresses’ IDs in the elements_sites table. |
trashed | Narrows the query results to only addresses that have been soft-deleted. |
uid | Narrows the query results based on the addresses’ UIDs. |
with | Causes the query to return matching addresses eager-loaded with related elements. |
# administrativeArea
Narrows the query results based on the administrative area the assets belong to.
Possible values include:
Value | Fetches addresses… |
---|---|
'AU' | with a administrativeArea of AU . |
'not US' | not in a administrativeArea of US . |
['AU', 'US'] | in a administrativeArea of AU or US . |
['not', 'AU', 'US'] | not in a administrativeArea of AU or US . |
{# Fetch addresses in the AU #}
{% set addresses = craft.queryFunction()
.administrativeArea('AU')
.all() %}
# afterPopulate
Performs any post-population processing on elements.
# andRelatedTo
Narrows the query results to only addresses that are related to certain other elements.
See Relations (opens new window) for a full explanation of how to work with this parameter.
{# Fetch all addresses that are related to myCategoryA and myCategoryB #}
{% set addresses = craft.queryFunction()
.relatedTo(myCategoryA)
.andRelatedTo(myCategoryB)
.all() %}
# asArray
Causes the query to return matching addresses as arrays of data, rather than Address (opens new window) objects.
{# Fetch addresses as arrays #}
{% set addresses = craft.queryFunction()
.asArray()
.all() %}
# cache
Enables query cache for this Query.
# clearCachedResult
Clears the cached result (opens new window).
# countryCode
Narrows the query results based on the country the assets belong to.
Possible values include:
Value | Fetches addresses… |
---|---|
'AU' | with a countryCode of AU . |
'not US' | not in a countryCode of US . |
['AU', 'US'] | in a countryCode of AU or US . |
['not', 'AU', 'US'] | not in a countryCode of AU or US . |
{# Fetch addresses in the AU #}
{% set addresses = craft.queryFunction()
.countryCode('AU')
.all() %}
# dateCreated
Narrows the query results based on the addresses’ creation dates.
Possible values include:
Value | Fetches addresses… |
---|---|
'>= 2018-04-01' | that were created on or after 2018-04-01. |
'< 2018-05-01' | that were created before 2018-05-01 |
['and', '>= 2018-04-04', '< 2018-05-01'] | that were created between 2018-04-01 and 2018-05-01. |
{# Fetch addresses created last month #}
{% set start = date('first day of last month')|atom %}
{% set end = date('first day of this month')|atom %}
{% set addresses = craft.queryFunction()
.dateCreated(['and', ">= #{start}", "< #{end}"])
.all() %}
# dateUpdated
Narrows the query results based on the addresses’ last-updated dates.
Possible values include:
Value | Fetches addresses… |
---|---|
'>= 2018-04-01' | that were updated on or after 2018-04-01. |
'< 2018-05-01' | that were updated before 2018-05-01 |
['and', '>= 2018-04-04', '< 2018-05-01'] | that were updated between 2018-04-01 and 2018-05-01. |
{# Fetch addresses updated in the last week #}
{% set lastWeek = date('1 week ago')|atom %}
{% set addresses = craft.queryFunction()
.dateUpdated(">= #{lastWeek}")
.all() %}
# fixedOrder
Causes the query results to be returned in the order specified by id.
If no IDs were passed to id, setting this to true
will result in an empty result set.
{# Fetch addresses in a specific order #}
{% set addresses = craft.queryFunction()
.id([1, 2, 3, 4, 5])
.fixedOrder()
.all() %}
# id
Narrows the query results based on the addresses’ IDs.
Possible values include:
Value | Fetches addresses… |
---|---|
1 | with an ID of 1. |
'not 1' | not with an ID of 1. |
[1, 2] | with an ID of 1 or 2. |
['not', 1, 2] | not with an ID of 1 or 2. |
This can be combined with fixedOrder if you want the results to be returned in a specific order.
# ignorePlaceholders
Causes the query to return matching addresses as they are stored in the database, ignoring matching placeholder elements that were set by craft\services\Elements::setPlaceholderElement() (opens new window).
# inReverse
Causes the query results to be returned in reverse order.
{# Fetch addresses in reverse #}
{% set addresses = craft.queryFunction()
.inReverse()
.all() %}
# limit
Determines the number of addresses that should be returned.
{# Fetch up to 10 addresses #}
{% set addresses = craft.queryFunction()
.limit(10)
.all() %}
# offset
Determines how many addresses should be skipped in the results.
{# Fetch all addresses except for the first 3 #}
{% set addresses = craft.queryFunction()
.offset(3)
.all() %}
# orderBy
Determines the order that the addresses should be returned in. (If empty, defaults to dateCreated DESC
.)
{# Fetch all addresses in order of date created #}
{% set addresses = craft.queryFunction()
.orderBy('dateCreated ASC')
.all() %}
# owner
Sets the ownerId parameter based on a given owner element.
{# Fetch addresses for the current user #}
{% set addresses = craft.queryFunction()
.owner(currentUser)
.all() %}
# ownerId
Narrows the query results based on the addresses’ owner elements, per their IDs.
Possible values include:
Value | Fetches addresses… |
---|---|
1 | created for an element with an ID of 1. |
'not 1' | not created for an element with an ID of 1. |
[1, 2] | created for an element with an ID of 1 or 2. |
['not', 1, 2] | not created for an element with an ID of 1 or 2. |
{# Fetch addresses created for an element with an ID of 1 #}
{% set addresses = craft.queryFunction()
.ownerId(1)
.all() %}
# preferSites
If unique() (opens new window) is set, this determines which site should be selected when querying multi-site elements.
For example, if element “Foo” exists in Site A and Site B, and element “Bar” exists in Site B and Site C,
and this is set to ['c', 'b', 'a']
, then Foo will be returned for Site B, and Bar will be returned
for Site C.
If this isn’t set, then preference goes to the current site.
{# Fetch unique addresses from Site A, or Site B if they don’t exist in Site A #}
{% set addresses = craft.queryFunction()
.site('*')
.unique()
.preferSites(['a', 'b'])
.all() %}
# prepareSubquery
Prepares the element query and returns its subquery (which determines what elements will be returned).
# relatedTo
Narrows the query results to only addresses that are related to certain other elements.
See Relations (opens new window) for a full explanation of how to work with this parameter.
{# Fetch all addresses that are related to myCategory #}
{% set addresses = craft.queryFunction()
.relatedTo(myCategory)
.all() %}
# search
Narrows the query results to only addresses that match a search query.
See Searching (opens new window) for a full explanation of how to work with this parameter.
{# Get the search query from the 'q' query string param #}
{% set searchQuery = craft.app.request.getQueryParam('q') %}
{# Fetch all addresses that match the search query #}
{% set addresses = craft.queryFunction()
.search(searchQuery)
.all() %}
# siteSettingsId
Narrows the query results based on the addresses’ IDs in the elements_sites
table.
Possible values include:
Value | Fetches addresses… |
---|---|
1 | with an elements_sites ID of 1. |
'not 1' | not with an elements_sites ID of 1. |
[1, 2] | with an elements_sites ID of 1 or 2. |
['not', 1, 2] | not with an elements_sites ID of 1 or 2. |
{# Fetch the address by its ID in the elements_sites table #}
{% set address = craft.queryFunction()
.siteSettingsId(1)
.one() %}
# trashed
Narrows the query results to only addresses that have been soft-deleted.
# uid
Narrows the query results based on the addresses’ UIDs.
{# Fetch the address by its UID #}
{% set address = craft.queryFunction()
.uid('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')
.one() %}
# with
Causes the query to return matching addresses eager-loaded with related elements.
See Eager-Loading Elements (opens new window) for a full explanation of how to work with this parameter.
{# Fetch addresses eager-loaded with the "Related" field’s relations #}
{% set addresses = craft.queryFunction()
.with(['related'])
.all() %}
# Working with Address Fields
# Field Handles
You can reference individual fields—native and custom—using their field handles like any other element:
<ul>
<li>Name: {{ myAddress.title }}</li>
<li>Postal Code: {{ myAddress.postalCode }}</li>
<li>Custom Label Color: {{ myAddress.myCustomColorFieldHandle }}</li>
</ul>
# Attribute Labels
The addressing library’s abstracted Administrative Area → Locality → Dependent Locality terminology probably isn’t what you actually call address bits in your part of the world, and it’s even less likely you’d want to show those terms to site visitors.
You can use any address element’s addressAttributeLabel()
method to get human-friendly labels for a given locale. Using a U.S. address…
{{ myAddress.attributeLabel('administrativeArea') }} {# State #}
{{ myAddress.attributeLabel('locality') }} {# City #}
{{ myAddress.attributeLabel('dependentLocality') }} {# Suburb #}
{{ myAddress.attributeLabel('postalCode') }} {# Zip Code #}
# |address
Formatter
You can use the |address
filter to get an address with its default HTML formatting:
{{ myAddress|address }}
{# Output:
<p translate="no">
<span class="address-line1">1234 Balboa Towers Circle</span><br>
<span class="locality">Los Angeles</span>, <span class="administrative-area">CA</span> <span class="postal-code">92662</span><br>
<span class="country">United States</span>
</p>
#}
The default formatter includes the following options:
- locale – defaults to
'en'
- html – defaults to
true
; disable withfalse
to maintain line breaks but omit HTML tags - html_tag – defaults to
p
- html_attributes – is an array that defaults to
['translate' => 'no']
{# Swap enclosing paragraph tag with a div #}
{{ myAddress|address({ html_tag: 'div' }) }}
{# Output:
<div translate="no">
<span class="address-line1">1234 Balboa Towers Circle</span><br>
<span class="locality">Los Angeles</span>, <span class="administrative-area">CA</span> <span class="postal-code">92662</span><br>
<span class="country">United States</span>
</div>
#}
{# Replace the default `translate` param with something whimsical and pointless #}
{{ myAddress|address({ html_attributes: { example: 'sure is!' }}) }}
{# Output:
<p example="sure is!">
<span class="address-line1">1234 Balboa Towers Circle</span><br>
<span class="locality">Los Angeles</span>, <span class="administrative-area">CA</span> <span class="postal-code">92662</span><br>
<span class="country">United States</span>
</p>
#}
{# Omit all HTML tags #}
{{ myAddress|address({ html: false }) }}
{# Output:
1234 Balboa Towers Circle
Los Angeles, CA 92662
United States
#}
{# Format with `uk` locale #}
{{ myAddress|address({ html: false, locale: 'uk' }) }}
{# Output:
1234 Balboa Towers Circle
Los Angeles, CA 92662
Сполучені Штати
#}
# Customizing the Formatter
You can also pass your own formatter to the |address
filter. The addressing library includes PostalLabelFormatter (opens new window) to make it easier to print shipping labels. Here, we can specify that formatter and set its additional origin_country
option:
{# Use the postal label formatter #}
{% set addressService = craft.app.getAddresses() %}
{% set labelFormatter = create(
'CommerceGuys\\Addressing\\Formatter\\PostalLabelFormatter',
[
addressService.getAddressFormatRepository(),
addressService.getCountryRepository(),
addressService.getSubdivisionRepository(),
]) %}
{{ addr|address({ origin_country: 'GB' }, labelFormatter) }}
{# Output:
1234 Balboa Towers Circle
LOS ANGELES, CA 92662
UNITED STATES
#}
You can also write a custom formatter that implements FormatterInterface (opens new window). We might extend the default formatter, for example, to add a hide_countries
option that avoids printing the names of specified countries:
<?php
namespace mynamespace;
use CommerceGuys\Addressing\AddressInterface;
use CommerceGuys\Addressing\Formatter\DefaultFormatter;
use CommerceGuys\Addressing\Locale;
class OptionalCountryFormatter extends DefaultFormatter
{
/**
* @inheritdoc
*/
protected $defaultOptions = [
'locale' => 'en',
'html' => true,
'html_tag' => 'p',
'html_attributes' => ['translate' => 'no'],
'hide_countries' => [],
];
/**
* @inheritdoc
*/
public function format(AddressInterface $address, array $options = []): string
{
$this->validateOptions($options);
$options = array_replace($this->defaultOptions, $options);
$countryCode = $address->getCountryCode();
$addressFormat = $this->addressFormatRepository->get($countryCode);
if (!in_array($countryCode, $options['hide_countries'])) {
if (Locale::matchCandidates($addressFormat->getLocale(), $address->getLocale())) {
$formatString = '%country' . "\n" . $addressFormat->getLocalFormat();
} else {
$formatString = $addressFormat->getFormat() . "\n" . '%country';
}
} else {
// If this is in our `hide_countries` list, omit the country
$formatString = $addressFormat->getFormat();
}
$view = $this->buildView($address, $addressFormat, $options);
$view = $this->renderView($view);
$replacements = [];
foreach ($view as $key => $element) {
$replacements['%' . $key] = $element;
}
$output = strtr($formatString, $replacements);
$output = $this->cleanupOutput($output);
if (!empty($options['html'])) {
$output = nl2br($output, false);
// Add the HTML wrapper element.
$attributes = $this->renderAttributes($options['html_attributes']);
$prefix = '<' . $options['html_tag'] . ' ' . $attributes . '>' . "\n";
$suffix = "\n" . '</' . $options['html_tag'] . '>';
$output = $prefix . $output . $suffix;
}
return $output;
}
}
We can instantiate and use that just like the postal label formatter:
{# Use our custom formatter #}
{% set addressService = craft.app.getAddresses() %}
{% set customFormatter = create(
'mynamespace\\OptionalCountryFormatter',
[
addressService.getAddressFormatRepository(),
addressService.getCountryRepository(),
addressService.getSubdivisionRepository(),
]
) %}
{{ addr|address({ hide_countries: ['US'] }, customFormatter) }}
{# Output:
1234 Balboa Towers Circle
Los Angeles, CA 92662
#}