Product Form Tag

{exp:store:product_form}
  {exp:channel:entries channel="products" dynamic="no" limit="5"}
      {exp:store:product disable_form="yes" entry_id="{entry_id}"}
          <!-- add to cart form / product details -->
    {exp:store:product}
  {/exp:channel:entries}
{/exp:store:product_form}

The product form tag is used to create custom add to cart forms, which allow the customer to add multiple items to their cart at once. Most websites will not need to use this tag, as the Product Tag already provides an add to cart form wrapped around a single product.

Product Form Tag Parameters

return=”path”

This parameter is optional. It will redirect the user to the specified page after the form is submitted - such as the Checkout page. If no redirect is specified, the form will display the current page (useful if you just wish to update a sidebar cart)

empty_cart=”yes”

This empties the current cart before adding the selected product. Useful for single-product websites, or donations.

form_id=”value”

Set an id attribute on the generated HTML form.

form_name=”value”

Set a name attribute on the generated HTML form.

form_class=”value”

Set a class attribute on the generated HTML form.

html:attribute=”value”

Specify arbitrary attributes on the generated HTML form. For example:

html:parsley-validate="true"

input:custom_modifier_name=”Name”

The input parameter can be used to allow custom product modifiers on any product in your form. This behaves the same way it does in the Product Tag, except that it applies to all products in the form.

Product Form Tag Inputs

The product form tag does not have any variables. Instead, it is intended to be wrapped around a channel entries tag, or multiple product tags. Because the product form supports adding multiple products to the cart, there are some differences in the form tag layout.

A standard simple product tag may look like this:

{exp:channel:entries channel="products"}
  {exp:store:product entry_id="{entry_id}"}
    <input type="text" name="item_qty" value="1" />
    <input type="submit" value="Add to Cart" />
  {/exp:store:product}
{/exp:channel:entries}

To turn this into an add to cart form with multiple products, we need to wrap it with a product form tag. We also need to disable the form provided by the product tag, because HTML forms can not be nested.

{exp:store:product_form}
  {exp:channel:entries channel="products"}
    {exp:store:product entry_id="{entry_id}" disable_form="yes"}
      <input type="hidden" name="items[{count}][entry_id]" value="{entry_id}" />
      <input type="text" name="items[{count}][item_qty]" value="1" />
    {/exp:store:product}
  {/exp:channel:entries}
  <input type="submit" value="Add to Cart" />
{/exp:store:product_form}

Note that we changed the form input name, and also added a hidden entry_id input. This creates an array which tells the product form how many of each product to add to the cart. In this case, we have used the {count} variable provided by the channel entries tag to index the array of products, but this could be any number, as long as it consistent between the entry_id and item_qty inputs.

The same input name format can be applied to product modifiers. For example:

{if modifier_type == 'var' OR modifier_type == 'var_single_sku'}
  <select name="items[{count}][{modifier_input_name}]">
    {modifier_options}
      <option value="{option_id}">{option_name}</option>
    {/modifier_options}
  </select>
{if:else}
  <input type="text" name="items[{count}][{modifier_input_name}]" value="" />
{/if}

To better understand this concept, take a look at the generated HTML when three products are created using a channel entries loop:

<div>
  <input type="hidden" name="items[1][entry_id]" value="23" />
  <input type="text" name="items[1][item_qty]" size="3" value="" />
</div>
<div>
  <input type="hidden" name="items[2][entry_id]" value="24" />
  <input type="text" name="items[2][item_qty]" size="3" value="" />
</div>
<div>
  <input type="hidden" name="items[3][entry_id]" value="25" />
  <input type="text" name="items[3][item_qty]" size="3" value="" />
</div>

In each iteration of the channel entries loop, the {count} variable has been used to create a unique form input name, while still relating each entry_id field to an item_qty field.

Product Form Tag Example

{exp:store:product_form return="store/checkout"}

  {exp:channel:entries channel="products"}
    {exp:store:product entry_id="{entry_id}" disable_form="yes"}

      <p>{price}</p>

      {modifiers}
        <p>
          <label>{modifier_name}</label><br />
          {if modifier_instructions}<small>{modifier_instructions}</small><br />{/if}
          {if modifier_type == 'var' OR modifier_type == 'var_single_sku'}
            <select name="items[{count}][{modifier_input_name}]">
              {modifier_options}
                <option value="{option_id}">
                  {option_name}
                  {if price_mod_val} ({price_mod}) {/if}
                </option>
              {/modifier_options}
            </select>
          {if:else}
            <input type="text" name="items[{count}][{modifier_input_name}]" />
          {/if}
        </p>
      {/modifiers}

      <p>
        <label>Quantity</label><br />
        <input type="hidden" name="items[{count}][entry_id]" value="{entry_id}" />
        <input type="text" name="items[{count}][item_qty]" size="3" value="1" />
      </p>

    {/exp:store:product}
  {/exp:channel:entries}

  <p>
    <input type="submit" name="submit" value="Add to Cart" />
  </p>

{/exp:store:product_form}