Understanding the Drupal Commerce 2.x Address Book

Developer Desk

Drupal Commerce 2.x includes address book functionality for both customers (from their account pages and checkout form) and administrators (from order edit pages). This article provides a quick summary of the address book architecture to help you understand how customer addresses are modeled / saved to your database and what you need to take into consideration when writing custom code / data migration processes for orders and customer profiles.

Commerce Core uses our Address module to add address fields to various entities, including stores and customer profiles. Address fields are not added directly to orders, payment methods, or shipments - the things for which we might typically expect an address to be selected from an address book. Those entities instead reference profiles with addresses that represent billing and shipping addresses.

As a result, a user’s address book is basically just a collection of profiles with the same uid as the user. These profiles are created in various ways:

  1. By the user through the address book UI on the user account page. In this case, the profiles are immediately saved and accessible to users in subsequent checkouts. For each profile type configured to support multiple profiles, one will always be considered the default. The default designation can be changed by the customer in the address book UI.
  2. By the user through the checkout form via the `copy_to_address_book` checkbox in the relevant checkout panes. In this case, when the order is placed, the order module’s `AddressBookSubscriber` creates copies of the relevant profiles.
  3. By an administrator on a customer’s behalf through an order edit form via the `copy_to_address_book` checkbox in the relevant fieldsets. In this case, when the profile is saved, the copy is created immediately.

Understanding what's in your database

Upon checkout completion, multiple profiles will exist per order. These include:

  1. A shipping profile if the shipping module is enabled; this will have a uid of 0 and be referenced by a Shipment entity.
  2. A payment method billing profile, e.g. the credit card address; this will have a uid of 0 and be referenced by a Payment method entity.
  3. An order billing profile, typically copied from the payment method; this will have a uid of 0 and be referenced by the Order entity.
  4. Copies of the shipping and / or billing profiles as instructed by the user; only possible if the user is logged in, these will have the same uid as the user.

What this logic ensures is that shipments, payment methods, and orders will always maintain the address and other profile information provided at the time of order entry. The copies made to the address book permit the customer to edit or delete address book entries at will without worrying about previous order data being lost.

Impact on custom code / data migration

What’s important to note is that profiles assigned to shipments, payment methods, and orders must have a uid of 0. They are functionally “owned” by those entities, and there is code in the preSave() methods on the related entity classes to ensure the profiles they reference have a uid of 0. This means if you write custom code that references a profile in a user’s address book from one of these entities and save the entity, the profile will no longer appear in the user’s address book.

Additionally, this requires a bit more complexity in your data migration process. You must ensure that address book profiles are created and that any imported orders have the requisite copies of profiles created for them.

Add new comment