Skip to main content
Collections group products together for storefront categories, promotions, and other purposes.

Overview

Collections provide a flexible way to group products together for any purpose: storefront categories, hero sliders, landing page features, seasonal promotions, or any other grouping a store requires. Products can be added to a collection explicitly or filtered by criteria, and collections can be nested to form a hierarchy. Each collection belongs to a collection group, allowing different sets of collections to be maintained independently for different parts of a storefront.

Collection Groups

Collection groups act as top-level containers for collections. A store might have separate groups for a main catalog, seasonal promotions, or landing page features.
Lunar\Models\CollectionGroup

Fields

FieldTypeDescription
idbigIncrementsPrimary key
namestringThe group name
handlestringUnique slug identifier
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
collectionsHasManyLunar\Models\CollectionAll collections in this group

Creating a collection group

use Lunar\Models\CollectionGroup;

$group = CollectionGroup::create([
    'name' => 'Main Catalogue',
    'handle' => 'main-catalogue',
]);

Collections

Lunar\Models\Collection

Fields

FieldTypeDescription
idbigIncrementsPrimary key
collection_group_idforeignIdThe parent collection group
_lftunsignedIntegerRead-only nested set left pointer
_rgtunsignedIntegerRead-only nested set right pointer
parent_idunsignedInteger nullableParent collection for tree hierarchy
typestringDefault: static
attribute_datajsonTranslatable attributes (e.g. name, description)
sortstringProduct sort method, default: custom
created_attimestamp
updated_attimestamp
deleted_attimestamp nullableSoft deletes

Relationships

RelationshipTypeRelated ModelDescription
groupBelongsToLunar\Models\CollectionGroupThe parent collection group
productsBelongsToManyLunar\Models\ProductProducts in this collection; pivot: position
customerGroupsBelongsToManyLunar\Models\CustomerGroupPivot: visible, enabled, starts_at, ends_at
channelsMorphToManyLunar\Models\ChannelPivot: enabled, starts_at, ends_at
discountsBelongsToManyLunar\Models\DiscountPivot: type (limitation or assignment)
brandsBelongsToManyLunar\Models\Brand
urlsMorphManyLunar\Models\Url
childrenHasManyLunar\Models\CollectionDirect child collections (from NodeTrait)
ancestorsAncestorsRelationLunar\Models\CollectionAll parent collections up the tree (from NodeTrait)
descendantsDescendantsRelationLunar\Models\CollectionAll children recursively (from NodeTrait)

Scopes

ScopeDescription
inGroup(int $id)Filter by collection group ID
channel($channel, $startsAt, $endsAt)Filter by channel availability with optional scheduling
customerGroup($group, $startsAt, $endsAt)Filter by customer group availability with optional scheduling

Creating a collection

use Lunar\Models\Collection;
use Lunar\FieldTypes\Text;

$collection = Collection::create([
    'collection_group_id' => $group->id,
    'attribute_data' => [
        'name' => new Text('Clearance'),
    ],
]);
Lunar internally expects a name attribute on collection attribute data. It must be present in the attributes, otherwise the admin panel may throw unexpected errors.

Nested Collections

Collections form a tree hierarchy using the kalnoy/nestedset package. Child collections can be added using the appendNode method:
use Lunar\Models\Collection;

$parent = Collection::create([
    'collection_group_id' => $group->id,
    'attribute_data' => [
        'name' => new \Lunar\FieldTypes\Text('Clothing'),
    ],
]);

$child = Collection::create([
    'collection_group_id' => $group->id,
    'attribute_data' => [
        'name' => new \Lunar\FieldTypes\Text('T-Shirts'),
    ],
]);

$parent->appendNode($child);
This produces the following hierarchy:
- Clothing
    - T-Shirts
At least one root (parent) collection must exist before child collections can be created. This is important when using seeders to set up the initial catalog state.
The breadcrumb accessor returns the translated names of all ancestor collections, which is useful for building breadcrumb navigation:
$collection->breadcrumb; // Illuminate\Support\Collection of ancestor names

Querying the tree

The nested set package provides many methods for working with the tree. Here are some common examples:
// Get all root collections
Collection::whereIsRoot()->get();

// Get direct children
$collection->children;

// Get all descendants (recursive)
$collection->descendants;

// Get ancestors
$collection->ancestors;

// Get siblings
$collection->getSiblings();

// Get depth in tree (requires withDepth() scope)
Collection::withDepth()->find($collection->id)->depth;
Refer to the kalnoy/nestedset documentation for the full API.

Products

Products are associated using a BelongsToMany relationship with a position pivot column for ordering.

Adding products

$collection->products()->sync([
    $productA->id => ['position' => 1],
    $productB->id => ['position' => 2],
]);

Sorting products

The sort field on a collection determines how products are ordered. Lunar ships with the following sort options:
SortDescription
min_price:ascSort by base price ascending
min_price:descSort by base price descending
sku:ascSort by SKU ascending
sku:descSort by SKU descending
customManually specify the order of each product via the position pivot
When a collection is updated, Lunar automatically dispatches the UpdateProductPositions job to reorder products based on the current sort setting.

Channels

Collections use the HasChannels trait, enabling channel-based availability with optional scheduling. See Channels for full details.
use Lunar\Models\Channel;

$channel = Channel::first();

// Enable for a channel immediately
$collection->scheduleChannel($channel);

// Schedule availability with a start and end date
$collection->scheduleChannel($channel, now()->addDays(7), now()->addDays(30));

Querying by channel

use Lunar\Models\Collection;

Collection::channel($channel)->get();

Collection::channel($channel, now()->addDay(), now()->addDays(7))->get();

Customer Groups

Collections use the HasCustomerGroups trait, enabling customer group visibility with optional scheduling. See Customers for full details.
use Lunar\Models\CustomerGroup;

$group = CustomerGroup::first();

// Enable for a customer group immediately
$collection->scheduleCustomerGroup($group);

// Schedule with a date range
$collection->scheduleCustomerGroup($group, starts: now()->addDays(7), ends: now()->addDays(30));

// Disable for a customer group
$collection->unscheduleCustomerGroup($group);

Querying by customer group

use Lunar\Models\Collection;

Collection::customerGroup($group)->get();

Collection::customerGroup([$groupA, $groupB])->get();

URLs

Collections use the HasUrls trait, which automatically generates URLs when a collection is created.
// Get the default URL
$collection->defaultUrl;

// Get all URLs
$collection->urls;
See URLs for full details on managing URLs.

Media

Collections use the HasMedia trait (via Spatie Media Library), allowing images and other media to be attached.
$collection->addMedia($pathToFile)->toMediaCollection('images');

// Get the thumbnail URL
$collection->getThumbnailImage();

Deletion Behavior

Collections use soft deletes. When a collection is deleted, all related pivot data is automatically detached:
  • Products
  • Channels
  • URLs (hard deleted)
  • Customer groups
  • Discounts