Bundles and slots
A bundle
A bundle is a purchasable element. It has a title, a SKU, a price, a field layout, optionally a URL — everything a product has — and no inventory of its own. That last part is the whole idea: a bundle's stock is whatever its contents can supply, and its fulfilment is whatever its contents have shipped.
A slot
A slot is one position in the box: a pool of purchasables that may go there, and how many.
| Setting | What it does |
|---|---|
| Name | What the shopper sees above the choice |
| Handle | How templates address it: selection[mug] |
| Choices come from | A list you pick, or a query (Pro) |
| Quantity | Fixed, or a range the shopper chooses (Pro) |
| How many | Fixed mode: units this slot always contributes |
| Fewest / Most | Range mode: the bounds the shopper picks within |
| Optional (Pro) | Fixed mode: let them leave this slot out |
| Allow repeats (Pro) | Range mode: may one option be taken more than once |
| Discount | Percentage off everything in this slot |
| Show prices | Whether templates show component prices for this slot |
The two shapes
Every bundle anyone builds is one of these, or a mix:
A hamper. Slots with a pool of one and a fixed quantity. "This box always contains a mug, a bag of tea and a card." Nothing is chosen; the shopper buys it or does not.
A build-your-own box. One slot with a pool of twenty and a range of 0–6, plus a bundle-wide minimum and maximum. "Pick any six."
They are the same object because they are the same question asked with different bounds, which is why there is no separate "mix and match" bundle type to choose between at the start.
Per-option settings
Within a list slot, each choice can carry:
- Default qty — how many are pre-selected when the shopper first sees the bundle
- Discount — overrides the slot's discount for this one option. An explicit
0is how you say "everything in this slot is 20% off except the expensive one". Leaving it empty means "use the slot's", which is not the same thing.
Query pools (Pro)
Instead of listing choices, a slot can be filled from a query: variants of one or more product types, optionally narrowed to those related to an element, optionally only those in stock.
The pool is resolved at read time, so adding a product to the catalogue adds it to every box that queries for it — no re-saving bundles.
Bundle-wide limits (Pro)
Fewest items and Most items apply across every slot together. A box with three range slots of 0–6 and a bundle maximum of 6 lets the shopper distribute six units however they like.
Present refuses to save a bundle whose slots cannot between them reach its own minimum, because that bundle can never be configured at all and the shopper would only find out at the add-to-cart button.
What cannot go in a bundle
Another bundle. Nesting looks reasonable and is not: the stock rollup would have to recurse, the fulfilment rollup would have to nest, and Commerce would still see one flat list of line items at the end of it. Present refuses outright rather than half-supporting it.
Auditing
php craft present/bundles/check
Lists every bundle, how many boxes can be built, what is limiting it, and anything wrong — a slot
with nothing available in it, a slot pointing at variants that have since been disabled, a bundle
with no slots, a minimum that cannot be reached. --problems-only shows just the bad ones.