3

This is sort of related to Store module: How to update cart quantity without submitting the cart? but I don't want to update that question b/c it is still relevant to other people. It turns out my issue is that our store uses price modifiers for the "quantity." So people order 1 item, but it might come in 500, 1000, 1500, or 2000. Instead of changing the item_qty field on the cart page, I need to be able to change a price modifier instead. This is what my template looks like thus far, and it sort of works, but instead of updating the existing item it just adds an entirely new item to the cart.

A simplified version of my cart template:

{exp:store:checkout next="cart/confirm" return="cart/ORDER_HASH"}
    {!-- This seems to be the only way to get the full list of available 
    modifiers in this tag :( --}
    {modifiers}
        {if modifier_name == "Quantity" OR modifier_name == "quantity"}
            {embed="cart/_product-modifiers" 
                entry_id="{entry_id}" 
                key="{key}" 
                option_id="{option_id}" 
                modifier_id="{modifier_id}"}
        {/if}
    {/modifiers}

    {!-- Quantity is determined by the modifier since its a fixed amount, so set to 1 
    here so the product is actually added to the cart --}                
    <input type="hidden" name="item_qty[{key}]" value="1" />

    <button type="submit" name="update_cart" class="item-quantity-update" value="Update Cart">Update Cart</button>
{/exp:store:checkout}

And the cart/_product-modifiers template to render this dropdown http://cl.ly/image/0e0Q0q391I0s:

{exp:store:product entry_id="{embed:entry_id}"}

    {modifiers}
        <select name="item[{embed:key}][modifiers_{embed:modifier_id}]">
            {modifier_options}
                <option value="{option_id}" {if "{embed:option_id}" == "{option_id}"}selected="selected"{/if}>{option_name}</option>
            {/modifier_options}
        </select>
    {/modifiers}

{/exp:store:product}
  • Is the price linearly related to the quantity, or are there discounts for higher quantities? e.g. 250 = $25, 500 = $50. If so, you could simply template item_qty as a select which would make your life much easier. – Adrian Macneil Jan 31 '13 at 4:10
3

Based on Adrian's suggestion, and my thoughts before seeing his response I used one of Store's hooks to modify the product modifiers when the cart is updated. Its also crucial to update the product sku and the mod_values array. When those are updated in the store_cart_update_start hook it will trigger Store to swap the item entirely (b/c each modifier is a separate sku) and update all the prices accordingly.

This is what the embed code was changed to:

{exp:store:product 
    entry_id="{embed:entry_id}" 
    disable_form="yes" 
    disable_javascript="yes"}

    {modifiers}
        {if modifier_name == "Quantity" OR modifier_name == "quantity"}
            <select name="update_modifier[{embed:key}][{embed:modifier_id}]">
                {modifier_options}
                    <option value="{option_id}__{option_name}__{option_sku}" {if "{embed:option_id}" == "{option_id}"}selected="selected"{/if}>
                        {option_name}{if price_mod_val} ({price_mod}) {/if}
                    </option>
                {/modifier_options}
            </select>
        {/if}
    {/modifiers}

{/exp:store:product}

Notice that the select name is a custom value I made up and has nothing to do with Store. The hook used to modify it is as follows:

public function store_cart_update_start($cart, $update_data)
{
    // Only if submitted via the cart page. 
    // This hook is called when an item is added to the cart as well... we don't
    // want to do anything to the item at that point.
    if ( ! ($updates = $this->EE->input->post('update_modifier')))
    {
        return $cart;
    }

    foreach ($updates as $update_item_key => $update_data)
    {
        foreach ($cart['items'] as $item_key => $item)
        {
            if ($item_key == $update_item_key)
            {
                foreach ($item['modifiers'] as $modifier_key => $modifier_data)
                {
                    foreach ($modifier_data as $name => $value)
                    {
                        if ($name == 'modifier_id' AND isset($update_data[$value]))
                        {
                            $parts = explode('__', $update_data[$value]);

                            $option_id = $parts[0];
                            $modifier_value = $parts[1];
                            $new_sku = isset($parts[2]) ? $parts[2] : $item['sku'];

                            $cart['items'][$item_key]['sku'] = $new_sku;
                            $cart['items'][$item_key]['modifiers'][$modifier_key]['option_id'] = $option_id;
                            $cart['items'][$item_key]['modifiers'][$modifier_key]['modifier_value'] = $modifier_value;

                            $modifier_id = $cart['items'][$item_key]['modifiers'][$modifier_key]['modifier_id'];
                            $cart['items'][$item_key]['mod_values'][$modifier_id] = $option_id;
                        }
                    }
                }
            }
        }
    }

    return $cart;
}

This hook gets called when an item is added to the cart, and when an update_cart submit button is clicked. This hook updates the cart['items'] array that is saved in the cart with the new values.

  • Awesome! Thanks for posting your solution here, I'm sure it will help other people. – Adrian Macneil Jan 31 '13 at 16:29
2

As mentioned in my comment, if your prices are linearly related to your quantity, then it would be much easier to template the item_qty as a select menu:

<select name="item_qty">
    <option value="500">500</option>
    <option value="500">1000</option>
    <option value="500">2000</option>
</select>

That way, everything would work as Store expects (updating the quantity would change the price, and adding new items would increment the existing quantity in the cart).

The underlying issue you are running into here is that Store treats any products with different modifiers as separate line items in your cart (for example if you add a Red Shirt and Blue Shirt to your cart, it would not make sense to merge them into a single line item on the invoice). That is why when you try to update the "modifier", it adds a new line item to the cart.

There is currently no way to update items once they are in the cart, except the quantity (other than going directly to the database). Therefore if you really need customers to change item modifiers once they reach the checkout stage, you have two options:

  • Write an extension with a custom form action URL, which edits the cart directly in the database (this is easier than it sounds, you can use our cart library to recalculate everything once you change the modifiers), or
  • Remove the existing product and add a new product. This would require some javascript to make two sequential form submissions seem like a single step to the customer, because the checkout form and product form have different action URLs.
  • The issue is I can't just do a global hard-coded quantity. Depending on the product it might come in different quantities at different prices. My next approach is a custom hook to update the cart like you mentioned. – Brian Litzinger Jan 31 '13 at 14:32

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.