Cart Field System
The cart field system is the foundation of EverShop's checkout logic. Every piece of data on a cart — from the subtotal and tax amount to the shipping address and coupon code — is defined as a field with its own calculation logic. Extensions can add new fields to inject custom data or business logic into the cart.
How Cart Fields Work
Each cart field is an object with three properties:
{
key: string; // Field name (e.g., 'sub_total', 'tax_amount')
resolvers: Function[]; // Array of functions that calculate the field's value
dependencies?: string[]; // Other fields this field depends on
}
When the cart is loaded or updated, EverShop:
- Sorts fields by their dependencies (topological sort).
- For each field, runs its resolvers in order.
- The final resolver's return value becomes the field's value.
Field Resolvers
Resolvers are functions that calculate a field's value. Inside a resolver, this gives you access to the cart (or cart item) data:
{
key: 'sub_total',
resolvers: [
async function resolver() {
// Access cart data via 'this'
const items = this.getItems();
let subTotal = 0;
for (const item of items) {
subTotal += item.getData('final_price') * item.getData('qty');
}
return subTotal;
}
],
dependencies: ['items'] // Depends on items being calculated first
}
Context Methods Available in Resolvers
Inside a cart field resolver, this provides:
| Method | Description |
|---|---|
this.getData(key) | Get a field's current value |
this.setData(key, value) | Set a field's value |
this.getItems() | Get all cart items |
this.setError(field, message) | Set a validation error on a field |
this.getTriggeredField() | Get which field triggered the recalculation |
Inside a cart item field resolver, this provides:
| Method | Description |
|---|---|
this.getData(key) | Get the item field's current value |
this.setData(key, value) | Set an item field's value |
this.setError(field, message) | Set a validation error |
this.getProduct() | Get the product data for this item |
Adding Custom Cart Fields
Register custom cart fields in your extension's bootstrap.ts using the cartFields processor:
import { addProcessor } from '@evershop/evershop/lib/util/registry';
export default () => {
addProcessor('cartFields', (fields) => {
return fields.concat([
{
key: 'gift_message',
resolvers: [
async function resolver() {
// Return existing value or empty string
return this.getData('gift_message') || '';
}
]
},
{
key: 'gift_wrap_fee',
resolvers: [
async function resolver() {
const hasGiftWrap = this.getData('gift_wrap');
return hasGiftWrap ? 5.00 : 0;
}
],
dependencies: ['sub_total'] // Calculate after subtotal
}
]);
});
};
Adding Custom Cart Item Fields
Similarly, use the cartItemFields processor:
import { addProcessor } from '@evershop/evershop/lib/util/registry';
export default () => {
addProcessor('cartItemFields', (fields) => {
return fields.concat([
{
key: 'personalization_text',
resolvers: [
async function resolver() {
return this.getData('personalization_text') || '';
}
]
}
]);
});
};
Field Dependencies
The dependencies array ensures fields are calculated in the correct order. If field B depends on field A, field A is always calculated first:
// Calculated first
{ key: 'sub_total', resolvers: [...] }
// Calculated second (depends on sub_total)
{ key: 'discount_amount', resolvers: [...], dependencies: ['sub_total'] }
// Calculated third (depends on both)
{ key: 'grand_total', resolvers: [...], dependencies: ['sub_total', 'discount_amount'] }
Circular dependencies (A depends on B, B depends on A) will cause an error during cart calculation.
Built-in Cart Fields
EverShop registers these cart fields by default:
| Field | Description |
|---|---|
cart_id | Database ID |
uuid | Unique cart identifier |
currency | Cart currency from config |
customer_email | Customer email address |
sub_total | Sum of all item prices |
sub_total_incl_tax | Subtotal including tax |
tax_amount | Calculated tax |
discount_amount | Applied discount |
grand_total | Final total |
shipping_fee_excl_tax | Shipping cost before tax |
shipping_fee_incl_tax | Shipping cost after tax |
shipping_method | Selected shipping method code |
payment_method | Selected payment method code |
coupon | Applied coupon code |
total_qty | Total quantity of items |
total_weight | Total weight of items |
Built-in Cart Item Fields
| Field | Description |
|---|---|
cart_item_id | Database ID |
uuid | Unique item identifier |
product_id | Product reference |
product_sku | Product SKU |
product_name | Product display name |
qty | Quantity (validated against stock) |
product_price | Unit price |
final_price | Price after discounts |
tax_percent | Tax percentage |
tax_amount | Item tax amount |
discount_amount | Item discount |
total | Line total (qty * final_price) |
Stock Validation
Cart item fields automatically validate quantities against product inventory:
- If
manage_stockis enabled andqtyexceeds available stock, an error is set on the item. - If the product is out of stock, an error is set immediately.
- These errors are tracked per-item and can be queried.
See Also
- Registry and Processors — How field registration works
- Extension Development — Creating extensions
- Payment Method Development — Custom payment methods
Support us
EverShop is an open-source project that relies on community support. If you find our project useful, please consider sponsoring us.