A client has a relatively simple algorithm for their product SKUs.
- 1st initial of Product Type Watch
- 1st initial of Category Wrist
- 1st initial of Brand Rolex
- a dash
-
- a random 3-6 digit number
So a valid SKU is WWR-187530
They want one less thing to worry about when adding products. Can Craft Commerce auto the SKUs for them?
Craft Commerce Settings: Automatic SKU Format
Craft Commerce 3 has an option in the Product Type settings that might work.
Go to Commerce > System Settings > Product Type > Your Product Type and look for Automatic SKU Format.
đź’ˇ If Automatic SKU Format is empty then SKU becomes a required field when creating a product.
Let’s assume you have a category: Underwear
With this code snippet { product.category[0].title|slice(0, 1)}-{now|date('mdy') }
, you can get the 1st initial of the main category + the current date.
But with this trivial approach you could run into errors 🚨 For example, not being able to create a new product because the SKU is not unique.
To ensure a unique SKU, we’d like to use Product ID as the random digit. But it’s not possible via this settings field because the ID is only available after the product has been created. So let’s try a more robust approach — a custom Craft plugin.
Using a custom Craft plugin to generate SKU
🔌 We’re not going to delve into how to create a Craft plugin. For detailed info, read Andrew Welch’s guide or watch the CraftQuest series.
With a plugin, you can use product->id
to ensure your SKU is unique. And also create custom functions to get the initials from a category or title.
There are 2 scenarios:
- New product being created or
- Existing product being updated
First, we confirm the element being saved is a Product type. Then we check the parameter isNew
inside the events.
If the element is NOT new, we can update the product using the Elements::EVENT_BEFORE_SAVE_ELEMENT
event since the product->id
already exists.
Event::on(Elements::class, Elements::EVENT_BEFORE_SAVE_ELEMENT, function(Event $event) {
if ($event->element instanceof \craft\commerce\elements\Product) {
if(!$event->isNew){
$product = $event->element;
$sku = $this->generateSKU($product);
}
}
});
If the element IS new, we’ll need to use Elements::EVENT_AFTER_SAVE_ELEMENT
Event::on(Elements::class, Elements::EVENT_AFTER_SAVE_ELEMENT, function(Event $event) {
if ($event->element instanceof \craft\commerce\elements\Product) {
if($event->isNew){
$product = $event->element;
$sku = $this->generateSKU($product);
Craft::$app->getElements()->saveElement($product, false, false);
}
}
});
There are a few reasons to use the isNew validation:
- ✨ Handling new products
If a product is new, we don’t know theproduct->id
until after the product has been saved, so we couldn’t useEVENT_BEFORE_SAVE_ELEMENT
. - ♻️ Preventing a loop
EVENT_AFTER_SAVE_ELEMENT
needs to executesaveElement()
, without theif($event->isNew)
validation, saving would stay in a loop, re-saving after every save. - 📦 Handling existing products
They will not fulfill the isNew rule and theproduct->id
is known, therefore they can be handled using theEVENT_BEFORE_SAVE_ELEMENT
.
Keep in mind that SKU assignment is done at the record/​variation level. So first you need to get the record of the product that’s just been created.
Even if you’re not using multiple variants, Craft Commerce’s product structure still uses variants under the hood.1
$product = $event->element;
$records = $product->getVariants();
So if we have the following product data:
Product Type: Shoes
Category: Sneakers -> Men
Brand: New Balance
The SKU should be:SSNB-132
Where 132 is the Product ID. And where NB are the brand initials.
Here’s the code for generateSKU($product) function to autogenerate the SKU:
function generateSKU($product){
$records = $product->getVariants();
$brandInitials = null;
$category = $product->category->level('>= 2')->one();
$brand = $product->brand->one();
$productType = substr($product->type->name,0,1);
$categoryInitial = (isset($category->title)) ? substr($category->title,0,1) : '';
$brand = (isset($brand->title)) ? explode(" ", $brand->title) : '';
foreach ($brand as $b) { $brandInitials .= $b[0]; }
$sku = $productType.$categoryInitial.$brandInitials.'-'.$product->id;
foreach($records as $key => $record){
$record->sku = $sku;
$records[$key] = $record;
}
$product->setVariants($records);
return $sku;
}
Summary
Craft Commerce Settings: Automatic SKU Format
Pros | Cons |
---|---|
No PHP programming skills needed | Only manually entered data can be used |
Attempting to get the initials of multiple fields gets messy quickly | |
If the category is new or the selected variable is empty, the SKU value will also be empty |
Custom Craft plugin
Pros | Cons |
---|---|
You can write functions to get parts of the title, category or field | PHP programming skills required |
Possible to add validations if the category is new or a field is empty | |
You can get as creative as you want with the SKU value |
đź‘Ť đź‘Ž What do you think of either approach? Let me know @claus0618
Bonus Tip - Set default values
With a custom plugin you can also set default values for product fields.
For example, for the product type Watches we needed to specify if it had the original box & papers. Since most of the watches were pre-owned, most didn’t have the original packaging.
We assigned the default values in the custom plugin by using Elements::EVENT_BEFORE_SAVE_ELEMENT
:
$event->element->setFieldValues([
'boxPapers' => ($product->boxPapers != "") ? $product->boxPapers : "No. Only the watch.",
'condition' => ($product->condition != "") ? $product->condition : "Pre-owned."
]);