Building a member area on Core dna: who a member is in the data, the sign-in flows and their templates, single sign-on and passkeys, the my-account routing convention, members-only content, sessions and the page cache, and the security checks to run before launch.
L2
Unit 01 · The identity model
Addresses and the address book
The identity modelPage 5 of 36Updated
A person keeps an address book: any number of addresses, each with an address type and one marked primary. The model, the address types, the screens a member manages it with, rendering it in the member area, and how orders and checkouts use a saved address without linking to it.
Who this is for. The theme developer building the address screens, and the commerce implementer pre-filling checkout from them. Afterwards you know what an address book entry holds, how to render and edit it, and why editing it never changes an old order.
The model
Each entry in a person's address book is a person address with three parts:
Part
Holds
In a template
The address
First and last name, company, unit, address lines 1 and 2, suburb, city, postcode, state, country, phone, and any custom fields defined for addresses
getAddress(), then getCity(), getPostcode(), getStateName(), getCountryName() and the rest
The address type
A record with a name, such as "Home" or "Office deliveries", and a kind: billing, shipping or other
getType(), then getName() and getType()
Primary
Whether this is the person's main address
isPrimary()
A person has at most one primary address across all types: saving a second is refused with "Only one address can be set as primary". Address types are managed under Users › Address Types; a site can have several of each kind. Address types are permission-controlled records, so members need read on them for the type list on the add form to fill.
Shows the add form with countries and types; saves a new address
Redirects to the list with "Address Added"
GET /my-account/address/{id}
Shows one address in an edit form, as personAddress
PUT /my-account/address/{id}
Saves changes
Redirects to the address with "Address Updated"
DELETE /my-account/address/{id}
Deletes it
Redirects to the list with "Address Deleted"
The form posts type (the address type's id), primary (1 or 0) and the address as address[firstName], address[addressLine1], address[city], address[postcode], address[country] (a country id from countries), address[stateName] and so on, with the CSRF token; PUT and DELETE travel as a POST with _method. A failed add re-renders the form with HTTP 422 and the field errors; a failed edit or delete redirects with the error as a message, so print the site's messages on these pages. Write your own list template before offering the screen, rather than the framework's (Which screens need a template from you).
A Twig site extends its own Twig account menu (The account menu). The profile route can change addresses too, since Primary address and Addresses are among its changeable properties by default (The profile route); most sites use the address screens and leave the profile form to names and preferences.
Orders and checkouts
Orders and checkouts do not link to the address book. Each holds its own billing and delivery address, so editing or deleting a saved address never changes an order already placed, and an order's addresses stay as they were at the time.
To save a member typing, pre-fill the checkout's address fields from the address book: read the entry you want in the checkout template and print its values into the fields. The checkout's own validation then applies as usual (Checkout):
{% set saved = null %}
{% if _currentUser.isLoggedIn() %}
{% for entry in _currentUser.getPerson().getPersonAddresses() %}
{% if entry.isPrimary() %}{% set saved = entry.getAddress() %}{% endif %}
{% endfor %}
{% endif %}
<input name="billingAddress[addressLine1]" value="{{ saved ? saved.getAddressLine1()|e : '' }}">
To offer a choice, list the entries of the right kind, billing or shipping, by getType()->getType(), and fill the fields from the one the member picks with a few lines of script. Whatever is pre-filled is printed for one member, so the checkout page must not be cached; checkout routes already prevent caching themselves.