Skip to main content
Products are the core catalog model in Lunar, representing items available for sale with variants, pricing, and options.

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 a ProductType, which determines which attributes are available during editing. Products can also optionally belong to a Brand.

Fields

Relationships

Scopes

Creating a Product

Lunar internally expects and uses the name attribute in product attribute data. It must be included in the product type’s attributes and populated; otherwise the admin panel may throw unexpected errors.

Filtering by status

Channels

Products support multi-channel availability through the HasChannels 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

The customerGroup 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

Product types have Attributes associated to them. These associated attributes determine which fields are available to products when editing. For example, an attribute of 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:
Both Product and ProductVariant attributes can be associated to a product type, and each will display on the corresponding model when editing.
Deleting an attribute will drop the association and could result in data loss.

Retrieving the product type relationship

Product Options

Product options define the different variations available for a product. Each ProductOption has a set of ProductOptionValue models. For example, a ProductOption called “Color” could have values like “Blue”, “Red”, and “Green”.
Product options and product option values are defined at a system level and are translatable.

Creating a ProductOption

Values can then be created for the option:
This product option and its values are now ready to be used with product variants.

Product Option Meta

Both ProductOption 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.
Create the product option and its values:
Create the variants and attach their option values:
Then create pricing for each variant:

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
For handling unit conversions, Lunar uses the Cartalyst Converter package, which supports a wide range of units of measure. Each dimension has a corresponding _value and _unit column in the database:

Configuring measurements

Available units of measure can be configured in the lunar/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:
Formatted values

Pricing

Overview

Prices are stored in the database as integers. When retrieving a Price model, the price and compare_price attributes are cast to a Price datatype with useful helpers for display.
For full details on price formatting, see the Pricing Reference.
The same formatting methods apply to the compare_price attribute.

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 the customer_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. The min_quantity column determines when each price tier applies:
In the above example, ordering 1–9 items costs 1.99 per item, while ordering 10 or more costs 1.50 per item.

Fetching Prices

The PricingManager 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

The PricingManager 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 the HasPrices trait (such as ProductVariant) exposes a pricing() method:
Fetching a price for a currency that has no pricing defined will throw a Lunar\Exceptions\MissingCurrencyPriceException.

The get() method returns a PricingResponse object. Unless noted as a collection, each property returns a Lunar\Models\Price object.
To retrieve all prices across a product’s variants without loading the variants individually, use the 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 the stored_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 in config/lunar/pricing.php:
Custom pipelines can modify pricing during resolution:
Pipelines run from top to bottom.

Full Example

This example walks through creating a pair of Dr. Martens boots with multiple size and color variants. The steps involved are:
  • 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.
The resulting variants: SKUs, pricing, and other variant details can be adjusted as needed before publishing.