Customers represent the buyers in a store, kept separate from the application’s User model.
Overview
Lunar uses a dedicated Customer model to store customer details, rather than the User model. This keeps the application’s User models untouched and provides greater flexibility.
Customers
| Field | Type | Description |
|---|
id | | primary key |
title | string nullable | Salutation e.g. Mr, Mrs, Miss |
first_name | string | |
last_name | string | |
company_name | string nullable | |
tax_identifier | string nullable | |
account_ref | string nullable | |
attribute_data | json nullable | |
meta | json nullable | |
created_at | timestamp | |
updated_at | timestamp | |
Creating a customer
Lunar\Models\Customer::create([
'title' => 'Mr.',
'first_name' => 'Tony',
'last_name' => 'Stark',
'company_name' => 'Stark Enterprises',
'tax_identifier' => 'GB123456789',
'meta' => [
'account_no' => 'TNYSTRK1234'
],
]);
Accessors
The Customer model provides a full_name accessor that combines the title, first_name, and last_name fields.
$customer = Lunar\Models\Customer::create([
'title' => 'Mr.',
'first_name' => 'Tony',
'last_name' => 'Stark',
]);
$customer->full_name; // "Mr. Tony Stark"
Relationships
| Relationship | Type | Related Model | Description |
|---|
customerGroups | BelongsToMany | Lunar\Models\CustomerGroup | Pivot: customer_customer_group |
users | BelongsToMany | Configured via auth.providers.users.model | Pivot: customer_user |
discounts | BelongsToMany | Lunar\Models\Discount | Pivot: customer_discount |
addresses | HasMany | Lunar\Models\Address | |
orders | HasMany | Lunar\Models\Order | |
mappedAttributes | MorphToMany | Lunar\Models\Attribute | |
Users
Customers are typically associated with a user so that they can place orders. It is also possible to have multiple users associated with a single customer. This is useful in B2B e-commerce where a customer may have multiple buyers.
Attaching users to a customer
$customer = \Lunar\Models\Customer::create([/* ... */]);
$customer->users()->attach($user);
$customer->users()->sync([$userA->id, $userB->id, $userC->id]);
Attaching a customer to a customer group
$customer = \Lunar\Models\Customer::create([/* ... */]);
$customer->customerGroups()->attach($customerGroup);
$customer->customerGroups()->sync([$groupA->id, $groupB->id]);
Customer Groups
Default retail
Customer groups allow customers to be organized into logical segments, enabling different criteria to be defined on models based on which group a customer belongs to.
These criteria include:
Pricing
Different pricing can be specified per customer group. For example, customers in the trade customer group may have different prices than those in retail.
Product Availability
Product visibility can be toggled depending on the customer group, meaning only certain products will show depending on the group a customer belongs to. Scheduling availability is also supported, allowing products to be released earlier or later to different groups.
At least one customer group must exist in the store. When Lunar is installed, a default group named retail is created.
Creating a customer group
$customerGroup = Lunar\Models\CustomerGroup::create([
'name' => 'Retail',
'handle' => 'retail', // Must be unique
'default' => false,
]);
Only one default customer group can exist at a time. If a new customer group is created with default set to true, the existing default will be set to false.
Scheduling availability
To add customer group availability to custom models, the HasCustomerGroups trait can be used.
// ...
use Lunar\Base\Traits\HasCustomerGroups;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class MyModel extends Model
{
use HasCustomerGroups;
public function customerGroups(): BelongsToMany
{
$prefix = config('lunar.database.table_prefix');
return $this->belongsToMany(
\Lunar\Models\CustomerGroup::class,
"{$prefix}customer_group_my_model"
)->withTimestamps()->withPivot(['enabled', 'visible', 'starts_at', 'ends_at']);
}
}
This provides access to the following methods:
Scheduling customer groups
// Schedule with a specific start and end date.
$myModel->scheduleCustomerGroup(
$customerGroup,
starts: now()->addDays(14),
ends: now()->addDays(28),
);
// Schedule the model to be enabled immediately (starts_at defaults to null).
$myModel->scheduleCustomerGroup($customerGroup);
// The schedule method will accept an array or collection of customer groups.
$myModel->scheduleCustomerGroup(CustomerGroup::get());
Unscheduling customer groups
To disable a model for a customer group, it can be unscheduled. This sets enabled to false and clears the starts_at and ends_at dates.
$myModel->unscheduleCustomerGroup($customerGroup);
// Additional pivot data can also be passed.
$myModel->unscheduleCustomerGroup($customerGroup, pivotData: ['visible' => false]);
Parameters
| Field | Description | Type |
|---|
$models | A CustomerGroup model, array, or collection of CustomerGroup models. | mixed |
$starts | The date the customer group will be active from. | DateTime|null |
$ends | The date the customer group will be active until. | DateTime|null |
$pivotData | Any additional pivot data on the link table. | array |
Pivot Data
By default the following values are used for $pivotData:
enabled - Whether the customer group is enabled. Defaults to true when scheduling and false when unscheduling.
starts_at - When scheduling, set to the $starts value. When unscheduling, set to null.
ends_at - When scheduling, set to the $ends value. When unscheduling, set to null.
Any of these values can be overridden, as they are merged internally.
Querying by customer group
The HasCustomerGroups trait adds a customerGroup scope to the model. This allows querying based on availability for one or more customer groups.
The scope accepts a CustomerGroup model instance, an array, or a collection.
$results = MyModel::customerGroup($customerGroup)->paginate();
$results = MyModel::customerGroup([
$groupA,
$groupB,
])->paginate(50);
The $startsAt and $endsAt parameters can optionally be passed to filter by scheduling window. Both should be DateTime instances.
- If neither is provided, both default to
now() (with $endsAt set one second ahead).
- The scope returns models where
starts_at is null or before the given start, ends_at is null or after the given end, and either enabled or visible is true.
// Filter by a specific date window.
$results = MyModel::customerGroup($customerGroup, now()->addDay(), now()->addDays(7))->get();
A model will only be returned if the enabled or visible column is true on the pivot, regardless of whether the start and end dates match.
Limit by customer group
Eloquent models that use the HasCustomerGroups trait have a useful scope available:
// Limit products available to a single customer group
Product::customerGroup($customerGroup)->get();
// Limit products available to multiple customer groups
Product::customerGroup([$groupA, $groupB])->get();
// Limit to products that are available the next day
Product::customerGroup($groupA, now()->addDay())->get();
// Limit to products that are available within a date range.
Product::customerGroup($groupA, now()->addDay(), now()->addDays(2))->get();