Overview
Products represent the items available for sale in a store. All custom attributes are defined against the product, and products can have multiple variations. In Lunar, a product always has at least one variant. When a product has only a single variant, the editing experience appears as though the product itself is being edited directly, but behind the scenes the data lives on the variant. Every product belongs to aProductType, which determines which attributes are available during editing. Products can also optionally belong to a Brand.
Fields
Relationships
Scopes
Creating a Product
Filtering by status
Channels
Products support multi-channel availability through theHasChannels trait. When a product is created, all channels are automatically synced. Each channel can be independently enabled or disabled, with optional start and end dates for scheduled availability.
Scheduling a channel
Filtering by channel
Customer Groups
Products can be assigned to customer groups with optional scheduling. This controls whether the product is visible and purchasable for members of each group.Scheduling a customer group
Filtering by customer group
ThecustomerGroup scope accepts a single customer group (or ID), or a collection/array of customer groups or IDs.
Product Types
Product types categorize products and determine which attributes are available during editing (e.g. Television, T-Shirt, Book, Phone).Fields
Relationships
Creating a product type
Screen Type associated to a TVs product type would make that field available on any product with that type.
Attributes can be associated using a standard polymorphic relationship:
Product and ProductVariant attributes can be associated to a product type, and each will display on the corresponding model when editing.
Retrieving the product type relationship
Product Options
Product options define the different variations available for a product. EachProductOption has a set of ProductOptionValue models. For example, a ProductOption called “Color” could have values like “Blue”, “Red”, and “Green”.
Creating a ProductOption
Product Option Meta
BothProductOption and ProductOptionValue models include a meta field for storing custom information such as color hex values, image links, or other display data.
Lunar makes no assumptions about the structure of the meta JSON field. Any values can be stored in whatever format the application requires.
Product Associations
Products can be associated with other products as cross-sells, up-sells, or alternates. See the Associations reference for full details on creating and managing product associations.Variants
Variants represent the different purchasable permutations of a product, such as “Small Blue T-shirt” or “Size 9 Leather Boots”. The product acts as the parent, and variants hold the specific data including pricing, inventory, shipping information, and product identifiers. A product always has at least one variant. When additional variants are generated, Lunar uses the first variant as a baseline for pricing, inventory, and other data.Fields
Product Identifiers
Each variant can store product identifiers for use in internal systems or external services. SKU (Stock Keeping Unit) — A code (usually eight alphanumeric digits) used to track stock levels internally. Each variant of a product typically has a unique SKU. GTIN (Global Trade Item Number) — An internationally recognized product identifier, often accompanying a barcode. Useful with services like Google Shopping to help classify products. MPN (Manufacturer Part Number) — An identifier from the manufacturer that differentiates a product among similar items from the same brand. EAN (European Article Number) — A series of characters that identifies specific products within an inventory system.Creating Variants
A product variant requires a product, currency (for pricing), and a tax class.Exceptions
Shipping
By default, all product variants are marked as shippable. To mark a variant as non-shippable:Dimensions
Products can store dimension data on each variant. The available dimensions are:- Length
- Width
- Height
- Weight
- Volume
_value and _unit column in the database:
Configuring measurements
Available units of measure can be configured in thelunar/shipping.php config file. The defaults include:
Length: m, mm, cm, ft, in
Weight: kg, g, lbs
Volume: l, ml, gal, floz
Getting and converting measurement values
The raw*_value and *_unit values can be accessed directly, but Lunar also provides an accessor for each dimension that supports conversion:
Volume calculation
Volume can be calculated automatically from the length, width, and height dimensions, or set manually:Pricing
Overview
Prices are stored in the database as integers. When retrieving aPrice model, the price and compare_price attributes are cast to a Price datatype with useful helpers for display.
Base Pricing
Pricing is defined at the variant level, meaning each variant has its own price for each currency. Prices can be created directly or through the relationship:Customer Group Pricing
Setting thecustomer_group_id column controls which customer group a price applies to. When left as null, the price applies to all customer groups. This allows different pricing and quantity breaks per customer group.
Price Breaks
Price breaks adjust the unit price based on purchase quantity. Themin_quantity column determines when each price tier applies:
Fetching Prices
ThePricingManager facade provides a fluent API for retrieving the correct price based on various criteria.
Minimum example
A quantity of 1 is implied when not passed.With quantities
With customer groups
If no customer group is passed, Lunar uses the default group and includes pricing that is not specific to any group.For a specific user
ThePricingManager assumes the current authenticated user by default.
With a specific currency
If no currency is passed, the default currency is used.From a model
Any model using theHasPrices trait (such as ProductVariant) exposes a pricing() method:
The
get() method returns a PricingResponse object. Unless noted as a collection, each property returns a Lunar\Models\Price object.
prices relationship on the product:
Storing Prices Inclusive of Tax
Lunar supports storing pricing inclusive of tax, which is useful for charm pricing (e.g. $9.99) that may not be achievable when storing prices exclusive of tax due to rounding. To enable this, set thestored_inclusive_of_tax config value in lunar/pricing to true and ensure the default tax zone is configured with the correct tax rates. The cart will then automatically calculate tax correctly.
To display both tax-inclusive and tax-exclusive prices on product pages:
Customizing Prices with Pipelines
Pricing pipelines are defined inconfig/lunar/pricing.php:
Full Example
This example walks through creating a pair of Dr. Martens boots with multiple size and color variants.
- Create the product type
- Create the initial product
- Create product options and their values
- Create the variants
Set up the product type
This example assumes attributes for name and description already exist and are assigned to the product type.
Create the initial product
Create product options
Based on the example above, two options are needed: Size and Color.Create product option values
Create the variants
With the options and values defined, variants can be created for each combination. Each variant needs a product, tax class, SKU, and at least one price.
SKUs, pricing, and other variant details can be adjusted as needed before publishing.