Model
Extending the Model
config/shopper/models.php:
Database Schema
ProductVariant Table
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint | no | auto | Primary key |
name | string | no | - | Variant name (indexed) |
sku | string | yes | null | Stock Keeping Unit (unique) |
barcode | string | yes | null | Barcode (unique) |
ean | string | yes | null | European Article Number (unique) |
upc | string | yes | null | Universal Product Code (unique) |
allow_backorder | boolean | no | false | Allow ordering when out of stock |
position | integer | no | 1 | Display order |
product_id | bigint | no | - | FK to product |
weight_unit | string | no | kg | Weight unit enum |
weight_value | decimal(10,2) | yes | null | Weight value |
height_unit | string | no | cm | Height unit enum |
height_value | decimal(10,2) | yes | null | Height value |
width_unit | string | no | cm | Width unit enum |
width_value | decimal(10,2) | yes | null | Width value |
depth_unit | string | no | cm | Depth unit enum |
depth_value | decimal(10,2) | yes | null | Depth value |
volume_unit | string | no | l | Volume unit enum |
volume_value | decimal(10,2) | yes | null | Volume value |
metadata | json | yes | null | Additional custom data |
created_at | timestamp | yes | null | Creation timestamp |
updated_at | timestamp | yes | null | Last update timestamp |
Attribute Value Variant Table (Pivot)
| Column | Type | Nullable | Description |
|---|---|---|---|
id | bigint | no | Primary key |
value_id | bigint | no | FK to attribute value |
variant_id | bigint | no | FK to variant |
Dimension Enums
Weight
Length (Height, Width, Depth)
Volume
Relationships
Product
Attribute Values
Stock Management
Variants use theHasStock trait for inventory:
Avoiding N+1 Queries
When displaying a product page with multiple variants, accessing$variant->stock on each variant
triggers an individual query. Use loadCurrentStock() to batch-load stock in a single query:
$variant->stock without prior batch-loading throws a
LazyStockLoadingException with a clear message telling you to use loadCurrentStock().
For more details on the batch-loading pattern, see the Products Stock Management section.
Pricing
Variants use theHasPrices trait:
Media Management
Variants use theHasMedia trait for images:
Creating Variants
Basic Variant
Variant with Dimensions
Variant with Attribute Values
Generate Variant Name from Attributes
Retrieving Variants
Observer Behavior
TheProductVariantObserver handles cleanup when deleting:
Working with Products
Variant Matrix Generation
Shopper provides built-in tools to generate all possible variant combinations from product attributes.Using SaveProductVariantsAction
Generate Permutations with Arr::permutate
MapProductOptions Helper
For products with existing attribute assignments, useMapProductOptions:
Components
config/shopper/components/product.php:
Storefront Example
Use Cases
| Scenario | Example |
|---|---|
| Color variants | T-shirt in Red, Blue, Green |
| Size variants | Shoes in sizes 8, 9, 10, 11 |
| Combined variants | T-shirt in Red/Small, Red/Medium, Blue/Small, etc. |
| Material variants | Bag in Leather, Canvas, Nylon |
| Trait | Purpose |
|---|---|
HasStock | Inventory management |
HasPrices | Currency-specific pricing |
HasMedia | Variant-specific images |
HasDimensions | Shipping calculations |