# Single Product Flow

> How the single-product demo template connects current-product dynamic data, variable selectors, quantity, add-to-cart, and buy-now behavior.

<!-- Sources: src/Testing/E2E/Woo/WooDemoStoreBuilder.php; src/Testing/E2E/Woo/WooScenarioProvider.php; src/Patterns/Testing/TestSingleWooProductTemplate/TestSingleWooProductTemplate.php; src/Patterns/Testing/TestSingleWooProductTemplate/TestSingleWooProductTemplate.css; src/Woo/ProductDynamicData.php; src/Components/Woo/AddToCartForm/AddToCartForm.php; src/Components/Woo/AddToCartButton/AddToCartButton.php; src/Components/Woo/AttributeSelector/AttributeSelector.php; src/Components/Woo/BuyNowButton/BuyNowButton.php; client/src/domains/woo/helpers/variation-sync.ts; tests/e2e/components/woo/behavior.spec.ts -->

# Single Product Flow

The single product flow is the current-product version of the purchase family. `WooDemoStoreBuilder` registers a single product template from `TestSingleWooProductTemplate`; the template reads from `{this.*}` instead of loop-item bindings.

## Flow Structure

```text
Single product template
  CartCount
  CartNotices
  Product image from this.omewoo.media
  Product title and price
  AddToCartForm
    AttributeSelector loop over this.omewoo.attributes
    AddToCartButton in counter mode
    BuyNowButton
```

The current product is the WordPress queried object. That makes `{this.id}`, `{this.title}`, and `{this.omewoo.*}` the correct bindings.

## Product Data Bindings

| Binding | Meaning |
| --- | --- |
| `{this.id}` | Current product ID. |
| `{this.title}` | Current product title. |
| `{this.omewoo.media.image.url}` | Main product image URL. |
| `{this.omewoo.media.image.alt}` | Main product image alt text. |
| `{this.omewoo.price.text}` | Formatted product price text. |
| `{this.omewoo.product.type}` | Product type passed into the form. |
| `{this.omewoo.attributes}` | Attribute objects used by variable product selectors. |

Use `{this.*}` only when the template is actually rendered in a product context. In loops, use `{item.*}` instead.

## Variable Product Form

The demo single-product template builds an `AddToCartForm` with:

| Form value | Source |
| --- | --- |
| `product_id` | `{this.id}` |
| `product_type` | `{this.omewoo.product.type}` |
| `variation_id` | Empty by default; selectors resolve selected variation state. |
| `available` / `unavailable` / `stock_status` | Product availability values passed into runtime state. |

`AttributeSelector` loops over `this.omewoo.attributes` and uses radio mode in the demo template. The runtime variation sync helper keeps the selected option values, availability, and final variation ID aligned with the parent form.

## Quantity And Submit

The demo uses `AddToCartButton` in counter mode. The button renders quantity controls and submits the parent form through `trigger=form_submit`. Quantity values are clamped to the authored min, max, and step before the Store API request is sent.

The `product_single_simple_add` scenario verifies this directly by entering a value above the max, then asserting the input value is clamped before the cart count changes.

## Buy Now

`BuyNowButton` belongs inside `AddToCartForm`. It serializes the same form payload as the add-to-cart submit path, sends the Store API add request, and redirects to the authored checkout URL or the localized Woo checkout URL.

Do not use `BuyNowButton` as a standalone checkout link. Its behavior depends on the parent form state, especially for variable products.

## E2E Coverage

| Scenario | Verified behavior |
| --- | --- |
| `product_single_simple_add` | Quantity bounds are enforced before add-to-cart. |
| `variable_product_requires_variation` | Missing required variation keeps the form invalid. |
| `variation_selector_button_radio` | Button-radio selectors set available options and reject unavailable options. |
| `buy_now_redirect` | Buy-now sends the add request and redirects. |
| `out_of_stock_product_disabled` | Product availability disables the form submit path. |
