boxcart.dev

Database Schema

BoxCart creates 11 custom database tables to store locations, time slots, orders, baskets, and product quantity types. This reference documents every column, type, and index.

Overview

All custom tables are prefixed with {$wpdb->prefix}boxcart_ (e.g. wp_boxcart_locations on a default WordPress install). They are defined in BoxCart_Database (includes/core/class-boxcart-database.php) and created automatically on plugin activation via BoxCart_Activator::activate().

The 11 custom tables fall into four groups:

GroupTables
Locationsboxcart_locations, boxcart_location_schedules, boxcart_location_slots, boxcart_location_closures, boxcart_location_capacity_overrides
Ordersboxcart_orders, boxcart_order_items, boxcart_order_status_log
Basketsboxcart_baskets, boxcart_basket_items
Productsboxcart_product_quantities

In addition to the custom tables, BoxCart uses the WordPress boxcart_product custom post type, the boxcart_product_category taxonomy, and several user meta keys.

boxcart_locations

Stores collection location details including address, contact information, and display ordering.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
namevarchar(255)NOT NULL
address_line_1varchar(255)DEFAULT NULL
address_line_2varchar(255)DEFAULT NULL
cityvarchar(100)DEFAULT NULL
countyvarchar(100)DEFAULT NULL
postcodevarchar(20)DEFAULT NULL
countryvarchar(2)DEFAULT 'GB'
phonevarchar(32)DEFAULT NULL
emailvarchar(100)DEFAULT NULL
instructionstextDEFAULT NULL
is_activetinyint(1)NOT NULL DEFAULT 1
sort_orderint(11)NOT NULL DEFAULT 0
created_atdatetimeNOT NULL
updated_atdatetimeNOT NULL

Keys: PRIMARY (id), KEY is_active, KEY sort_order

boxcart_location_schedules

Defines which days of the week each location is open. One row per location per day (0 = Sunday, 6 = Saturday).

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
location_idbigint(20) unsignedNOT NULL
day_of_weektinyint(1)NOT NULL
is_opentinyint(1)NOT NULL DEFAULT 0

Keys: PRIMARY (id), UNIQUE KEY location_day (location_id, day_of_week), KEY location_id

boxcart_location_slots

Collection time slots per location per day of week. Each slot has a start/end time and an order capacity limit.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
location_idbigint(20) unsignedNOT NULL
day_of_weektinyint(1)NOT NULL
time_starttimeNOT NULL
time_endtimeNOT NULL
capacityint(11)NOT NULL DEFAULT 10
is_activetinyint(1)NOT NULL DEFAULT 1

Keys: PRIMARY (id), KEY location_day (location_id, day_of_week), KEY is_active

boxcart_location_closures

Ad-hoc closure dates for locations (e.g. bank holidays, staff training). Orders cannot be placed for a closed date.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
location_idbigint(20) unsignedNOT NULL
closure_datedateNOT NULL
reasonvarchar(255)DEFAULT NULL

Keys: PRIMARY (id), UNIQUE KEY location_date (location_id, closure_date), KEY location_id, KEY closure_date

boxcart_location_capacity_overrides

Allows overriding the default slot capacity for a specific date (e.g. increasing capacity on a busy day or reducing it for a short-staffed day).

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
location_idbigint(20) unsignedNOT NULL
slot_idbigint(20) unsignedNOT NULL
override_datedateNOT NULL
capacityint(11)NOT NULL

Keys: PRIMARY (id), UNIQUE KEY slot_date (slot_id, override_date), KEY location_id, KEY slot_id

boxcart_orders

The main orders table. Stores customer details, collection slot selection, order totals, payment information, and timestamps.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
order_numbervarchar(32)NOT NULL
customer_idbigint(20) unsignedDEFAULT NULL
customer_emailvarchar(100)NOT NULL
customer_first_namevarchar(100)NOT NULL
customer_last_namevarchar(100)NOT NULL
customer_phonevarchar(32)DEFAULT NULL
delivery_typevarchar(20)NOT NULL DEFAULT 'collection'
delivery_address_1varchar(255)DEFAULT NULL
delivery_address_2varchar(255)DEFAULT NULL
delivery_cityvarchar(100)DEFAULT NULL
delivery_postcodevarchar(20)DEFAULT NULL
delivery_instructionstextDEFAULT NULL
delivery_feedecimal(10,2)DEFAULT NULL
location_idbigint(20) unsignedDEFAULT NULL
slot_datedateDEFAULT NULL
slot_time_starttimeDEFAULT NULL
slot_time_endtimeDEFAULT NULL
statusvarchar(32)NOT NULL DEFAULT 'pending'
item_countint(11)NOT NULL DEFAULT 0
subtotaldecimal(10,2)DEFAULT NULL
discount_amountdecimal(10,2)DEFAULT NULL
tax_amountdecimal(10,2)DEFAULT NULL
totaldecimal(10,2)DEFAULT NULL
payment_methodvarchar(32)DEFAULT NULL
payment_statusvarchar(32)DEFAULT 'not_required'
transaction_idvarchar(255)DEFAULT NULL
customer_notestextDEFAULT NULL
admin_notestextDEFAULT NULL
ip_addressvarchar(45)DEFAULT NULL
user_agentvarchar(255)DEFAULT NULL
created_atdatetimeNOT NULL
updated_atdatetimeNOT NULL

Keys: PRIMARY (id), UNIQUE KEY order_number, KEY customer_id, KEY customer_email, KEY location_id, KEY slot_date, KEY status, KEY payment_status, KEY created_at, KEY delivery_type

Migration note

Several columns on this table (delivery_*, discount_amount, tax_amount, payment_method, payment_status, transaction_id) are added via migration methods rather than the initial CREATE TABLE statement, because dbDelta does not reliably add columns to existing tables. See BoxCart_Database::ensure_order_columns() and ensure_payment_columns().

boxcart_order_items

Line items for each order. Product name and SKU are stored as snapshots so the order record is preserved even if the product is later deleted.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
order_idbigint(20) unsignedNOT NULL
product_idbigint(20) unsignedNOT NULL
product_namevarchar(255)NOT NULL
product_skuvarchar(100)DEFAULT NULL
quantity_type_idbigint(20) unsignedDEFAULT NULL
unit_labelvarchar(50)DEFAULT NULL
price_per_unitdecimal(10,2)DEFAULT NULL
quantitydecimal(10,3)NOT NULL DEFAULT 1
pricedecimal(10,2)DEFAULT NULL
line_totaldecimal(10,2)DEFAULT NULL

Keys: PRIMARY (id), KEY order_id, KEY product_id

boxcart_order_status_log

Audit trail for order status changes. Records who changed the status and an optional note.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
order_idbigint(20) unsignedNOT NULL
old_statusvarchar(32)DEFAULT NULL
new_statusvarchar(32)NOT NULL
changed_bybigint(20) unsignedDEFAULT NULL
notetextDEFAULT NULL
created_atdatetimeNOT NULL

Keys: PRIMARY (id), KEY order_id, KEY created_at

boxcart_baskets

Session-based shopping baskets. Supports both guest and logged-in users. Expired baskets are cleaned up by the boxcart_cleanup_expired_baskets cron job (runs twice daily).

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
session_idvarchar(64)NOT NULL
user_idbigint(20) unsignedDEFAULT NULL
selected_location_idbigint(20) unsignedDEFAULT NULL
selected_slot_datedateDEFAULT NULL
selected_slot_time_starttimeDEFAULT NULL
selected_slot_time_endtimeDEFAULT NULL
slot_selected_atdatetimeDEFAULT NULL
slot_reservation_expires_atdatetimeDEFAULT NULL
created_atdatetimeNOT NULL
updated_atdatetimeNOT NULL
expires_atdatetimeNOT NULL

Keys: PRIMARY (id), UNIQUE KEY session_id, KEY user_id, KEY expires_at, KEY selected_location_id

Migration note

The slot selection columns (selected_location_id, selected_slot_date, selected_slot_time_start, selected_slot_time_end, slot_selected_at, slot_reservation_expires_at) are added via BoxCart_Database::ensure_slot_columns().

boxcart_basket_items

Individual items within a basket. The unique key on (basket_id, product_id, quantity_type_id) ensures a product with the same quantity type only appears once per basket — adding it again increments the quantity.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
basket_idbigint(20) unsignedNOT NULL
product_idbigint(20) unsignedNOT NULL
quantity_type_idbigint(20) unsignedDEFAULT NULL
quantitydecimal(10,3)NOT NULL DEFAULT 1
added_atdatetimeNOT NULL

Keys: PRIMARY (id), UNIQUE KEY basket_product_qty_type (basket_id, product_id, quantity_type_id), KEY basket_id, KEY product_id

Migration note

The quantity_type_id column and the change of quantity from int to decimal(10,3) were added via BoxCart_Database::ensure_quantity_type_columns().

boxcart_product_quantities

Defines quantity types for products (e.g. "per kg", "per bunch", "each"). A single product can have multiple quantity types, each with its own price per unit, step increment, and min/max constraints.

ColumnTypeAttributes
idbigint(20) unsignedNOT NULL AUTO_INCREMENT, PRIMARY KEY
product_idbigint(20) unsignedNOT NULL
unit_labelvarchar(50)NOT NULL
unit_label_pluralvarchar(50)DEFAULT NULL
display_labelvarchar(20)DEFAULT NULL
price_prefixvarchar(20)DEFAULT 'per'
price_per_unitdecimal(10,2)NOT NULL
allow_decimalstinyint(1)NOT NULL DEFAULT 0
step_incrementdecimal(10,3)NOT NULL DEFAULT 1
min_quantitydecimal(10,3)NOT NULL DEFAULT 1
max_quantitydecimal(10,3)DEFAULT NULL
display_orderint(11)NOT NULL DEFAULT 0
created_atdatetimeNOT NULL

Keys: PRIMARY (id), KEY product_id, KEY display_order

Migration note

The display_label column was added via BoxCart_Database::ensure_display_label_column() and price_prefix via ensure_price_prefix_column().

Database Migrations

BoxCart uses migration methods in BoxCart_Database to safely add columns to existing tables. These run on activation and are tracked via the boxcart_columns_ensured option.

MethodPurpose
ensure_slot_columns()Adds slot selection columns to the baskets table
ensure_order_columns()Adds delivery and discount columns to the orders table
ensure_quantity_type_columns()Adds quantity type support to basket_items and order_items; changes quantity from int to decimal
ensure_display_label_column()Adds display_label to product_quantities
ensure_price_prefix_column()Adds price_prefix to product_quantities
ensure_payment_columns()Adds payment_method, payment_status, transaction_id to orders
fix_inconsistent_quantity_settings()One-time data fix for legacy quantity data

Custom Post Type

Products are stored as the boxcart_product custom post type. The CPT is not publicly queryable — products are displayed exclusively through BoxCart shortcodes and blocks.

PropertyValue
Post Typeboxcart_product
Publicfalse
Show UIfalse
Publicly Queryablefalse
Has Archivefalse
Supportstitle, editor, thumbnail

Each product stores additional data in post meta:

Meta KeyPurpose
_boxcart_skuProduct SKU
_boxcart_priceProduct price
_boxcart_stock_statusStock status (in_stock or out_of_stock)
_boxcart_sort_orderDisplay sort order

Custom Taxonomy

Product categories use the boxcart_product_category custom taxonomy, attached to the boxcart_product post type.

PropertyValue
Taxonomyboxcart_product_category
Attached Toboxcart_product
Hierarchicaltrue
Publicfalse
Show UIfalse

Term Meta: _boxcart_sort_order — controls category display order.

User Meta

BoxCart stores customer-specific data in the WordPress usermeta table:

Meta KeyPurpose
boxcart_phoneCustomer phone number
boxcart_favouritesSerialised array of favourite product IDs
boxcart_billing_*Billing address fields

Entity Relationships

The diagram below describes how the custom tables relate to each other. Foreign keys are enforced at the application level, not by database constraints.

Location group

Order group

Basket group

Product group