I want to try and stop Commerce 3 creating a new customer address if it’s a duplicate of an existing one.

As an example, a store selling digital products has a checkout form that simply asks for the customer’s country, no other address info is required. All we need is one select field for the country (pre-checked if we already have that info). We could add radio buttons to select any previously saved addresses as well as a field to select a (new) country. However, even if we do that, there’s still a chance the user opts to enter a new address and actually submits a duplicate.

I’ve created a plugin that catches a duplicate address and then tries to set the cart’s billingAddressId so a new address isn’t created. What I have so far though doesn’t work. It doesn’t successfully update the billingAddressId. If I use Craft::$app->getElements()->saveElement($cart) it results in a loop in this case.

How can I complete this to update the cart, or perhaps the post data in this case, with the matched billingAddressId?

Event::on(Address::class, Address::EVENT_BEFORE_VALIDATE, function (Event $e) {

    $sentCountryId = $e->sender->countryId;
    $currentUser = Craft::$app->user;
    $cart = craft\commerce\Plugin::getInstance()->getCarts()->getCart();
    $addresses = $cart->customer->addresses;
    $match = 0;

    // Only check against saved addresses if currentUser has any
    if ($currentUser and $addresses) {

        // If true, loop saved address to check for a country match
        foreach($addresses as $address) {

            // Loop until a match is found
            if ($match == 0) {

                $savedCountryId = $address->countryId;

                if ($sentCountryId == $savedCountryId) {
                    $match = $address->id;



        // If there’s a match set the cart’s billingAddressId
        // to stop a new address being created:
        if ($match) {

            $cart->billingAddressId = $match; // This doesn’t work on it’s own
            // Craft::$app->getElements()->saveElement($cart); // Results in a loop
            $e->handled = true;




If I am reading you right you are wanting to update the shipping address on the order rather than create a new one.

You should be able to do that right now:

{% set cart = craft.commerce.carts.cart %}

{% set flashNotice = craft.app.session.getFlash('notice') %}

{% if flashNotice %}
    <div class="bg-blue-500 text-white">
        <div class="flash container mx-auto px-6 py-4">
            {{ flashNotice }}
{% endif %}

{% set flashError = craft.app.session.getFlash('error') %}

{% if flashError %}
<div class="bg-red-500 text-white">
  <div class="flash container mx-auto px-6 py-4">
    {{ flashError }}
{% endif %}

//meat and potatoes:

{{ cart.number }}<br>

Shipping Address ID: {{ cart.shippingAddressId }}

<form method="post">
  <input type="hidden" name="action" value="commerce/cart/update-cart"/>
  <input type="hidden" name="cartUpdatedNotice" value="The address was updated.">
  {{ redirectInput('shop/address') }}
  {{ csrfInput() }}

  {% if cart.shippingAddress.id %}
  <input type="hidden" name="shippingAddress[id]" value="{{cart.shippingAddress.id}}"/>
  {% endif %}

  First Name:
  <input name="shippingAddress[firstName]" value="{{cart.shippingAddress.firstName ?? ''}}"/>

  <button type="submit">Update</button>

As demonstrated above, using an input with name="shippingAddress[id]" with the address ID set as the value will update the address with that ID on the commerce/cart/update-cart controller action just fine.

You would only use name="shippingAddressId" if you wanted to let the user select an existing one.

  • Let me know if I am not understanding. – Luke Holder Apr 8 '20 at 11:31
  • Thanks Luke. Appreciate it’s perhaps not that common but there’s a situation (at least in our checkout process) where a duplicate address is more likely. For example, in our simple form rather than display previous addresses and give the option to add a new one. It just asks for the billingAddress[countryId]. That’s it. So that form opens up the likelihood of duplicates if they’ve checked out before, or have to go back a step in the process and submit the same form twice. Therefore, I’m trying to check against existing addresses on the plugin side. – James Apr 8 '20 at 12:11
  • Also, less likely to happen but even with radio buttons to select a saved address, they could still opt to enter a new one and input a duplicate. Or maybe I’m missing something? – James Apr 8 '20 at 12:13
  • I’ve updated the question to hopefully make it clearer. I still think there’s a need to catch duplicates. – James Apr 9 '20 at 10:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.