Overview
Products are generally what you will be selling in your store. You define all your attributes against the product and products can also have variations. In Lunar a product will always have at least one variation. From a UX point of view, it will just look like you’re editing a single product, but behind the scenes you’ll be editing one variant. Products also belong to aProductType and aside from the attributes, which you are free to define yourself, will have
a base SKU and a brand name.
Creating a product
Customer Groups
You can assign customer groups to a product, this allows you to either always have that product enabled for the customer group, or you can state which dates they should be active for (as long as the customer group is enabled).Attaching customer groups
Retrieving products for a customer group
If you want to get all products related to a customer group, you can use thecustomerGroup scope. This scope can
either take a single customer group (or customer group id) or a collection/array of ids/customer group models.
Product Types
e.g. Television, T-Shirt, Book, Phone, etc. Assigns the appropriate attributes for the product type.Creating a product type
Screen Type
associated to a TVs product type, any products with that product type would have access to that attribute when
editing.
You can associate attributes to a product type like so (it’s just a straight
forward Polymorphic relationship).
Product and ProductVariant attributes to a product type which will then display on either the
product or product variant when editing.
If you decide to delete an attribute, this will cause the association to be dropped and you could lose data.
Retrieving the product type relationship
If you have a product, you can fetch its product type like so:Product Identifiers
You can choose to add product identifiers to each product variant. These are fields which, as the name suggests, allow you to identify a product and its variants for use in your internal systems. You can choose whether these are required and unique in the hub whilst editing.Available fields
SKU SKU stands for “stock keeping unit” and — as the name suggests — it is a number (usually eight alphanumeric digits) that you can assign to products to keep track of stock levels internally. If a product has different colors and sizes, each variation may use a unique SKU number. GTIN A Global Trade Item Number (GTIN) is a unique and internationally recognized identifier for a product. These usually accompany a barcode and are useful when using services such as Google to help them classify the product. MPN MPN (Manufacturer Part Number) is the product identifier used to differentiate a product among other (similar) products from the same brand/manufacturer, making it easier for customers to find and buy your products and protecting them from counterfeit. EAN European Article numbering code (EAN) is a series of letters and numbers in a unique order that helps identify specific products within your own inventory.Validation
Depending on your storefront needs, you might not need any of these fields to be required or unique. For this reason you can change this behaviour at a validation level.config/lunar-hub/products.php
Product Options
These are what you use to define the different options a product has available to it. These are directly related to the different variants a product might have. EachProductOption will have a set of ProductOptionValue models related to
it. For example:
You could have a ProductOption called “Colour” and then multiple ProductionOptionValue models for each colour you
would like to offer.
Product options and Product option values are defined at a system level and are translatable.
Creating a ProductOption
Product Shipping
By default Lunar will mark all product variants asshippable. If you don’t need to ship a certain variant then you can
simply set this to false.
Product dimensions
Product’s can have different dimensions assigned to them, the values we have available are- Length
 - Width
 - Height
 - Weight
 - Volume
 
_value and _unit column in the database. For example:
Configuring measurements
You can configure the available UOM’s in thelunar/shipping.php config file. Here is an example of what Lunar provides
by default:
Length
- m
 - mm
 - cm
 - ft
 - in
 
- kg
 - g
 - lbs
 
- l
 - ml
 - gal
 - floz
 
Getting and converting measurement values
You are free to access to the*_value and *_unit values for the variant and use them in their raw form, however we
do offer an accessor for each unit that you can use:
Volume calculation
Volume is calculated different to the rest of the measurements. You can either have it automatically figure out the volume or manually set it yourself:Variants
Variants allow you to specify different variations of a product. Think things like Small Blue T-shirt, or Size 8 and Size 9 Leather Boots. Your product is the main parent and your variants are based off that product to create multiple permutations. Variants are also responsible for storing data such as Pricing, Inventory/Stock information, Shipping information etc. For that reason a product will always have at least one variant. When you decide you want to offer more than one variant for a product, upon generation, Lunar will take the first variant and use that as a base for all other variants in terms of pricing, inventory etc. So you won’t lose any data you may have already saved against the existing product.Creating variants
New Product
If you have a new product, you would create your variants in bulk based on an array ofProductOptionValue IDs.
You will need to determine what variants you need to create and assign the correct option values to that variant.
For example, lets say we have an option “Colour” and we want to create “Blue” and “Red” variants.
A product variant will require a product, currency and a tax class.
If you do not have these entities created, you will need to do so before continuing.
Exceptions
When creating variants there are some exceptions that will be thrown if certain conditions are met.| Exception | Conditions | 
|---|---|
Illuminate\Validation\ValidationException | Thrown if validation fails on the value options array. | 
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. This casting gives you some useful helpers when dealing with prices on your
front end.
| Field | Description | Default | Required | 
|---|---|---|---|
price | A integer value for the price | null | yes | 
compare_price | For display purposes, allows you to show a comparison price, e.g. RRP. | null | no | 
currency_id | The ID of the related currency | null | yes | 
min_quantity | The minimum quantity required to get this price. | 1 | no | 
customer_group_id | The customer group this price relates to, leaving as null means any customer group | null | no | 
priceable_type | This is the class reference to the related model which owns the price | null | yes | 
priceable_id | This is the id of the related model which owns the price | null | yes | 
Price formatting
For the full reference on how to format prices for your storefront see the Pricing ReferenceThe same methods apply to the compare_price attribute
Base Pricing
Pricing is defined on a variant level, meaning you will have a different price for each variant and also for each currency in the system. In order to add pricing to a variant, you can either create the model directly or use the relationship method.Customer group pricing
You can specify which customer group the price applies to by setting thecustomer_group_id column. If left as null
the price will apply to all customer groups. This is useful if you want to have different pricing for certain customer
groups and also different price quantity breaks per customer group.
Price Break Pricing
Price Break pricing is a concept in which when you buy in bulk, the cost per item will change (usually go down). With Pricing on Lunar, this is determined by themin_quantity column when creating prices. For example:
1.99 per item. But if you order at least 10 you
will pay 1.50 per item.
Fetching the price
Once you’ve got your pricing all set up, you’re likely going to want to display it on your storefront. We’ve created aPricingManager which is available via a facade to make this process as painless as possible.
To get the pricing for a product you can simply use the following helpers:
Minimum example
A quantity of 1 is implied when not passed.With Quantities
With Customer Groups
If you don’t pass in a customer group, Lunar will use the default, including any pricing that isn’t specific to a customer group.Specific to a user
The PricingManager assumes you want the price for the current authenticated user. If you want to always return the guest price, you may use…With a specific currency
If you don’t pass in a currency, the default is implied.For a model
Assuming you have a model that implements thehasPrices trait, such as a ProductVariant, you can use the following
to retrieve pricing.
If you try and fetch a price for a currency that doesn’t exist, a 
 Lunar\Exceptions\MissingCurrencyPriceException
exception will be thrown.This will return a
PricingResponse object which you can interact with to display the correct prices. Unless it’s a
collection, each property will return a Lunar\Models\Price object.
prices relationship on the product.
Price models.
Storing Prices Inclusive of Tax
Lunar allows you to store pricing inclusive of tax if you need to. This is helpful if you need to show charm pricing, at $9.99 for example, which may not be possible if pricing is stored exclusive of tax due to rounding. To start you will need to set thestored_inclusive_of_tax config value in lunar/pricing to true. Then you will
need to ensure your default Tax Zone is set up correctly with the correct tax rates.
Once set, the cart will automatically calculate the tax for you.
If you need to show both ex. and inc. tax pricing on your product pages, you can use the following methods which are
available on the Lunar\Models\Price model.
Customising Prices with Pipelines
All pipelines are defined inconfig/lunar/pricing.php
Pipelines will run from top to bottom
Full Example
For this example, we’re going to be creating some Dr. Martens boots. Below is a screenshot of what we’re aiming for:
- Create our product type
 - Create the initial product
 - Create the product options and their values
 - Generate the variants based on those values
 
Set up the product type.
This example assumes we already have Attributes set up for name and description and that they’re assigned to the product
type.
Create the initial product
Product Options
Based on the example above we’re going to need 2 options, Size and Colour.Product Option Values
From here we now need to create our option values like so:Generate the variants
First we just need to grab the values we want to use to generate the variants. Since we’re generating them for everything, we just grab all of them.When generating variants, the sku will be derived from the Product’s base SKU, in this case 
DRBOOT and will be
suffixed with -{count}.| SKU | Colour | Size | 
|---|---|---|
| DRBOOT-1 | Black | 3 | 
| DRBOOT-2 | Black | 6 | 
| DRBOOT-3 | White | 3 | 
| DRBOOT-4 | White | 6 | 
| DRBOOT-5 | Pale Pink | 3 | 
| DRBOOT-6 | Pale Pink | 6 | 
| DRBOOT-7 | Mid Blue | 3 | 
| DRBOOT-8 | Mid Blue | 6 |